持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
Spring有两种动态代理方式:
JDK代理:我们可以通Proxy类获取一个目标类的代理对象,但JDK代理要求被代理的类必须实现接口,所以是基于接口的代理。
cglib代理:如果目标类没有接口,使用cglib代理,是由asm封装的,直接操作类得字节码,效率也很高。
Spring使用了PointCut接口,对符合条件的bean进行增强处理。通过该接口的getMethodMatcher方法获取一个方法匹配器,然后通过matches方法匹配到目标类对象的目标方法执行增强操作。mathcer匹配规则就是通过Spring 配置的expression表达式了。
AOP的实现需要两个关键步骤:
- 匹配切点方法(构建切入点表达式类和切面类)
- 创建代理对象
这两方面在Spring的实现里非常复杂,尤其是第一步匹配切点方法过程,这个过程中,Spring会将@Aspect注解类的@Before,@After,@Around、@Pointcut等注解都封装成待执行的切面方法类,然后通过方法匹配器匹配到的要增强的方法前后执行切面方法类,达到方法增强的目的。
第二阶段,创建代理对象默认是通过JDK代理实现配置,<aop:aspectj-autoproxy proxy-target-class="true">这样配置可以指定使用cglib代理。
注册AnnotationAwareAspectJAutoProxyCreator
真正实现AOP功能就是AnnotationAwareAspectJAutoProxyCreator类,这个类实现了BeanPostProcessor和InstantiationAwareBeanPostProcessor接口,最终在实例化bean对象也就是执行BeanFactory.getBean(beanName)的过程中,会调用这两个接口的方法(执行顺序如下):
InstantiationAwareBeanPostProcessor先执行:
postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
postProcessBeforeInstantiation方法主要是找出注解了Advice的类,并将Advice的类使用了@Before,@After,@Around、@Pointcut,@AfterThrowing等注解的方法封装成一个一个类放入到缓存中供匹配到的类生成代理用。
BeanPostProcessor再执行:
Object postProcessAfterInitialization(Object bean, String beanName)
postProcessAfterInitialization主要是匹配符合条件的目标类对象,然后生成代理的过程,接下来就按顺序分析这两个方法完成的功能。
AOP的实现基本上是在这两个方法中进行的。
JDK和CGLIB的使用区分
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
在实现接口的情况下,如何强制使用CGLIB实现AOP
1、添加CGLIB库,SPRING_HOME/cglib/*.jar
2、在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK动态代理和CGLIB字节码生成的区别
1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类
2、CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法;因为是继承,所以该类或方法最好不要声明成final。