代理框架图
代理模式通俗理解:目标对象A找工作需要租房,需要查询房子位置、价格和大小并交租,很麻烦,但如果通过房屋中介代理对象B,让B查询房子位置、价格和大小,A只需要交租即可
Spring AOP 解决的是 非业务代码抽取的问题,底层是动态代理技术,有JDK动态代理和CGLIB动态代理:
- JDK动态代理只提供接口的代理
- JDK运行时为目标类生成一个动态代理类$proxy*.class,动态代理类继承Proxy类并实现了目标类的接口,重写接口的所有方法以进行增强
- 调用时,动态代理类先调用方法拦截处理器InvocationHandler#invoke()进行增强,再通过反射方式Method#invoke()调用目标方法
- CGLIB动态代理
- 通过ASM在运行时动态生成目标类的子类,即动态代理类继承目标类,重写目标类的所有方法以进行增强
- 调用时,先通过动态代理类调用MethodInterceptor#intercept()进行增强,之后动态代理类调用目标类(父类)的方法
- 目标类和其方法不能被final修饰
- JDK方式生成动态代理类速度快于CGLIB,调用速度慢于CGLIB,老版本的CGLIB调用速度快于JDK,但由于JDK的版本升级,JDK7以上的动态代理性能已经高于CGLIB方式
public interface InvocationHandler {
// proxy: 代理对象;method:目标对象方法的反射,目标对象方法的参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
// loader:类加载器;interfaces:目标类的接口,h:方法拦截处理器
private static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
}
动态代理的代理对象 代理的是接口,且需要提供目标对象
JDK动态代理需要 有实现接口的类
CGLib动态代理
切入点表达式 指定拦截目标对象的方法,执行目标对象方法时,动态植入切面类方法
动态代理也有个约束:目标对象一定是要有接口的
cglib代理也叫子类代理,从内存中构建出一个子类来扩展目标对象的功能,在运行期扩展Java类与实现Java接口,拦截对象的方法
代理的类不能为final,final修饰的类不能继承
目标对象的方法不能为final/static
即使抽取成类还是会出现重复的代码,因为这些逻辑(开始、结束、提交事务)依附在我们业务类的方法逻辑中
AOP的理念:就是将分散在各个业务逻辑代码中相同的代码通过横向切割的方式抽取到一个独立的模块中
代理能干嘛?代理可以帮我们增强对象的行为!使用动态代理实质上就是调用时拦截对象方法,对方法进行改造、增强!
Spring对AOP的支持局限于方法拦截
- 如果是单例的我们最好使用CGLib代理,如果是多例的我们最好使用JDK代理
原因:
- JDK在创建代理对象时的性能要高于CGLib代理,而生成代理对象的运行性能却比CGLib的低。
- 如果是单例的代理,推荐使用CGLib