动态代理
静态代理
首先看看一般的代理模式即静态代理。我们直接来看代码。
public interface HelloService{
void sayHello();
}
/**
* 被代理类
*/
public class HelloServiceImpl implements HelloService{
public void sayHello(){
System.out.println("Hello World!");
}
}
/**
* 代理类
*/
public class HelloServiceProxy implements HelloService{
private HelloServiceImpl target;
public HelloServiceProxy(HelloServiceImpl target){
this.target = target;
}
public void sayHello(){
System.out.println("额外操作")
target.sayHello();
}
}
public class Client{
public static void main(String[] args){
HelloService service = new HelloServiceProxy(new HelloServiceImpl());
service.sayHello()
}
}
被代理类与代理类实现相同的接口,代理类的方法实现就是调用被代理类的同名方法,同时加入一些额外操作,比如通用的日志打印等。这就是代理。
但这种方式的弊端很明显,代理类与被代理耦合太严重。
- 方法名变了,代理类的方法名也要修改;
- 新增了方法,代理类也要新增方法;
- 被代理类实现了新接口,代理类也要实现新接口。
即,代理类需要我们手动地编写修改,这很麻烦。动态代理就在这种情况下应运而生。
JDK动态代理
上面的例子中代理类与别代理类实现了相同的接口,JDK动态代理的适用情景和上面一样,被代理类必须实现接口。
JDK动态代理通过增加一个中间层InvocationHandler来解耦。
public class MyJdkProxy implements InvocationHandler{
private Object target;
public Object getProxyInstance(Object target){
this.target = target;
return Proxy.newInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
System.out.println("额外操作");
return method.invoke(target, args);
}
}
通过Proxy.newInstance(…args)生成代理对象,代理对象在调用接口方法时,会转为调用invoke方法,在invoke方法中通过反射调用被代理对象的真实方法。
其中,Proxy.newInstance(…args)生成代理对象的关键步骤通过查看源码可以得到:
- 查找或生成指定的代理类。
Class<?> cl = getProxyClass0(loader, intfs);
- 通过反射获取入参为InvocationHandler的构造函数对象。
final Constructor<?> cons = cl.getConstructor(constructorParams);
- 通过构造函数对象生成代理对象。
return cons.newInstance(new Object[]{h});
很明显,最核心的就是第一步生成指定代理类。本来需要我们自己编写的代理类现在有JDK动态代理自动生成。被代理类发生了之前所说的变化后,不再像静态代理那样需要相应地改变代理类。现在,我们只需专注于代理逻辑的编写,即在invoke方法中编写相应逻辑。
为加深理解,可查看自动生成的代理类 <math> <semantics> <mrow> <mi> P </mi> <mi> r </mi> <mi> o </mi> <mi> x </mi> <mi> y </mi> <mn> 0. </mn> <mi> c </mi> <mi> l </mi> <mi> a </mi> <mi> s </mi> <mi> s </mi> <mi mathvariant="normal"> 。 </mi> <mi mathvariant="normal"> 在 </mi> <mi mathvariant="normal"> 代 </mi> <mi mathvariant="normal"> 码 </mi> <mi mathvariant="normal"> 中 </mi> <mi mathvariant="normal"> 加 </mi> <mi mathvariant="normal"> 上 </mi> <mi mathvariant="normal"> 下 </mi> <mi mathvariant="normal"> 面 </mi> <mi mathvariant="normal"> 的 </mi> <mi mathvariant="normal"> 代 </mi> <mi mathvariant="normal"> 码 </mi> <mi mathvariant="normal"> , </mi> <mi mathvariant="normal"> 在 </mi> <mi mathvariant="normal"> 项 </mi> <mi mathvariant="normal"> 目 </mi> <mi mathvariant="normal"> 根 </mi> <mi mathvariant="normal"> 目 </mi> <mi mathvariant="normal"> 录 </mi> <mi mathvariant="normal"> 下 </mi> <mi mathvariant="normal"> 会 </mi> <mi mathvariant="normal"> 生 </mi> <mi mathvariant="normal"> 成 </mi> <mi mathvariant="normal"> 文 </mi> <mi mathvariant="normal"> 件 </mi> <mi> c </mi> <mi> o </mi> <mi> m </mi> <mi mathvariant="normal"> . </mi> <mi> s </mi> <mi> u </mi> <mi> n </mi> <mi mathvariant="normal"> . </mi> <mi> p </mi> <mi> r </mi> <mi> o </mi> <mi> x </mi> <mi> y </mi> <mi mathvariant="normal"> . </mi> </mrow> <annotation encoding="application/x-tex"> Proxy0.class。在代码中加上下面的代码,在项目根目录下会生成文件com.sun.proxy. </annotation> </semantics> </math>Proxy0.class。在代码中加上下面的代码,在项目根目录下会生成文件com.sun.proxy.Proxy0.class。
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
截取其中的方法实现的代码,可以看到是通过调用InvocationHandler的invoke方法来实现的。
public final void sayHello(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
Cglib动态代理
Cglib动态代理可对未实现接口的类实现代理,代理类是被代理类的子类。
Cglib动态代理通过增加中间层MethodInterceptor实现解耦。
public class MyCglibProxy implements MethodInterceptor{
public Object getProxyInstance(Class cls){
Enhancer enhancer = new Enhancer();
enhancer.setSuperClass(cls);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object object, Mehtod method, Object[] args, MethodProxy methodProxy){
System.out.println("额外操作");
return methodProxy.invokeSuper(object, args);
}
}
通过enhancer生成代理对象,代理对象在调用方法时,转为调用intercept方法,在intercept方法中通过invokeSuper调用被代理对象的真实方法,该方法属于直接调用,而不是反射,因此Cglib动态代理比JDK动态代理效率要高。
代码链接:码云