我们通常说的动态代理主要有JDK的动态代理和CGLIB代理,Spring的AOP也是基于这两种代理实现的。
下面我们先从代码上来了解一下这两种代理。
被代理类
public class Teacher implements Person {
public void sleep(){
System.out.println("sleeping");
}
public void teach(){
System.out.println("teaching");
}
}
JDK动态代理
JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
我们先抽象出一个接口,让被代理类去实现。
public interface Person {
public void sleep();
public void teach();
}
然后代理类实现InvocationHandler接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxy2 implements InvocationHandler{
private Object object;
public Object getPersonProxy(Object object){
this.object = object;
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("进入方法:" + method.getName() + " " + start);
Object result = method.invoke(object, args);
long end = System.currentTimeMillis();
System.out.println("结束方法:" + method.getName() + " " + end);
return result;
}
}
cglib动态代理
cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理
需要依赖jar包,maven依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
代理类
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private Object object;
public Object getProxy(Object object){
this.object = object;
//创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
//为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
enhancer.setSuperclass(object.getClass());
//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("进入方法:" + method.getName() + " " + start);
Object result = method.invoke(object, args);
long end = System.currentTimeMillis();
System.out.println("结束方法:" + method.getName() + " " + end);
return result;
}
}
测试类
public class Test {
public static void main(String[] args) {
Person person = (Person)new JDKProxy().getPersonProxy(new Teacher());
person.sleep();
person.teach();
System.out.println("------------------------");
Person person2 = (Person)new JDKProxy2().getPersonProxy(new Teacher());
person2.sleep();
person2.teach();
System.out.println("------------------------");
Teacher teacher = (Teacher)new CglibProxy().getProxy(new Teacher());
teacher.sleep();
teacher.teach();
}
}
控制台输出
进入方法:sleep 1505810963080
sleeping
结束方法:sleep 1505810963081
进入方法:teach 1505810963081
teaching
结束方法:teach 1505810963081
------------------------
进入方法:sleep 1505810963081
sleeping
结束方法:sleep 1505810963081
进入方法:teach 1505810963081
teaching
结束方法:teach 1505810963081
------------------------
进入方法:sleep 1505810963162
sleeping
结束方法:sleep 1505810963162
进入方法:teach 1505810963162
teaching
结束方法:teach 1505810963162
比较
JDK动态代理
- 不需要依赖第三方库
- 通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法,所以必须实现接口
- 可以通过实现InvocationHandler,或者构造InvocationHandler匿名内部类实现
- 通过Proxy.newProxyInstance方法创建代理接口的代理类
CGLIB
- 必须依赖CGLIB类库
- 通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理,所以需要代理的方法不能被final修饰
- 代理类必须实现MethodInterceptor拦截器,重写其intercept方法
- 通过new Enhancer()创建加强器
- 如果类被final修饰,那么不能使用CGLIB代理,因为被final修饰的类不允许生成子类
Spring AOP的选择
- 当Bean实现接口时,Spring就会用JDK的动态代理
- 当Bean没有实现接口时,Spring使用CGlib是实现
- 可以强制使用CGlib(在spring配置中加入)
喜欢这篇文章的朋友,欢迎长按下图关注公众号lebronchen,第一时间收到更新内容。