目录
JDK动态代理(基于Proxy和InvocationHandler)
Cglib动态代理(基于Enhancer和MethodInterceptor)
JDK动态代理(基于Proxy和InvocationHandler)
Jdk动态代理可以代理接口和类,基于Proxy类提供的一些静态方法如Proxy.newInstance,同时Proxy也是所有其生成的代理类的父类。
jdk动态代理需要被代理者是个接口,或者是个接口的实现类,如果是一个纯类没实现任何接口,应考虑cglib动态代理,包括Spring官方文档AOP的代理机制中也如是说。
代理接口:被代理的接口,没有任何实现,jdk动态代理帮助其实现
代理接口实现类:被代理的接口,已有实现,但是jdk动态代理可以为实现的方法切面编程
代理接口代码
可以看到Engine没有实现类,jdk动态代理相当于帮它产生一个实现类,并实现了Engine接口中的方法,invoke方法相当于帮你实现接口中的方法。
public interface Engine {
void fire();
String woo();
}
public class TestEngineJdkProxy{
public static void main(String[] args) {
Engine engine = (Engine) Proxy.newProxyInstance(Engine.class.getClassLoader(), new Class[]{Engine.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("123");
if ("woo".equals(method.getName()))
return "666";
else
return "777";
}
});
System.out.println(engine.woo());
}
}
代理接口实现类的代码
可以看到EngineImpl为Engine接口实现类,jdk动态代理为EngineImpl中的方法增加了逻辑,类似切面编程。
注意,如果InvocationHandler中不慎使用了method.invoid(proxy,args) 而非 method.invoke(engine,args) ,则会造成递归直到报错。
public interface Engine {
void fire();
String woo();
}
public class EngineImpl implements Engine {
private String name;
public EngineImpl(){}
public EngineImpl(String name) {
this.name = name;
}
@Override
public void fire() {
System.out.println("this is fire");
}
@Override
public String woo() {
System.out.println("this is woo");
return "woo~";
}
public void name(){
System.out.println(name);
}
}
public class TestEngineImplJdkProxy {
public static void main(String[] args) {
Engine engine = new EngineImpl();
Engine proxy = (Engine) Proxy.newProxyInstance(engine.getClass().getClassLoader(), engine.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("fire".equals(method.getName()))
System.out.println("23333~");
else
System.out.println("woo233333~");
return method.invoke(engine,null);
}
});
proxy.fire();
System.out.println("==========");
System.out.println(proxy.woo());
}
}
究其原理
jdk动态代理实际帮助被代理的接口生成了一个实现类,帮助被代理的类生成一个代理类。通过反编译发现这个类叫$Proxy0.class:
class类定义为:
public final class $Proxy0 extends Proxy implements Interface {
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
// 这里可以发现,fire()已经被代理类重写了,他会先去调用InvocationHandler的invoke方法,再将结果返回
public final void fire() {
try {
this.invocationHandler.invoke(this, m3, null);
return;
}catch (Error|RuntimeException localError) {
throw localError;
}catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
}
Cglib动态代理(基于Enhancer和MethodInterceptor)
cglib底层是使用一个小而快的字节码处理框架ASM,转换字节码并生成新的类。核心基于Enhancer和MethodInterceptor。
代理接口的代码
cglib代理接口相当于帮你实现接口逻辑,返回接口方法的返回值
public class TestCGlibIntf {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Engine.class);
enhancer.setInterfaces(new Class[]{Engine.class});
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 此时被代理的是接口,所以o = engine$$EnhancerByCglib = null
System.out.println("intercept方法相当于帮你完成接口中方法的实现逻辑");
if ("fire".equals(method.getName()))
System.out.println("fire---------------");
else
return "7777";
return null;
}
});
Engine engine = (Engine) enhancer.create();
System.out.println(engine.woo());
System.out.println("------------------------");
engine.fire();
}
}
代理类的代码
cglib动态代理类相当于对类的方法进行且面编程,返回代理类本身
public class TestCglibImpl {
public static void main(String[] args) {
Engine engine = new EngineImpl();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(engine.getClass());
enhancer.setInterfaces(engine.getClass().getInterfaces());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before--------------");
Object invoke = method.invoke(engine, null);
System.out.println("after----------------");
return o;
}
});
Engine engineProxy = (Engine) enhancer.create();
engineProxy.fire();
}
}
究其原理
其实也是类似于jdk动态代理,通过字节码框架ASM生成子类,重写源类方法,在重写方法中先调用MethodInterceptor的intercept方法,返回代理对象
注意事项
动态代理无法重写final方法
假如我有个Urine类包含final和非final方法,并使用cglib进行动态代理,尝试调用代理对象的final方法,会发现它并没有被拦截重写
cglib被代理类的构造方法会执行两次
同样是上方cglib案例中的Urine类,在构造方法中打印日志,会发现将打印两次,而Spring4.0以后只会调用一次,是因为使用了Objesenis库,待探究。