代理的分类
- 静态代理:每个代理类只能为一个接口服务
- 动态代理:可以通过一个代理类完成全部的代理功能(由JVM生成实现一系列接口的代理类,即:生成实现接口的类的代理)
静态代理
静态代理看这里。
动态代理
在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler
接口和java.lang.reflect.Proxy
类的支持
java.lang.reflect.InvocationHandler
接口的定义如下:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
- Object proxy:被代理的对象
- Method method:要调用的方法
- Object[] args:方法调用时传递的参数
注意:并不是对象的所有方法均通过代理来完成,对如继承自Object
的方法,只将hashCode()
、equals()
、toString()
转交给InvocationHandler
来处理。
可以通过Proxy
中的getProxyClass()
或newProxyInstance()
方法获取代理
其中newProxyInstance()
方法定义如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
- ClassLoader loader:类加载器
- Class<?>[] interfaces:要实现的接口
- InvocationHandler h:InvocationHandler子类的实例对象
实现动态代理
定义接口
public interface Sourceable {
void function();
}
定义实现类
public class Source implements Sourceable {
@Override
public void function() {
System.out.println("function");
}
}
定义工具类
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SourceableProxyUtil {
private Sourceable source;
public SourceableProxyUtil(Sourceable source) {
super();
this.source = source;
}
public Sourceable getProxy() throws Exception {
Class<Sourceable> clazzSource = (Class<Sourceable>) Proxy
.getProxyClass(source.getClass().getClassLoader(), source
.getClass().getInterfaces());
Constructor<Sourceable> constructor = clazzSource
.getConstructor(InvocationHandler.class); // 没有无参构造函数
return constructor.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before");
Object retVal = method.invoke(source, args);
System.out.println("after");
return retVal;
}
});
}
}
但是,使用getProxyClass()
方式过于复杂,改用newProxyInstance()
:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SourceableProxyUtil {
private Sourceable source;
public SourceableProxyUtil(Sourceable source) {
super();
this.source = source;
}
public Sourceable getProxy() throws Exception {
return (Sourceable)Proxy.newProxyInstance(source.getClass().getClassLoader(),
source.getClass().getInterfaces(), new InvocationHandler() {
... ...
});
}
}
测试
public class Main {
public static void main(String[] args) throws Exception {
Sourceable source = new Source();
Sourceable proxy = new SourceableProxyUtil(source).getProxy();
proxy.function();
}
}
测试结果:
before
function
after
可以看出,代理类能够正常使用。
改进工具类
上述方式只能用于生成Sourceable
接口的代理类,可以进一步将其抽象得到通用的代理类生成工具:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public final class ProxyUtil {
public static Object getProxy(final Object target) throws Exception {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println("before");
Object retVal = method.invoke(target, args);
System.out.println("after");
return retVal;
}
});
}
}
这样,无论任何类型的对象都可以通过此种方法获得相应的代理对象。此时,测试代码如下:
public class Main {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Sourceable proxy = (Sourceable)ProxyUtil.getProxy(new Source());
proxy.function();
}
}
抽取切面
将上述代码中的目标方法执行前后的操作进一步地抽象,将其抽取为切面。将切面代码进行封装得到通告。
定义通告接口
public interface Advice {
void before();
void after();
}
改写工具类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public final class ProxyUtil {
public static Object getProxy(final Object target, final Advice advice) throws Exception {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
advice.before();
Object retVal = method.invoke(target, args);
advice.after();
return retVal;
}
});
}
}
此时的测试代码为:
public class Main {
public static void main(String[] args) throws Exception {
Sourceable proxy = (Sourceable)ProxyUtil.getProxy(new Source(), new Advice() {
@Override
public void before() {
// TODO Auto-generated method stub
System.out.println("before");
}
@Override
public void after() {
// TODO Auto-generated method stub
System.out.println("after");
}
});
proxy.function();
}
}
此种方式更具有普遍适用性,无论对于任意类型都可以产生相对应的代理对象,同时可以自定义所要进行的操作(通过通告)。
在整个过程中需要手动写的只有通告Advice
AOP编程
AOP(Aspect-Oriented Programming)可以通过代理方式完成诸如:安全控制、事务管理、性能统计、日志记录、错误处理...等功能。
在实际过程中,更多是通过getter/setter方式传递target与advice的:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactoryBean {
private Object target;
private Advice advice;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
advice.before();
Object retVal = method.invoke(target, args);
advice.after();
return retVal;
}
});
};
}
通过AOP的方式可以在运行时动态地将代码切入到类的指定方法、指定位置上。
Spring框架的核心即是IoC和AOP。
若你在博客园上发现了几乎一样的文章,那么恭喜你:找到我的博客园地址了。详细代码在这里。