前言
在 Spring 中 AOP 是我们使用的非常频繁的一个特性. 通过 AOP 我们可以补足一些面向对象编程中不足或难以实现的部分.
AOP
前置理论
首先在学习源码之前我们需要了解关于 AOP 的相关概念如切点切面等, 以及如何使用 AOP, 这里可以看我之前的文章: Spring 系列之 AOP 的原理及手动实现
创建 AOP 相关对象
对于 Java 这种面向对象语言来说任何功能的实现都是依赖于对象, AOP 也不例外.
首先我们先来准备好在配置文件中配置好 AOP 相关的属性.
spring.xml
- <bean id="aspect" class="cn.javass.spring.chapter6.aop.HelloWorldAspect"/>
- <bean id="beforeAdvice" class="cn.javass.spring.chapter6.aop.BeforeAdviceImpl"/>
- <aop:aspectj-autoproxy/>
- <aop:config>
- <aop:pointcut id="pointcutA" expression="execution(* cn.javass..*.sayAfterReturning(..))"></aop:pointcut>
- <aop:advisor id="advisor" pointcut="execution(* cn.javass..*.sayAdvisorBefore(..))"
- advice-ref="beforeAdvice"/>
- <aop:aspect id="aspects" ref="aspect">
- <aop:before pointcut="execution(* cn.javass..*.sayBefore(..)) and args(param)" method="beforeAdvice(java.lang.String)" arg-names="param"/>
- <aop:after-returning pointcut-ref="pointcutA" method="afterReturningAdvice" arg-names="retVal" returning="retVal"/>
- <aop:after-throwing pointcut="execution(* cn.javass..*.sayAfterThrowing(..))" method="afterThrowingAdvice" arg-names="exception" throwing="exception"/>
- <aop:after pointcut="execution(* cn.javass..*.sayAfterFinally(..))" method="afterFinallyAdvice"/>
- <aop:around pointcut="execution(* cn.javass..*.sayAround(..))" method="aroundAdvice"/>
- </aop:aspect>
- </aop:config>
在上面的配置中创建了几种不同的 advice. 这些配置在 spring 启动时会被相应的创建为对象.
AopNamespaceHandler
在前面 IoC 文章中我们有提到在 Spring 中通过 spi 的机制来确定解析配置文件中不同标签的解析类.
在 aop 包中找到 spring.handlers 文件:
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
可以确定的是处理 aop 相关标签的就是 AopNamespaceHandler 这个类.
- public void init() {
- this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
- this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
- this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
- this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
- }
在 AopNamespaceHandler 中除了构造函数就只有上面的 init 方法, 上面代码就是注册解析不同标签的解析器的 BeanDefinition. 我们需要关注的是 aspectj-autoproxy 标签的解析器类 AspectJAutoProxyBeanDefinitionParser.
#### AopConfigUtils#registerOrEscalateApcAsRequired
跟踪 ```AspectJAutoProxyBeanDefinitionParser``` 的 parse 方法最终会进入到 ```AopConfigUtils#registerOrEscalateApcAsRequired``` 中.
- private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
- Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
- if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
- BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
- if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
- int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
- int requiredPriority = findPriorityForClass(cls);
- if (currentPriority <requiredPriority) {
- apcDefinition.setBeanClassName(cls.getName());
- }
- }
- return null;
- }
- RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
- beanDefinition.setSource(source);
- beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
- beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
- registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
- return beanDefinition;
- }
这里的常量 ```AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator";```
首先要明白的是 internalAutoProxyCreator 并不是 spring 中的一个实际的类, AUTO_PROXY_CREATOR_BEAN_NAME 是一个用于创建 aop 类的 Beandefinition 的名字.
在上面的代码逻辑中如果 AUTO_PROXY_CREATOR_BEAN_NAME 表示的 Beandefinition 已经存在则判断新需要注册的类其优先级和已经存在的类定义进行比较, 如果新需要注册的优先级较高则进行替换.
如果不存在已经注册的 Beandefinition 则将其进行注册. 被注册的 Beandefinition 表示的类为 ```AspectJAwareAdvisorAutoProxyCreator```.
#### 完成 aop 功能需要创建的对象
在前面 IoC 文章中分析过了在解析完配置文件后需要创建的对象都会将其 BeanDefinition 注册到 IoC 容器中, 所以我们可以将断点设置在配置文件解析完成之后就可以看到需要创建那些对象了.
![需要创建的对象](https://user-gold-cdn.xitu.io/2019/7/5/16bc0e4a57f8a819?w=789&h=405&f=png&s=22442)
如上图 ```helloWorldService``` 就是需要被增强的类.
- public interface IHelloWorldService {
- void sayHello();
- void sayBefore(String param);
- boolean sayAfterReturning();
- void sayAfterThrowing();
- boolean sayAfterFinally();
- void sayAround(String param);
- void sayAdvisorBefore(String param);
- }
而 ```aspect,beforeAdvice,pointcutA,advisor``` 都是我们在配置文件中配置过的, 是切点, 切面和处理方法的实现类.```org.springframework.aop.config.internalAutoProxyCreator``` 是上面分析过的用于创建 aop 代理的实现类.
而后面的以 ```org.springframework.aop.aspectj.AspectJPointcutAdvisor``` 开头的几个类实际上就是包含了切点和通知的一个切面的实现类, 也就是它来决定哪些类需要被增强.
![增强实现类](https://user-gold-cdn.xitu.io/2019/7/5/16bc0e4a582a62c7?w=785&h=223&f=png&s=15964)
- ### 功能增强
- #### 增强时机
如果看过前面手写 aop 文章的同学应该知道当时我们分析 aop 增强时机时有说过 aop 的增强功能实际上是依赖于动态代理实现的. 而动态代理如果要对一个对象进行增强那么首先需要持有该对象才行.
所以我们在对对象进行增强的前提是该对象已经被创建完成之后. 而且我们要清楚的是一个类对象被增强后我们所有需要使用该对象的地方都应该使用该对象, 这样就确定了类增强的时机一定是在类对象创建之后并且在完成注入之前.
#### AspectJAwareAdvisorAutoProxyCreator
前面有说过创建代理对象实际上是通过 ```AspectJAwareAdvisorAutoProxyCreator``` 来完成, 先来了解一下该类, 查看该类的继承体系.
![继承体系](https://user-gold-cdn.xitu.io/2019/7/5/16bc0e4a5a031b8b?w=639&h=215&f=png&s=21661)
可以看到实际上该类本身还是一个 BeanPostProcessor, 那么可以肯定的是我们只要找到执行 BeanPostProcessor 的地方并且是在实例化后执行的地方即可. 经过调试后定位到 ```AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization``` 方法.
- public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
- Object result = existingBean;
- for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
- result = beanProcessor.postProcessAfterInitialization(result, beanName);
- if (result == null) {
- return result;
- }
- }
- return result;
- }
这里是对后置处理器进行遍历, 对于 aop 我们需要关注的是 ```AspectJAwareAdvisorAutoProxyCreator``` 这一个处理器.
- ##### wrapIfNecessary
- protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
- //more code
- // Create proxy if we have advice.
- Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
- if (specificInterceptors != DO_NOT_PROXY) {
- this.advisedBeans.put(cacheKey, Boolean.TRUE);
- Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
- this.proxyTypes.put(cacheKey, proxy.getClass());
- return proxy;
- }
- this.advisedBeans.put(cacheKey, Boolean.FALSE);
- return bean;
- }
这里去掉了前面一些代码, getAdvicesAndAdvisorsForBean 方法是用来获取和当前对象匹配的切面. 这里获取相匹配的切面类是通过 ```AbstractAdvisorAutoProxyCreator``` 来实现.
- protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
- List<Advisor> candidateAdvisors = findCandidateAdvisors();
- List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
- extendAdvisors(eligibleAdvisors);
- if (!eligibleAdvisors.isEmpty()) {
- // 根据在配置文件中配置的 order 属性或者注解 @order()进行从小到大的排序
- //order 的值越小其优先级越高
- eligibleAdvisors = sortAdvisors(eligibleAdvisors);
- }
- return eligibleAdvisors;
- }
首先获取到所有的切面类, 然后通过 ```AopUtils.findAdvisorsThatCanApply``` 方法来确定哪些类能够匹配.
- ##### AopUtils.findAdvisorsThatCanApply
- public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
- if (candidateAdvisors.isEmpty()) {
- return candidateAdvisors;
- }
- List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
- for (Advisor candidate : candidateAdvisors) {
- if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
- eligibleAdvisors.add(candidate);
- }
- }
- boolean hasIntroductions = !eligibleAdvisors.isEmpty();
- for (Advisor candidate : candidateAdvisors) {
- if (candidate instanceof IntroductionAdvisor) {
- // already processed
- continue;
- }
- if (canApply(candidate, clazz, hasIntroductions)) {
- eligibleAdvisors.add(candidate);
- }
- }
- return eligibleAdvisors;
- }
- public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
- if (advisor instanceof IntroductionAdvisor) {
- return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
- }
- else if (advisor instanceof PointcutAdvisor) {
- PointcutAdvisor pca = (PointcutAdvisor) advisor;
- return canApply(pca.getPointcut(), targetClass, hasIntroductions);
- }
- else {
- // It doesn't have a pointcut so we assume it applies.
- return true;
- }
- }
实现逻辑很简单, 遍历所有的 advisor 调用 canApply 确定是否匹配.
切面是有切入点和通知组成, 切入点用来确定哪些对象需要被增强, 而通知决定如何进行增强. 所以很明显这里确定类对象是否匹配是由切入点 (pointCut) 决定的.
我们先来看一下切入点是什么.
- public interface Pointcut {
- // 类过滤器 用于确定类对象是否匹配 只有当类对象匹配后才进行方法的匹配
- ClassFilter getClassFilter();
- // 方法匹配器 用于确定具体哪一些方法需要被增强.
- MethodMatcher getMethodMatcher();
- // 生成一个 pointcut 对象实例
- Pointcut TRUE = TruePointcut.INSTANCE;
- }
上面我们可以看到实际上就是通过 ClassFilter 和 MethodMatcher 相互配合来实现的, 具体的实现过程会因为实现方式大同小异. 其中实现方式包括比如正则匹配, AspectJ 匹配等, 在我们之前的手写系列中就是通过正则来进行匹配的, 这里匹配的实现不深入探讨.
通过上面的逻辑便可以确定好增强该类会用到哪些 advisor.
#### createProxy
当确定好需要用到的 advisor 和其顺序后就开始进行创建代理对象了. 创建代理对象的方法由前面提到的 ```wrapIfNecessary``` 来调用 ```createProxy``` 方法实现.
- protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
- ProxyFactory proxyFactory = new ProxyFactory();
- proxyFactory.copyFrom(this);
- if (!proxyFactory.isProxyTargetClass()) {
- if (shouldProxyTargetClass(beanClass, beanName)) {
- proxyFactory.setProxyTargetClass(true);
- }
- else {
- evaluateProxyInterfaces(beanClass, proxyFactory);
- }
- }
- Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
- for (Advisor advisor : advisors) {
- proxyFactory.addAdvisor(advisor);
- }
- proxyFactory.setTargetSource(targetSource);
- customizeProxyFactory(proxyFactory);
- proxyFactory.setFrozen(this.freezeProxy);
- if (advisorsPreFiltered()) {
- proxyFactory.setPreFiltered(true);
- }
- return proxyFactory.getProxy(getProxyClassLoader());
- }
创建实际上市代理通过代理工厂类 (ProxyFactory) 实现的.
##### JDK 代理还是 cglib
在创建代理对象时需要确定使用 JDK 代理还是 cglib 代理, 前面有提到过如果在配置文件中配置了 ```proxy-target-class="true"``` 的话那么就只会使用 cglib 进行代理. 但是如果没有配置的话则需要通过实际情况来决定是 JDK 代理还是 cglib.
而除了 ```proxy-target-class``` 外, 我们实际上还可以配置一个属性 ```optimize```, 该属性默认值为 false, 如果我们将其置为 true 那么就表示允许 spring 对代理生成策略进行优化, 意思就是如果该类有接口, 就代理接口(使用 JDK 代理); 如果没有接口, 就代理类(使用 CGLIB 代理). 而不是像如果只配置 proxyTargetClass=true 时强制代理类, 而不去考虑代理接口的方式.
综上在 spring 中使用代理方式的策略如下:
- 如果没有配置 ```optimize``` 和 ```proxy-target-class``` 并且该类实现了接口, 那么使用 JDK 动态代理.
- 如果没有配置 ```optimize``` 和 ```proxy-target-class``` 并且该类没有实现接口, 那么使用 cglib 动态代理.
- 如果配置了 ```optimize``` 和 ```proxy-target-class``` 并且该类实现了接口, 那么使用 JDK 动态代理.
- 如果配置了 ```optimize``` 和 ```proxy-target-class``` 并且该类没有实现接口, 那么使用 cglib 动态代理.
实现代码如下:
- public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
- if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
- Class<?> targetClass = config.getTargetClass();
- if (targetClass == null) {
- throw new AopConfigException("TargetSource cannot determine target class:" +
- "Either an interface or a target is required for proxy creation.");
- }
- if (targetClass.isInterface()) {
- return new JdkDynamicAopProxy(config);
- }
- return new ObjenesisCglibAopProxy(config);
- }
- else {
- return new JdkDynamicAopProxy(config);
- }
- }
现在已经取到了创建代理对象的策略和目标对象, 就可以直接创建代理对象了. 如果对这放面有兴趣的可以自行搜索.
创建好代理对象之后使用代理对象替代之前创建好的对象, 那么在使用的时候就会调用增强后的方法完成功能.
#### 多个 advisor 如何确定顺序
在实际开发过程中, 可能会存在一个方法被多个 advisor 增强, 可能有的在方法执行前增强有的在方法执行后进行增强. 那么在 spring 中如何确定每一个增强方法的调用时机保证不会出问题的呢?
在手写 aop 系列中有讲过这个问题, 当时我们是通过 [责任链模式](https://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html) 来解决这个问题. 实际上 spring 中就是通过责任链模式来解决该问题的.
在 ```JdkDynamicAopProxy``` 的 ```invoke``` 方法中, 会通过 getInterceptorsAndDynamicInterceptionAdvice 方法来获取增强当前调用方法的所有 advisor 的 chain, 但是需要注意的是这个 chain 并不是根据实际应该的执行顺序排列的. 仅仅只是所有会被执行的增强方法的集合.
List
来源: https://www.cnblogs.com/liyus/p/11138294.html