看个简单的例子
被代理类
需要对add方法增强
@Component
public class StudentService {
public String add(String s) {
System.out.println(s);
return "ok";
}
}
切面类
@Aspect
@Component
public class AopDemo {
@pointcut("execution(public * cn.com.dq.annotation.aop.StudentService.*(..))")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知前");
Object object = joinPoint.proceed();
System.out.println("环绕通知后");
return object;
}
}
开启aop的类
@Component
@EnableAspectJAutoproxy
public class EnableAspectJAutoproxyBean {
}
扫描注解的类
@ComponentScan("cn.com.dq.annotation")
public class Start {
}
测试方法
@Test
public void test2() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Start.class);
StudentService studentService = (StudentService) applicationContext.getBean("studentService");
studentService.add("你好");
}
结果输出
结果分析
从结果看,确实对我们的add方法进行了增强,通过上面的示例:引发如下思考:
1.没有@EnableAspectJAutoproxy可以吗
2.@EnableAspectJAutoproxy是怎么被spring扫描到的
3.@EnableAspectJAutoproxy做了哪些事情?
4.aop的入口在哪里?什么时候注册,并且实例化的?
5.代理类是如何生成的?被代理类去了哪里?我们如何拿到?
6.@Aspect,@pointcut,@Around,@Before,@After,@AfterReturning,@AfterThrowing注解,spring是如何认识他们的,他们的作用是什么?
7.如何对目标方法进行增强的?链式调用的原理?
没有@EnableAspectJAutoproxy可以吗?
我们将EnableAspectJAutoproxyBean上的注解先注释掉,看一下运行结果
我们可以看到没有对方法进行增强,因此
@EnableAspectJAutoproxy注解开启了aop功能
@EnableAspectJAutoproxy是怎么被spring扫描到的
@EnableAspectJAutoproxy 注解上面是个@Import注解,实际是扫描@Import注解
@Import是如何被spring容器识别到的
由于我们EnableAspectJAutoproxyBean这个类上面有@Component注解,EnableAspectJAutoproxyBean类才会被spring扫描到并实例化,然后spring才会识别@Import注解。在spring的bean注册与实例化章节,我们知道spring识别的最基础注解是@Component,@Service,@Configuration,@Service,@Configuration的本质就是@Component。
支撑@Import的组件是ConfigurationClasspostProcessor
ConfigurationClasspostProcessor是什么时候注册的,什么时候实例化的
ConfigurationClasspostProcessor的父类是BeanDeFinitionRegistryPostProcessor,在spring bean的注册与实例化流程中,refresh方法类,在bean完成注册之后,注册了几个重要的组件,其中有一个就是ConfigurationClasspostProcessor,完成bean的注册以后,执行了一个invokebeanfactoryPostProcessors(beanfactory)方法,该方法对BeanDeFinitionRegistryPostProcessor 类型的类进行了实例化,并且调用了postProcessBeanDeFinitionRegistry方法
1.ConfigurationClasspostProcessor类的注册源码
扫描注册完bean以后,就进行了ConfigurationClasspostProcessor类的注册
2.ConfigurationClasspostProcessor类的实例化源码
它是拿到BeanDeFinitionRegistryPostProcessor类型的所有beanNames,然后通过beanfactory.getBean操作去进行类的实例化
3.postProcessBeanDeFinitionRegistry方法调用的源码
循环所有的BeanDeFinitionRegistryPostProcessor类型的类,再调用postProcessBeanDeFinitionRegistry方法
ConfigurationClasspostProcessor类的postProcessBeanDeFinitionRegistry方法干了哪些事情
在postProcessBeanDeFinitionRegistry方法里面追踪我们可以看到一个比较重要的类ConfigurationClassparser,他的parse方法
parse方法里面处理了@Component,@PropertySources,@ComponentScan,@Import, @ImportResource,@Bean
处理import注解的方法体内
1.处理了实现ImportSelector接口的类
2.处理了实现ImportBeanDeFinitionRegistrar接口的类
3.又回到了doProcessConfigurationClass方法,这个方法是继续处理引入的目标类
@Import导入的目标类,会被spring容器实例化并管理吗?答案是否,因为处理import注解只有上述三种类型,前2种不满足,第三种继续处理目标类,但是目标类上面没有任何的注解,所有它是不会被spring容器实例化并管理的,但是如果目标类的方法上面加上@Bean注解,该bean是会被spring容器实例化并管理的。
@EnableAspectJAutoproxy注解的@Import导入的类是AspectJAutoproxyRegistrar,这个类有什么特别?我们发现该类是实现了ImportBeanDeFinitionRegistrar接口,正好满足@Import处理流程2,该流程主要是通过反射,创建了ImportBeanDeFinitionRegistrar类型的实例,并放在容器中。
在执行完parse方法后执行这段代码this.reader.loadBeanDeFinitions(configClasses);这个方法中有这么一行代码
loadBeanDeFinitionsFromregistrars(configClass.getImportBeanDeFinitionRegistrars());
该方法中完成了对AspectJAutoproxyRegistrar接口的registerBeanDeFinitions方法的调用
spring容器扫描@EnableAspectJAutoproxy实际是对@Import的扫描,支撑@Import注解的类是ConfigurationClasspostProcessor
@EnableAspectJAutoproxy做了哪些事情?
从上面我们可以看到 @EnableAspectJAutoproxy 实际是导入了AspectJAutoproxyRegistrar类,并实例化了这个类,然后完成了registerBeanDeFinitions方法的调用
registerBeanDeFinitions方法干了哪些事情
1.注册aop入口类AopConfigUtils.registeraspectJAnnotationAutoproxyCreatorIfNecessary(registry);
2.aop入口类是AnnotationAwareAspectJAutoproxyCreator,它是spring容器中的beanName是org.springframework.aop.config.internalAutoproxyCreator
3.然后对注解的2个属性进行了赋值proxyTargetClass,exposeProxy
@EnableAspectJAutoproxy 实际上做的事情是完成aop入口类AnnotationAwareAspectJAutoproxyCreator的注册,同时将@EnableAspectJAutoproxy注解的2个属性赋值给该bean
aop的入口在哪里?什么时候注册,并且实例化的?
aop的入口类是AnnotationAwareAspectJAutoproxyCreator,它是通过@Import导入AspectJAutoproxyRegistrar类,完成registerBeanDeFinitions方法的调用进行注册的
AnnotationAwareAspectJAutoproxyCreator何时实例化的
AnnotationAwareAspectJAutoproxyCreator 的父类是SmartInstantiationAwareBeanPostProcessor
SmartInstantiationAwareBeanPostProcessor是在何处实例化的?
在spring bean的注册与实例化流程中,refresh方法类,在bean完成注册之后,执行了registerBeanPostProcessors(beanfactory);这个方法,该方法是通过beanfactory.getBean操作完成BeanPostProcessor类型的类的实例化
aop的入口类是AnnotationAwareAspectJAutoproxyCreator,它是通过@Import导入AspectJAutoproxyRegistrar类,完成registerBeanDeFinitions方法的调用进行注册的,在spring bean的注册与实例化流程中执行registerBeanPostProcessors方法完成AnnotationAwareAspectJAutoproxyCreator的实例化
代理类是如何生成的?被代理类去了哪里?我们如何拿到?
spring 在实例化且依赖注入完成以后,执行了initializeBean方法,initializeBean方法体内执行3个比较重要的流程
1.执行BeanPostProcessor的postProcessBeforeInitialization方法
2.执行invokeInitMethods方法
3.执行BeanPostProcessor的postProcessAfterInitialization方法
而AnnotationAwareAspectJAutoproxyCreator的父类是AbstractAutoproxyCreator,而在AbstractAutoproxyCreator的postProcessAfterInitialization方法中,完成了代理类的创建,最后将代理类返回,存在spring容器的一级缓存中
在创建代理的时候,我们可以看到被代理对象呗封装成SingletonTargetSource作为参数传进了创建代理方法内
从源码上我们可以看到最后作为一个属性设置在ProxyFactory的TargetSource属性的,而TargetSource这个属性是其父类AdvisedSupport的一个属性,而在我们的JDK代理JdkDynamicAopProxy中有个属性就是AdvisedSupport,我们拿到AdvisedSupport就能拿到被代理对象
在AopProxyUtils类中有个方法getSingletonTarget就能拿到被代理的类
AnnotationAwareAspectJAutoproxyCreator的父类是AbstractAutoproxyCreator,AbstractAutoproxyCreator实现了
SmartInstantiationAwareBeanPostProcessor,在postProcessAfterInitialization方法中完成了代理类的创建,并且将代理类存在一级缓存中,被代理类封装在代理类的advised属性中,advised是一个AdvisedSupport对象,AdvisedSupport中有个属性就是TargetSource
@Aspect,@pointcut,@Around,@Before,@After,@AfterReturning,@AfterThrowing注解,spring是如何认识他们的,他们的作用是什么?
我们知道在创建某个类的代理,必须满足该类存在切面,我们才会创建代理,所以创建代理之前我们要寻找切面,如下图
跟踪代码,首先是获取所有的bean,然后循环这些bean,判断他们是否存在 @Aspect注解,如下图
然后将其封装成AspectMetadata对象,如下图
接着是创建了一个MetadataAwareAspectInstanceFactory对象,然后创建advisor对象,如下图
拿到有@Aspect注解的类Class,然后遍历他的所有方法判断是否存在pointcut注解,拿到所有的没有pointcut注解的方法,注意这里是没有pointcut注解的方法,那就是@Around,@Before,@After,@AfterReturning @AfterThrowing,如下图
有个pointcut方法,接着就是创建pointcut对象,如下图
创建pointcut对象之前首先是将注解信息封装在AspectJAnnotation对象中,然后创建pointcut对象,将表达式设置进去,注意此时的pointcut对象是除了@pointcut之外注解的注解信息。
一个Advisor需要pointcut和Advice,有了pointcut,我们还需要Advice。如下图
我们可以看到根据不同的注解类型创建了不同的advice,拿到了advice,封装成advisor返回了,最后放在了一个advisors容器中,如下图:
当我们拿到所有的advisor以后,然后就要判断我的这个切面是否是作用于当前bean上面,此时是个匹配过程
总结:@Aspect注解定义了一个切面Advisor,@pointcut定义了一个切点pointcut,@Around,@Before,@After,@AfterReturning,@AfterThrowing,定义了各种通知Advice
如何对目标方法进行增强的?链式调用的原理?
在java中当我们为一个类创建了动态代理,当我们执行这个类的方法的时候,会调到动态代理的invoke方法
下面我们以jdk动态代理,来看aop的调用过程
如上图,第一步获取调用链
如下图,获取所有的advisor,先进行类匹配,再进行方法匹配
如下图,拿到advisor中的advice对象,如果该advice实现了MethodInterceptor接口,直接加入调用链,如果没有实现MethodInterceptor接口,他会把对应的advice包装成MethodInterceptor,然后加入过滤器链
实现MethodInterceptor接口有AspectJAfteradvice,AspectJAroundAdvice,AspectJAfterThrowingAdvice
没有实现MethodIntercepto接口的有AfterReturningAdvice,最后将其包装成AfterReturningAdviceInterceptor,然后持有AfterReturningAdvice的引用,MethodBeforeAdvice,最后将其包装成MethodBeforeAdviceInterceptor,然后持有MethodBeforeAdvice的引用
拿到调用链后,并开始调用
调用完调用链后,执行被代理方法
下图是调用过程
问题一:
链条中的顺序是怎么样的?
在循环切面类的所有方法的时候
获取所有没有pointcut注解的方法,对方法进行了排序,按照如下规则排序
从上面我们可以看到调用连中顺序为
Around, Before, After, AfterReturning, AfterThrowing
问题二:
是如何形成调用链的
调用advice中的invoke方法,根据链条中的顺序,对链条中的advice按顺序执行。执行完链条后,执行被代理方法,这样就完成了目标方法的增强
执行顺序为
1.AspectJAroundAdvice类执行aroud方法,打印环绕通知前,进行火炬传递,传回procee()
2.MethodBeforeAdviceInterceptor类执行before方法,打印before,进行火炬传递,传回procee()
3.AspectJAfteradvice类,直接进行了火炬传递,传回procee()
4.AfterReturningAdviceInterceptor类,直接进行了火炬传递,传回procee()
5.AspectJAfterThrowingAdvice类,直接进行了火炬传递,传回procee()
6.无链条,执行被代理方法
7.aroud执行完,打印环绕通知后
8.AspectJAfteradvice类执行完,执行finally,执行after方法,打印after
9.AfterReturningAdviceInterceptor执行完,执行afterReturning方法,打印afterReturning
总结:调用链的核心流程,是拿到所有的MethodInterceptor,不是MethodInterceptor,要包装成MethodInterceptor,通过调用invoke方法完成通知方法的调用,调用完成以后通过invoke的参数MethodInvocation调用proceed方法返回主方法,完成下一个advice类的调用。