基于反射的动态代理实现
保留所有版权,请引用而不是转载本文(原文地址 https://yeecode.top/blog/05/ )。
1 动态代理
动态代理的实现方式常用的有两种:
- 反射实现:基于反射实现,目标对象必须要有接口。生成的代理类是接口的一个实现类
- Cglib实现:基于字节码实现,效率稍低,但目标对象不需要有接口。生成的代理类是目标类的子类,因此目标类不能是final的。
2 背景介绍
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象,只能针对接口做代理。
接口和目标类没有任何变化,只不过为了增加难度,我们增加了返回值。
接口如下,除了增加了方法的返回值外无任何变化:
public interface UserInterface {
String sayHello(String name);
}
目标类如下,无任何变化:
public class User implements UserInterface {
@Override
public String sayHello(String name) {
System.out.println("hello " + name);
return "OK";
}
}
2 反射调用方法
接下来,我们使用JDK完成动态代理,对于代理类只需要实现InvocationHandler接口即可,并且要实现其中的invoke()方法。
invoke()中需要传入目标对象、目标对象的方法、调用目标所需的参数,传入后,就会直接调用指定对象的指定方法了。
public class ProxyHandler<T> implements InvocationHandler {
/**
* @param proxy 被代理的对象
* @param method 要调用的方法
* @param args 方法调用时所需要参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("pre words");
Object ans = method.invoke(proxy, args);
System.out.println("post words");
return ans;
}
我们使用举例:
public class Main {
public static void main(String[] args) {
// 创建代理类
ProxyHandler proxyHandler = new ProxyHandler();
// 调用方法时所需要的参数
Object[] params = {"Jack"};
try {
proxyHandler.invoke(
new User(),
User.class.getMethod("sayHello",String.class),
params);
} catch (Throwable e) {
System.out.println(e.toString());
}
}
}
我们只要创建一个ProxyHandler对象,直接调用invoke方法,传入其中所需要的对象、方法、参数,便可以执行我们写好的invoke方法,并且invoke中还可以调用指定对象的指定方法。这种方法非常灵活,ProxyHandler建立后,调用invoke方法时,可以传入任何对象、任何方法,非常自由。
这种操作只是通过反射调用的指定对象的指定方法,并没有使用代理。但是可以用它来实现动态代理。我们只要生成一个代理对象,接口所有的操作都由代理对象来完成。其中,代理对象中再通过这种反射的方式去调用目标对象即可了。
3 动态代理
在使用动态代理时,ProxyHandler需要稍加改造,即在构造方法中传入目标对象。但是这里要注意的是,与静态代理不同,这里的目标对象的类型是任意的,因此同一个ProxyHandler可以用来代理不同的目标对象。
public class ProxyHandler<T> implements InvocationHandler {
private T target;
public ProxyHandler(T target) {
this.target = target;
}
/**
* @param proxy 被代理的对象
* @param method 要调用的方法
* @param args 方法调用时所需要参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("pre words");
Object ans = method.invoke(target, args);
System.out.println("post words");
return ans;
}
}
在使用时,如下:
public class Main {
public static void main(String[] args) {
// 创建目标类
User user = new User();
// 创建代理类,通过传参指明了它的目标类
ProxyHandler proxyHandler = new ProxyHandler(user);
// 生成一个代理对象实例。使用Proxy类的一个静态方法
UserInterface userProxy =
(UserInterface) Proxy.newProxyInstance(
User.class.getClassLoader(),
new Class[] { UserInterface.class },
proxyHandler);
// 通过接口调用相应的方法,实际时Proxy执行
userProxy.sayHello("Lily");
}
}
首先,我们定义了被代理的类的实例,并且传给了proxyHandler。然后,我们我们直接调用了Proxy的静态方法newProxyInstance生成了一个接口的实例,这个实例就是代理(而且它知道它代理的是user对象,初始化时已经传给它了)。
然后,直接对这个实例调用方法就可以了,对这个接口调用方法,就相当于执行了invoke中的操作,输出如下:
pre words
hello Lily
post words
注意,在这种实现中,目标类target是在初始化时写死,并且在invoke时直接调用的,如果在invoke中直接调用传来的proxy对象,则会引发循环调用,造成死循环。
我们查看下Proxy类中的静态方法,newProxyInstance方法:
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*
* <p>{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of
* a proxy class that is defined by the specified class loader
* and that implements the specified interfaces
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
该方法用来返回指定接口的一个代理类实例。
输入参数有:
- 生成代理类实例的classLoader;
- 要满足的接口;
- 代理类的处理器,即具体的实现。
这样就指明了用哪个处理器处理哪个接口中的操作,以及该对象如何生成出来。
生成的对象就是一个代理对象,就可以代理接口的操作了。
可以访问个人知乎阅读更多文章:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。