Spring(十六):创建AOP代理

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去做的

其实大概也能猜到是怎么做的

  1. 获取所有的beanName,即在beanFactory中注册的bean都会被提取出来
  2. 遍历所有的beanName,并且找出加上了@AspectJ注解的类,进行标记
  3. 对标记为AspectJ注解的类进行增强器的提取
  4. 将提取结果加入缓存

下面来看看源码是怎么实现的

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的提取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值