1.SpringAOP的基本概念
Aspect切面
就是定义切入点、通知等载体.如果使用@AspectJ注解的方式它就是一个类,如果使用Schema-based(springxml)的方式,它就是一个标签.
例如以@AspectJ注解的方式
1 | java复制代码@Aspect |
1 | java复制代码@Aspect |
也可以把通知和切点都写到一个Aspect切面里.
以Schema-based xml的方式
1 | xml复制代码 <!--目标对象一个普通bean--> |
1 | java复制代码//advice通知并没有加注解@AspectJ |
JoinPoint连接点
叫目标点更好理解.就是需要进行拦截的目标方法,在Aop的理念中很多地方都可以作为连接点,进行添加横切逻辑.但springAop中连接点就单指某个方法。在上例中连接目标点就是UserServiceImpl类中的所有方法。
PointCut切点
定义查找匹配连接点的表达式,可以简单的认为PointCut就是JoinPoint的集合.
代表被表达式匹配上的所有连接点.
advice通知
横切逻辑,前置通知、后置通知等等
注意:
@AspectJ注解使用了AOP的概念.跟AspectJ是没有关系的,只是用了个名字。
2.代码实现基于@AspectJ注解方式
定义连接点JoinPoint,也就是需要代理的目标方法 UserService.java
1 | typescript复制代码//UserService接口 |
定义pointCut切点,就是匹配查找JoinPoint连接点的表达式 UserPointCut.java
1 | java复制代码// 定义PointCut |
定义Advice通知,也就是需要增强的横切逻辑
1 | less复制代码@Slf4j |
application xml配置文件 springAop-2.0aspectJ.xml
1 | xml复制代码<?xml version="1.0" encoding="UTF-8"?> |
测试代码
1 | ini复制代码ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:springAop-2.0aspectJ.xml"); |
运行结果:
3.SpringAop的底层原理
代理模式是springAop的实现原理,但是具体是怎么的呢?如果让我们自己是实现springAop的功能,会怎么做?
3.1动态代理的原理
①.调用Proxy.newProxyInstance生成代理对象.
②.继承InvocationHandler接口,实现Invoke方法,可以在invoke方法添加各种通知横切逻辑,同时调用目标对象的方法.
③.JDK自动生成的代理对象会继承目标对象的接口,并实现目标对象需要代理的方法.方法里再调用②中的invoke方法,而invoke方法中又调用了目标对象的方法,这样就实现了代理的功能.
3.2自己实现aop的思路
①.首先要一个处理aop的工具类
②.找到需要代理的目标类
③.工具类里实现Invoke方法,将寻找到的advice和目标方法调用添加进去.
④.生成代理类对象.因为依赖Jdk动态代理,实现invoke方法将目标对象传入进去即可.
问题:
1.IOC容器如何区分普通类和代理类?— 通过接口BeanPostProcessor区分的
2.因PointCut切点是匹配表达式,就有可能匹配到很多不同的service方法,会生成一个代理对象包含所有的方法代理?还是生成多个代理对象? —对于单个被代理的目标对象来说只会生成任意一个代理对象,但在实际执行invoke里会循环执行全部匹配上的advice执行链.
4.源码实现
4.1.查找到处理aop的工具类
在这里是AnnotationAwareAspectJAutoProxyCreator,那怎么找到该类呢?
1.找到开头标签aop对应的命名空间
2.全局搜索命名空间:www.springframework.org/schema/aop 注意http后需加一个 “\”,在spring.handlers找到对应的Handler
3.再根据Handler找到对应的Parser,这里是AspectJAutoProxyBeanDefinitionParser
4.AspectJAutoProxyBeanDefinitionParser代码如下,parse()方法里会注册AnnotationAwareAspectJAutoProxyCreator类.
1 | less复制代码//解析方法 |
进入registerAspectJAnnotationAutoProxyCreatorIfNecessary方法
1 | scss复制代码public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( |
最终注册bean的key就是这个,下面会进行debug验证.
4.2 Aop处理类Creator何时被加载
那么springIOC容器是知道加载这个类呢?在何时加载的呢?
此处涉及到IOC知识,不详细展开。spring在读取xml配置文件时,会解析xml中的配置,也包括xml文件中的声明部分.
先看下IOC的启动流程,AbstractApplicationContext的refresh()方法.
1 | scss复制代码@Override |
先看a处的代码,进入到obtainFreshBeanFactory()方法,发现里面又调用了refreshBeanFactory()方法.
refreshBeanFactory在AbstractRefreshableApplicationContext中代码如下
1 | scss复制代码@Override |
loadBeanDefinitions方法最终会调用执行上面的AspectJAutoProxyBeanDefinitionParser的parse方法.loadBeanDefinitions执行后debug如下
可以看到BeanDefinitionMap里除了我们自己定义的bean之外,还多了一个org.springframework.aop.config.internalAutoProxyCreator。
4.2.创建Aop处理类Creator的对象
先看看Creator类的继承关系图如下
可以看到该类继承实现了BeanPostProcessor接口.这个接口大家应该很熟悉,就是spring设计的扩展功能实现.
另外关于动态代理的执行时机问题,是需要目标对象被创建初始化后,才能进行动态处理操作.不然目标对象都没有,代理谁呢?不符合逻辑.
从IOC的启动流程来说,有了Bean定义同时又实现了BeanPostProcessor接口.故在registerBeanPostProcessors(beanFactory)时会将该Creator类注册到容器里,参看4.2 b处的代码.注意这里注册就会创建Creator类对象了,创建的时机在普通的bean之前,这点尤其重要.
registerBeanPostProcessors源码如下:
1 | scss复制代码public static void registerBeanPostProcessors( |
到这里AnnotationAwareAspectJAutoProxyCreator类的对象已经创建好了,提前创建此种类型对象spring是通过其继承实现了BeanPostProcessor识别的.
4.4 创建普通Bean对象,如何变成代理对象的
创建普通Bean对象也就是需要代理的目标对象,在refresh()方法的finishBeanFactoryInitialization(beanFactory)里面,参看4.2的c处标记。
通过该方法一直点下去,最终会调用到AbstractAutowireCapableBeanFactory.doCreateBean()真正创建对象的地方.如下
1 | scss复制代码//省略。。。。 |
进入initializeBean方法
1 | ini复制代码protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { |
applyBeanPostProcessorsAfterInitialization会循环调用IOC容器中所有的BeanPostProcessor,上面创建的Creator对象的postProcessAfterInitialization在这里会被执行,执行后目标对象就被包装成代理对象了。
至此IOC是如何加载代理对象和普通目标对象就清晰了。接下来就是SpringAop主要内容了。
4.5 创建代理对象
被调用的postProcessAfterInitialization方法在AnnotationAwareAspectJAutoProxyCreator的父接口AbstractAutoProxyCreator中,如下
1 | less复制代码@Override |
wrapIfNecessary方法的详细处理
1 | kotlin复制代码protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { |
这步处理结束后、就返回想要的代理对象了.
4.5.1 寻找匹配目标Bean的通知方法(advice和advisor)
getAdvicesAndAdvisorsForBean是抽象方法,具体的实现有2个子类分别是AbstractAdvisorAutoProxyCreator和BeanNameAutoProxyCreator。
而AbstractAdvisorAutoProxyCreator是上面注册的AnnotationAwareAspectJAutoProxyCreator的父类,故找AbstractAdvisorAutoProxyCreator中相应的方法.
1 | less复制代码@Nullable |
AbstractAdvisorAutoProxyCreator中getAdvicesAndAdvisorsForBean()
1 | less复制代码protected Object[] getAdvicesAndAdvisorsForBean( |
1 | scss复制代码protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { |
4.5.1.1 查找全部的advisor
findCandidateAdvisors()方法,上面的处理是在AbstractAdvisorAutoProxyCreator中,而findCandidateAdvisors方法在其类中有实现,且在子类中也有实现,此处应该去AnnotationAwareAspectJAutoProxyCreator中的实现,优先调用自己的方法
1 | kotlin复制代码protected List<Advisor> findCandidateAdvisors() { |
经过处理后的advisors里面封装了pointCut和advice信息
4.5.1.2 匹配目标beanName对应的advisor
findAdvisorsThatCanApply()方法
1 | scss复制代码public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { |
4.5.2 生成具体的代理对象
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy
1 | scss复制代码protected Object createProxy(Class<?> beanClass, @Nullable String beanName, |
可以看到proxyFactory对象封装了advisors和目标对象的信息.
proxyFactory.getProxy()
1 | less复制代码public Object getProxy(@Nullable ClassLoader classLoader) { |
1 | kotlin复制代码protected final synchronized AopProxy createAopProxy() { |
根据配置先创建AopProxy对象,再根据AopProxy创建具体的proxy对象.是用JDK动态代理还是Cglib方式.
DefaultAopProxyFactory类createAopProxy()
1 | arduino复制代码@Override |
我们只看JdkDynamicAopProxy方法在JdkDynamicAopProxy类中
1 | arduino复制代码public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException { |
创建具体的代理对象
1 | less复制代码@Override |
至此代理对象就创建完成了.
4.6 执行调用Aop对象
此处只看JdkDynamicAopProxy JDK动态代理的方式
JDK动态代理的原理,生成的代理对象的方法内部会委托给InvocationHandler.invoke()方法。而invoke方法就会包含各个增强方法(通知),再调用原对象的方法。
看一下JdkDynamicAopProxy的源码实现了InvocationHandler接口
1 | kotlin复制代码final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { |
重点invoke()
1 | kotlin复制代码public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
ReflectiveMethodInvocation的proceed()
1 | kotlin复制代码public Object proceed() throws Throwable { |
为什么@Before、@After的Advice的invoke可以实现前置、后置的通知效果?
MethodBeforeAdviceInterceptor的invoke()
1 | scss复制代码public Object invoke(MethodInvocation mi) throws Throwable { |
AfterReturningAdviceInterceptor的invoke()
1 | java复制代码public Object invoke(MethodInvocation mi) throws Throwable { |
结束。
5.总结
Aop首先需要理解基础的概念,有哪些具体是什么用处,怎么使用的。
其次理解动态代理的原理,这是理解aop的基础。
最后具体的aop实现步骤,先创建代理对象 -> 创建目标对象时 -> 判断是否有匹配上的代理对象 ->找到所有的advice和pointCut -> 再找到和目标对象匹配的advice和pointCut -> 生成实际的代理对象 -> 程序调用 -> 代理对象里会调用invoke方法 -> 执行具体的横切逻辑通知 -> 执行调用目标对象.
另仅个人学习记录,有理解不到位的情况…
本文转载自: 掘金