什么是动态代理
动态代理是相对于静态代理产生的概念。
可以简单理解为,静态代理是在代码中编写好代理类,动态代理的代理类是在运行时动态生成的。
使用代理一般是为了在原类中增加一些增强功能,如Spring的AOP就是通过动态代理实现的。
Spring中使用动态代理
有JDK代理和CGLIB代理两种实现。
JDK代理和CGLIB代理区别
JDK
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CJLIB
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
何时选用
- 当Bean实现接口时,Spring就会用JDK的动态代理。
- 当Bean没有实现接口时,Spring使用CGlib是实现。
- 可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)。
具体实现
JDK动态代理实现
定义一个接口:
interface Demo{
int add(int, int);
}
实现类:
class Real implements Demo {
@Override
public int add(int x, int y) {
return x + y;
}
这里Real就是我们需要代理的类。
动态代理代码:
class Handler implements InvocationHandler {
private final Real real;
public Handler(Real real) {
this.real = real;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
System.out.println("=== BEFORE ===");
Object re = method.invoke(real, args);
System.out.println("=== AFTER ===");
return re;
}
}
构造方法把要代理的对象传入Handler中。
这里invoke方法很重要,可以看到方法调用是通过我们传递一个Method类型参数,然后调用method.invoke来实现,即通过反射来实现。
生成代理类的样子:
public ProxyClass implements Demo {
private static Method mAdd;
private InvocationHandler handler;
static {
Class clazz = Class.forName("Demo");
mAdd = clazz.getMethod("add", int.class, int.class);
}
@Override
public int add(int x, int y) {
return (Integer)handler.invoke(this, mAdd, new Object[] {x, y});
}
}
这里add方法是调用了handler的invoke方法,传递三个参数,第一个是代理类本身,第二个是add方法的反射类,最后一个是参数列表。我们是通过InvocationHandler来完成拦截与代理。
JDK Proxy具体使用:
Handler handler = new Handler(new Real());
ifc p = (ifc)Proxy.newProxyInstance(ifc.class.getClassLoader(),
new Class[] {Demo},
handler);
p.add(1, 2);
CGLIB动态代理实现
没有接口,直接是实现类:
class Real {
public int add(int x, int y) {
return x + y;
}
}
使用了一个与JDK Proxy中Handler类似的类:
public class Interceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj,
Method method,
Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("=== BEFORE ===");
Object re = proxy.invokeSuper(obj, args);
System.out.println("=== AFTER ===");
return re;
}
}
代理类的大致样子:
public ProxyClass extends Real {
private static Method mAdd;
private static MethodProxy mAddProxy;
private MethodInterceptor interceptor;
static {
Class clazz = Class.forName("ifc");
mAdd = clazz.getMethod("add", int.class, int.class);
// Some logic to generate mAddProxy.
// ...
}
@Override
public int add(int x, int y) {
return (Integer)interceptor.invoke(
this, mAdd, new Object[] {x, y}, mAddProxy);
}
}
与jdk代理大致相同,只是多了一个MethodProxy。
去上面Interceptor可以看到,调用invoke方法是
Object re = proxy.invokeSuper(obj, args);
而不是
Object re = method.invoke(obj, args);
原因是代理类继承了原始类,obj指向的就是代理类对象的实例,如果第二种写法就会递归调用代理类的add方法。
因此cglib封装了一个MethodProxy类,其中invokeSuper方法可以调用原始基类的真正方法。
使用:
public static void main(String[] args) {
Enhancer eh = new Enhancer();
eh.setSuperclass(Real.class);
eh.setCallback(new Interceptor());
Real r = (Real)eh.create();
int result = r.add(1, 2);
}
总结
JDK代理和CGLIB代理实现本质上是很相似的。
都包含一下两点内容:
- 有一个接口或者基类,定义了一个代理类。
- 一个方法拦截器,完成方法的拦截和代理,是所有调用链的桥梁。