OOP是面向对象编程,Java就是一门面向对象语言,但有时候给对象加一些公共行为时,比如说日志打印等,在没有AOP之前是需要所有对象都进行引入,这样就导致产生了冗余代码,因此就补充了一个面向切面编程,AOP关注的方向是横向的,而OOP关注的方向是纵向的
使用AOP
使用AOP的步骤如下
-
创建一个切面类,加上@Aspect注解
-
添加切入点@Pointcut
-
添加建言,@Before、@After、@Around
-
最关键的一步,在配置文件上加上 aop:aspectj-autoproxy/标签
这样就完成了添加了AOP并且开启了AOP了
那么Spring究竟是如何实现AOP的,为什么在配置文件上加上了了aop标签就启动了
动态AOP标签
我们可以知道,AOP标签不属于Spring解析bean的四个标签里面(beans、bean、alias、。。)
首先来看看AOP标签是在哪里注册的,代码位于下面的类中
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
* and '{@code scoped-proxy}' tags.
*/
@Override
public void init() {
// In 2.0 XSD as well as in 2.5+ XSDs
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
//对于aspectk-autoproxy进行注册Bean解析器
//注册AspectJAutoProxyBeanDefinitionParser
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace in 2.5+
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
可以看到,在解析配置文件的时候,一旦遇到aspectj-autoproxy标签时,就会使用这个AspectJAutoProxyBeanDefinitionParser进行解析(这个是在解析自定义注解的时候添加的)
所以,对AOP的处理细节都在AspectJAutoProxyBeanDefinitionParser中进行的
AspectJAutoProxyBeanDefinitionParser解析器
AspectJAutoProxyBeanDefinitionParser解析器是专门用来对AOP的Bean进行解析的
对于解析器,入口都是从parse函数开始的,因为Spring对于解析器都是统一实现BeanDefinitionParser接口的
parse函数
源码如下
public BeanDefinition parse(Element element, ParserContext parserContext) {
//注册AnnotationAwareAspectJAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
//扩展BeanDefinition,对于AOP注解中子类的处理
extendBeanDefinition(element, parserContext);
return null;
}
可以看到这个函数只是一个总体调用而已
- 首先注册AnnotationAwareAspectJAutoProxyCreator
- 最后扩展AOP的BeanDefinition,因为可能存在子类(AOP切面其实也是一个BeanDefinition)
注册AnnotationAwareAspectJAutoProxyCreator
源码如下
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
//注册或升级AutoProxyCreator
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//对于proxy-target-class以及expose-proxy属性的处理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
//注册组件并开启通知
registerComponentIfNecessary(beanDefinition, parserContext);
}
可以看到注册的流程也分为三步
- 注册或升级AnnotationAutoProxyCreator
- 之后要对于proxy-target-class以及expose-proxy属性进行处理
- 注册组件并开启通知
下面就对这3个流程进行分析
注册或者升级AnnotationAutoProxyCreator
可以看到,注册或者升级是交由AopConfigUtils去做的,并且其调用了registerOrEscalateApcAsRequired方法
重载的registerOrEscalateApcAsRequired方法源码如下
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
//断言判断注册容器是否为空
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//如果AUTO_PROXY_CREATOR_BEAN_NAME已经注册过了
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//取出已经注册过的AUTO_PROXY_CREATOR_BEAN_NAME
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
//判断是不是同一个Class对象
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
//如果不是同一个对象,那就需要根据优先级来判断到底需要使用哪一个了
//获取当前AOP自动代理创建器的优先级
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
//获取指定的AOP自动代理创建器的优先级
int requiredPriority = findPriorityForClass(cls);
//进行优先级比较
if (currentPriority < requiredPriority) {
//如果指定的大于现在的,apcDefinition改变AOP自动代理创建器的class类型
//相当于进行升级动作
//由此可见,此时AOP自动代理创建器还没有实例出来,
//这里只是将Aop自动代理创建其的BeanDefinition的className进行修改
apcDefinition.setBeanClassName(cls.getName());
}
}
//如果是同一个Class对象,证明是同一个处理器
//返回,不需要进行注册
return null;
}
//如果还没进行注册
//使用Class去创建RootBeanDefinition,这里仅仅只是注入Class
//从上面代码中可以看到,是可以进行更改的
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
//封装一些信息
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//注册进容器中,并且以AUTO_PROXY_CREATOR_BEAN_NAME为key
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
可以看到,注册是指如果registry容器中发现AUTO_PROXY_CREATOR_BEAN_NAME的BeanDefinition还没进行创建,就会进行注册,如果发现已经注册了,那么就会进行优先级比较,哪个优先级高就使用哪个
这里要注意的点是,此时AOP自动代理创建器还没被实例化,仅仅只是交由一个RootBeanDefinition保存了对应的一些信息,并且注册进容器中,key对应为AUTP_PROXY_CREATOR_BEAN_NAME
处理proxy-target-class以及expose-proxy属性
这两个属性都是在aop标签里面设置的
先来说说这两个属性的作用
- proxy-target-class:是否使用CGLIB代理和@AspectJ自动代理支持,该属性为true时,代表需要支持
- expose-proxy:是否对切面进行曝光
对应的方法为useClassProxyingIfNecessary
对应的方法源码如下
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if (sourceElement != null) {
//获取Proxy_target_class属性
//并且转为Boolean
boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
//如果开启了
if (proxyTargetClass) {
//强制使用Cglib代理
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
//获取expose_proxy_attribute属性
//并且转为布尔类型
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
//判断是否开启
if (exposeProxy) {
//如果开启了强制对切面进行曝光
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
下面就来看看是怎么强制使用的
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//从注册容器中去取出AOP切面的BeanDefinition
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
//改变里面的属性
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//从注册容器中去取出AOP切面的BeanDefinition
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
//改变里面的属性
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
可以看到,强制使用仅仅只是改变注册容器里面的AOP切面的BeanDefinition里面的属性
现在已经完成了AOP自动创建器的注册了,本来AOP自动创建器在Spring中的存储形式也是一个BeanDefinition,所以也需要注册后才能使用
创建AOP代理
上面已经将AOP自动创建器注册了,那么这个类到底做了什么工作来完成AOP的操作呢?接下来我们就对这个AnnotationAwareAspectJAutoProxyCreator进行分析
AnnotationAwareAspectAutoProxyCreator的架构
首先我们对这个类的整体架构进行分析
其关系大概是这样的
AnnotationAwareAspectJAutoProxyCreator继承了AspectJAwareAdvisorAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator又继承了AbstractAdvisorAutoProxyCreator,而AbstractAdvisorAutoProxy又继承了AbstractAutoProxyCreator
这个类实现了BeanPostProcessor接口,前面我们看Bean进行加载的时候,会去调用其PostProcessAfterInitialization方法(之前跳过了没去分析,所以现在知道了那个方法是针对切面类的BeanDefinition来进行处理的)
而postProcessAfrerInitialization方法却是在AbstractAutoProxyCreator来实现的
源码如下
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
//判断传进来的Bean是否为空
if (bean != null) {
//获取缓存的键名,这里的形式是beanClassName_beanName
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//判断是否已经提前曝光代理过
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//如果没被代理过,执行wrapIfNecessart
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
//如果bean为Null,直接返回,如果不为null,但已经被曝光出来,直接返回bean
//解决了循环依赖的问题
return bean;
}
关键还是在于wrapIfNecessary方法上
wrapIfNecessary
该方法的作用就是进行给BeanDefinition进行代理增强的,并且会先判断有没有必要进行代理增强
源码如下
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//判断是不是已经增强过
//即targetSourcedBeans容器里面已经包含了这个BeanDefinition
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
//如果已经增强过,直接返回
return bean;
}
//判断是不是无需进行增强
//如果不位于advisedBeans里面,代表无需进行增强
//因为每个Bean加载的时候都会进入这个方法,切面的Bean肯定比事务的Bean少
//所以会将创建切面的Bean都存进advisedBeans容器中
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
//如果不需要增强,返回结果
return bean;
}
//判断该BeanDefinition是不是基础设施类,如果是基础设施类并且需要跳过
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
//在advusedBeans容器中去存储该BeanDefinition
this.advisedBeans.put(cacheKey, Boolean.FALSE);
//value改为false,这也是前面第二个if判断的清空,如果是基础设施
return bean;
}
// Create proxy if we have advice.
//来到这里就证明需要给这个类建立切面了
//如果存在增强方法则要创建代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//判断是否存在增强方法,这里DO_NOT_PROXY是一个Null
if (specificInterceptors != DO_NOT_PROXY) {
//如果存在增强方法
//将当前被代理的AOP BeanDefinition存储缓存中,代表正在需要进行增强
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建切面
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
//保存切面的Class类型
this.proxyTypes.put(cacheKey, proxy.getClass());
//返回切面
return proxy;
}
//如果不存在增强方法,不需要创建切面
//存进缓存中,并且代表这个AOP BeanDefinition不需要增强
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
可以看到,判断是否需要增强还利用了缓存技术,发现之前不需要增强的,会直接返回
- 判断是不是已经增强过,如果增强过,直接返回
- 从缓存中判断是不是已经尝试过增强,如果之前尝试过增强,但没有增强,那这里从缓存中取出就是一个false值,直接返回
- 判断是不是基础设施类、或者需要跳过增强环节,如果是,那就往缓存中记录当前的AOP BeanDefinition不需要增强,然后返回
- 此时就需要进行尝试增强了
- 找出要增强的方法,也就是需要进行切入的方法
- 判断增强的方法是否为空
- 如果为空,同样往缓存中记录当前的AOP BeanDefinition不需要增强,然后返回
- 如果不为空,往缓存中记录当前的AOP BeanDefinition需要增强,然后创建切面,并且将切面的类型存放进ProxyTypes中,最后返回切面
寻找需要增强的方法
对应的方法为getAdvicesAndAdvisorsForBean,并且是交由AbstractAdvisorAutoProxyCreator去做的
可以看到,是由AbstractAdvisorAutoProxyCreator完成的
源码如下
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
//获取增强方法
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
//判断是否为空
if (advisors.isEmpty()) {
//如果为空,返回一个空数组
return DO_NOT_PROXY;
}
//如果不为空,转化为数组然后返回
return advisors.toArray();
}
可以看到,寻找需要增强的方法又推给了findEligibleAdvisors方法去做的
源码如下
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//获取所有的增强
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//从所有的增强中获取适合Bean的增强
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
//对适合Bean的增强方法进行扩展
extendAdvisors(eligibleAdvisors);
//判断是否为空
if (!eligibleAdvisors.isEmpty()) {
//如果不为空,进行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
//返回结果
return eligibleAdvisors;
}
可以看到,寻找需要增强的方法不是简单地寻找还需要进行过滤的,大概会分为两个动作(后面还有一个排序的,这里就先不包含进来)
- 获取所有的增强
- 从所有的增强中,筛选出适合Bean的增强
获取所有的增强
下面先看一下是怎么获取所有的增强的
对应的方法为findCandidateAdavisors,可以看到这个方法是由AnnotationAwareAspectJAutoProxyCreator来完成的,这里可能就有人疑惑,从上面的代码来看,那个方法不应该还是在AbstractAdvisorAutoProxyCreator中完成的吗?
对,在AbstractAdvisorAutoProxyCreator中的确有这个方法,但这个方法却被AnnotationAwareAspectJAutoProxyCreator重写了,所以调用的是AnnotationAwareAspectJAutoProxyCreator的
源码如下
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
//可以看到,这里重写的方法也会去调用父类的,也就是说对父类进行拓展了
//因为父类仅仅是对配置文件中进行增强,所以需要子类对注解形式也进行增强
//在Spring2.0之前就仅仅只是支持配置文件
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder != null) {
//对注解形式进行增强
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
因为现在支持了注解之后就没什么人会使用配置文件的形式了,所以下面仅仅针对注解的形式来分析。
可以看到,从注解中进行获取是交由aspectJAdvisorBuilder去做的
其实大概也能猜到是怎么做的
- 获取所有的beanName,即在beanFactory中注册的bean都会被提取出来
- 遍历所有的beanName,并且找出加上了@AspectJ注解的类,进行标记
- 对标记为AspectJ注解的类进行增强器的提取
- 将提取结果加入缓存
下面来看看源码是怎么实现的
public List<Advisor> buildAspectJAdvisors() {
//使用了ArrayList来存储切面
//获取此时的切面
List<String> aspectNames = this.aspectBeanNames;
//如果为null,证明之前是第一次初始化,可以进行加载切面
if (aspectNames == null) {
//进行上锁处理
synchronized (this) {
//这里又进行了判断为空
//我怎么感觉是一个DCL的????
//这就是一个DCL,连aspectBeanNames都是volatile修饰的
//这里的aspectNames是一个单例
aspectNames = this.aspectBeanNames;
//DCL再次检查是否为空
if (aspectNames == null) {
//创建一个ArrayList,用于存储Advisor
List<Advisor> advisors = new ArrayList<>();
//给aspectNames进行赋值
aspectNames = new ArrayList<>();
//获取所有的beanName
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
//遍历所有的依赖
for (String beanName : beanNames) {
//判断该依赖是否合法,如果不合法就直接跳过了
//这个方法,对于本类来说会一直返回True
//但其实质会交由子类AnnotationAwareAspectJAutoProxyCreator去重写
//即具体调用的方法是延迟到子类AnnotationAwareAspectJAutoProxyCreator实现的
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
// 此时虽然校验通过了,但还是要再检查
// 下面就是检查Bean的类型是否为空了
Class<?> beanType = this.beanFactory.getType(beanName, false);
if (beanType == null) {
//如果bean的类型为空,也是忽略不进行处理
continue;
}
//判断这个类是不是加上了@Aspect注解
if (this.advisorFactory.isAspect(beanType)) {
//如果加上了,就将beanName添加进aspectNames集合中
aspectNames.add(beanName);
//并且创建一个AspectMetadata,称为切面元数据
//里面构造方法注入了beanName与beanType
AspectMetadata amd = new AspectMetadata(beanType, beanName);
//判断切面的实例化模式
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
//如果是SINGLETON模式的话,进行下面的操作
//创建切面元数据的实例化工厂
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//对标记了@Aspect的类进行解析,取出里面的增强方法
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
//下面的if-else则是实现一个缓存机制的而已
//判断当前的beanName是不是单例的
if (this.beanFactory.isSingleton(beanName)) {
//如果是单例的,那就直接往增强方法的缓存里面存放
//key为beanName,value为classAdvisors
//advisorsCache是一个ConcurrentHashMap
//也就是说,对于单例,缓存的就是解析出来的增强方法
//因为单例只会创建一次,所以对增强方法的解析肯定也只有一次!!!
this.advisorsCache.put(beanName, classAdvisors);
}
else {
//如果是原型的,或者是其他范围的
//则存在切面工厂的缓存里面
//key仍然为beanName,但value却变成了factory
//也就是说,对于原型模式,缓存的是切面元数据的工厂
//因为原型模式每次使用都会去创建,所以缓存的对应也应该是切面元数据工厂
//这样才可以对应上,创建bean时,也去利用这个工厂去创建切面
this.aspectFactoryCache.put(beanName, factory);
}
//所以说,当切面的实例化方法是切面模式,是支持所有的BeanDefinition的
//最后往返回的增强方法集合里面添加从类上面解析出来的增强方法
advisors.addAll(classAdvisors);
}
//如果实例化切面不是SINGLETON方式
//会执行下面的逻辑
else {
// Per target or per this.
// 判断当前beanName是不是单例模式
if (this.beanFactory.isSingleton(beanName)) {
//如果是会出现报错
//因为切面的实例化模式不是单例,不支持单例的BeanDefinition
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
//如果不是单例模式,就像原型的处理方式一样
//创建切面元数据工厂
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
//往切面工厂缓存中去存放
this.aspectFactoryCache.put(beanName, factory);
//最后添加进增强方法的集合里面
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
//此时aspectNames里面就包含了所有添加上@Aspect注解的BeanName
//
this.aspectBeanNames = aspectNames;
//返回解析出来的增强方法
return advisors;
}
}
}
//来到这里就说明,aspectNames不为null
//代表已经进行初始化过了,要考虑从缓存中取了
//判断aspectNames是否为空
if (aspectNames.isEmpty()) {
//如果为空的话,代表没有切面
//直接返回一个空集合
return Collections.emptyList();
}
//如果aspectNames不为空
//那么证明已经初始化过了,先考虑从缓存中取
List<Advisor> advisors = new ArrayList<>();
//遍历aspectNames里面所有的切面
for (String aspectName : aspectNames) {
//尝试从增强方法的缓存中取
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
//如果增强方法的缓存中有
//添加进返回集合里面
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
//如果增强方法的缓存中没有
else {
//就会尝试从切面元数据实例工厂缓存里面去取
//从缓存中取出切面元数据实例工厂
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
//使用工厂来创建该切面的所有增强方法,然后添加进集合中去
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
//最终返回增强方法集合
return advisors;
}
从这个方法可以看到,对于增强方法,也是做了一个缓存处理的,并且还针对切面实例化的模式不同,做了两个缓存,并且可以看到这两个缓存都是ConcurrentHashMap
下面总结一下大概的步骤吧
-
首先使用DCL+Synchroinced来获取单例的aspectNames,里面存储的是经过解析的切面的beanName,这是因为利用了缓存技术,所以对切面的解析、增强方法的获取只会执行一次,接下来都只会从缓存中获取
-
如果为空,那就代表是第一次进行解析
-
先获取所有的beanName,然后进行遍历
-
如果不合法,直接跳过(不合法的方法判断是延迟到子类AnnotationAwareAspectJAutoProxyCreator实现的)
-
获取beanName对应的BeanDefinition的Class对象,判断是否为空,如果为空,也会跳过
-
检测该beanName对应的Class是不是被@Aspect修饰,如果没有也会跳过,此处相当于做标识
-
使用beanName和beanType来创建出切面的元数据
-
判断切面元数据的实例化方式
-
如果是SINGLETON方式的,那么对于此时的beanName对应的BeanDefinition,所有模式都可以接受
- 创建出切面元数据工厂,并且根据工厂解析出所有的增强方法
- 倘若BeanDefinition是单例模式的, 那就将解析出来的增强方法存放进advisorsCache中,即存放进增强方法缓存中,key是beanName,而value就是解析出来的增强方法
- 如果不是单例模式的,那么就会将创建出的切面元数据工厂存放进aspectFactoryCache中,即存放进切面工厂缓存里面,key依然是beanName,而value则是创建出来的切面元数据工厂
- 我个人感觉这里分两个缓存是因为要对应单例与原型的语义,因为单例的依赖在Spring中只会创建一次,后面都是从缓存中获取的,所以这里也就将单例切面的增强方法直接存储起来,但原型则不是,原型则是每次使用就会去创建一次,所以对于原型模式,缓存的是切面元数据工厂,后面取得时候再用切面元数据工厂来解析出所有的增强方法
-
如果不是SINGLETON方法的,那么是不会支持单例的BeanDefinition的
- 判断当前BeanName对应的BeanDefinition是不是单例,如果是单例的,那就直接抛错,因为不支持,你切面实例化的方法是原型模式,而该切面的BeanDefinition则是单例模式,显然语义上是无法对应的
- 如果不是单例模式,那么也是创建切面元数据工厂,并且使用工厂来解析出增强方法,最后存放进工厂缓存中
-
最后都会将解析出来的增强方法添加进结果集合中
-
-
最后将aspectName修改成结果集合(里面存储的是beanName而已),此时aspectName就不再为Null了,下次获取所有的增强方法都是从缓存中获取了
-
返回结果
-
-
如果不为空,那就证明已经解析过所有Bean了,并且已经将增强方法对应放到缓存里面了,此时就要考虑从缓存里面取了
- 首先会判断aspectNames集合里面是不是为空,如果为空,那就代表没有缓存的增强方法,直接return一个Collection.emptyList
- 如果aspectNames集合里面拥有beanName,那就考虑从缓存中取
- 遍历aspectNames
- 先考虑从advisorsCache中取,如果有,就取出value,里面value对应的就是增强方法,添加进返回集合里面
- 如果advisorsCache里面没有,再从aspectFactoryCache里面去取,此时取出的Value是MetaDataAwareAspectInstanceFactory,还需要使用该factory去解析出增强方法,然后也添加进返回集合里面
- 如果都没有,不做处理,直接下一个
-
最终都返回结果集合
整个流程是比较长的,但逻辑性也很强
但我们依然没有弄清楚增强方法是如何解析出来的,现在仅仅知道增强方法是经由这以下执行操作得到的
- 根据beanType和beanName去创建AspectMetaData(我称为切面元数据)【切面元数据仅仅用来判断切面的实例化类型,从这里我们可以看到,切面的实例化类型并不是统一的,不同切面会有不同的实例化类型】
- 使用beanFactory和beanName去创建MetaDataAwareAspectInstanceFactory(这里我称为切面元数据实例工厂),实际上是new一个BeanFactoryAspectInstanceFactory的
- advisorFactory使用创建的切面元数据实例工厂来获取增强方法,对应的方法为getAdvisors
this.advisoryFactory.getAdvisors
所以,获取所有的增强方法,被转移进了这里面来
可以看到这个是交由ReflectAspectAdvisorFactory来实现的(AspectJAdvisorFactory是一个接口)
源码如下
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
//获取被@Aspect标识的类,从工厂里面获取
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
//获取被@Aspect标识的name,也是从工厂中获取
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
//这里就比较奇怪了,为什么给工厂的参数只有BeanName与BeanFactory,是如何去到类型和名称的呢???
//对标识的类进行校验
validate(aspectClass);
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
// 注释上说,我们需要用修饰者来打包工厂,这样它才会只实例一次
//使用工厂来实例化LazySingletonAspectInstanceFactoryDecorator???
//为了确保只实例一次???
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
//使用一个ArrayList来存放增强方法
List<Advisor> advisors = new ArrayList<>();
//遍历从aspectClass反射获取的方法
for (Method method : getAdvisorMethods(aspectClass)) {
// 将方法转化成Advisor,也就是转化成增强方法,同时里面也会去检验这个方法是不是增强方法
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
//判断是否为Null
if (advisor != null) {
//如果不为null就添加进结果集合中
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
//如果到最终,若结果集不是空的,并且还配置了增强延迟初始化
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
//需要在首位加入同步实例化增强器???
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
//在首位添加同步实例化增强器
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
//获取aspectClass的所有贴上@DeclareParents注解的字段
//这个注解可能很少人用过,下面就来说说这个注解是干什么的
//@DeclareParents的作用是在不改动类的条件下,给这个类添加新功能
//更准确来说是给这个类添加某一个类的功能,当然也支持给这个类的子类去添加对应功能(这才是AOP)
//比如有时候我们要给一个类加功能,最简单的方法是改变这个类,但如果这个类太复杂了
//那么我们可以使用AOP来给他添加另一个类的功能
//其本质是创建了一个大的AOP代理,这个AOP代理对象里面就包含了两个对象
//一个是原先类的,一个新功能类
//调用的时候根据类的状态去进行调用方法,原先类强转成新功能类,然后就可以调用新添加的方法了
//@DeclareParents也是AOP的一个注解
//获取aspectClass的所有被@DeclaredParents修饰的字段(新功能类)
for (Field field : aspectClass.getDeclaredFields()) {
//同样进行转化成Advisor对象,然后过滤掉一些不符合条件的字段
Advisor advisor = getDeclareParentsAdvisor(field);
//如果不为null,添加进结果集里面
if (advisor != null) {
advisors.add(advisor);
}
}
//最终返回结果集
return advisors;
}
可以看到,这个方法主要是负责遍历从aspectClass里面获取的注解,然后对贴上注解的属性进行处理,转化成Advisor然后添加进结果集里面的,而且从这里可以看到,这里对注解的处理分为两种
- 对方法上的注解处理:即@Around,@Before,@After等。。。
- 使用getAdvisor方法将Method转化为Advisor
- 对字段上的注解处理:即@DeclaredParents
- 使用getDeclareParentsAdvisor对字段进行转化成Advisor
还有一个比较特殊的处理,就是针对方法上的注解的,如果配置了延时初始化,则还需要在advisors的第一位置去添加同步实例化增强器(这里还没弄懂怎么回事,延时初始化?同步实例化增强器?添加进首位不会把原先的解析方法注解出来的advisors给冲掉吗?)
先到此为止,下一篇再继续看Advisors的提取