【总结】SpringAOP源码分析

一、SpringAOP的总体流程

  • 注册解析AOP的服务
  • 解析和加载横切逻辑
  • 将横切逻辑织入目标Bean中

二、为什么在启动类加上@EnableAspectJAutoProxy就能支持AOP

注解有两个属性

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
   boolean proxyTargetClass() default false;   
   boolean exposeProxy() default false;
}

proxyTargetClass

  • 如果为true,则将全部使用CGLIb进行代理;如果为false,则会尽可能使用JDK的动态代理,如果对象没有实现接口,将会使用CGLIb

exposeProxy

  • 决定是否可以将动态代理对象暴露出来

  • 如果设置为true将会放入AopContext类的ThreadLocal属性中的中,这样就可以通过容器获取到该动态代理的对象

SpringAOP的流程

  1. 注册解析AOP的服务
  2. 解析和加载横切逻辑
  3. 将横切逻辑织入目标Bean中

三、如何注册解析AOP的服务?

  • 主要依靠@Import(AspectJAutoProxyRegistrar.class)注解
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

   
   @Override
   public void registerBeanDefinitions(
         AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
       //往容器注册一个AOP基于注解的自动代理创建器,负责动态代理的创建
      AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
		//注册注解的两个信息
      AnnotationAttributes enableAspectJAutoProxy =
            AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
      if (enableAspectJAutoProxy != null) {
         if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
         }
         if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
         }
      }
   }

}

registerBeanDefinitions()方法有两个参数:

  • AnnotationMetadata:保存注解相关的信息
  • BeanDefinitionRegistry:往容器注册BeanDefinition

该方法做的事情:

  1. 往容器注册一个AOP基于注解的自动代理创建器,负责动态代理的创建
  2. 根据注解属性,注册的相关配置

什么时候将配置注册到容器中?

  • 在容器初始化执行Refresh方法的时候,通过容器级别的后置处理器注册的

四、横切逻辑加载分析

1. TargetSource接口

  • 实现该接口,可以实现池化的操作,将存取代理类或者替换代理类

SingletonTargetSource:确保被代理的实例是单例的,定义了一个final的Object类,确保每次调用的都是同一个实例

private final Object target;
@Override
public Class<?> getTargetClass() {
	return this.target.getClass();
}

ProtoTypeTargetSource:确保被代理的实例是多例的,每次调用时都会返回一个新的实例

@Override
public Object getTarget() throws BeansException {
   return newPrototypeInstance();
}

其他的还有

  • 提供池化的CommonsPool2TargetSource,类似于线程池
  • 提供热交换的HotSwappableTargetSource,可以增删改查代理实例

2. buildAspectJAdvisors方法

  1. 从容器中获取所有的beanName
  2. 遍历所有的beanName,解析出被@Aspect注解标记的类
  3. 提取Aspect类中的Advisors
  4. 将结果放入缓存
public List<Advisor> buildAspectJAdvisors() {
	List<String> aspectNames = this.aspectBeanNames;

	if (aspectNames == null) {
		synchronized (this) {
			aspectNames = this.aspectBeanNames;
			if (aspectNames == null) {
				List<Advisor> advisors = new ArrayList<>();
				//用于保存切面的名称的集合
				aspectNames = new ArrayList<>();
				//获取所有的beanName
				// AOP功能中在这里传入的是Object对象,代表去容器中获取到所有的组件的名称,然后再
				// 进行遍历,这个过程是十分的消耗性能的,所以说Spring会再这里加入了保存切面信息的缓存。
				String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
						this.beanFactory, Object.class, true, false);
				//遍历我们从IOC容器中获取处的所有Bean的名称
				for (String beanName : beanNames) {
					if (!isEligibleBean(beanName)) {
						continue;
					}
					//获取对应的bean的类型
					Class<?> beanType = this.beanFactory.getType(beanName);
					if (beanType == null) {
						continue;
					}
					//提取@Aspect注解标记的Class
					if (this.advisorFactory.isAspect(beanType)) {
						//是切面类
						//加入到缓存中
						aspectNames.add(beanName);
						AspectMetadata amd = new AspectMetadata(beanType, beanName);
						if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
							MetadataAwareAspectInstanceFactory factory =
									new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
							//Aspect里面的advice和pointcut被拆分成一个个的advisor,
							// advisor里的advice和pointcut是1对1的关系
							List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
							if (this.beanFactory.isSingleton(beanName)) {
								//单例则直接将Advisor类存到缓存
								this.advisorsCache.put(beanName, classAdvisors);
							}
							else {
								// 否则将其对应的工厂缓存
								this.aspectFactoryCache.put(beanName, factory);
							}
							advisors.addAll(classAdvisors);
						}
						else {
							// Per target or per this.
							if (this.beanFactory.isSingleton(beanName)) {
								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));
						}
					}
				}
				this.aspectBeanNames = aspectNames;
				return advisors;
			}
		}
	}

	if (aspectNames.isEmpty()) {
		return Collections.emptyList();
	}
	List<Advisor> advisors = new ArrayList<>();
	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;
}

五、横切逻辑织入

在Bean完成初始化后,利用责任链模式进行横切逻辑的织入。

1. 横切逻辑的织入方法提供的有两个

  • 第一个是正常流程的织入入口,通过后置处理器实现
  • 第二个是循环依赖的织入入口,通过获取EarlyBeanReference实现

2. 流程

  • 横切逻辑的织入前会进行一系列的判断

    • 被织入的类名称不能是空
    • 判断该类是否需要跳过织入或者是基础的类,比如实现了Advice、Pointcut等接口
  • 获取合适的切面,利用AspectJ框架,进行类级别的初筛和方法级别的精确筛选,这些切面在容器创建的时候已经完成解析

    • 初筛只能校验within的表达式,对于execution,只能检验精确到某个类的,表达式使用的包名加类名
  • 创建Bean对应的代理,默认使用SingletonTargetSource用于封装实现类的信息,根据Bean是否实现了接口来决定使用JDK动态代理或者CGLib

  • 将生成的动态代理,放入缓存中

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		//已经被处理过
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		//不需要被织入逻辑的
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		//是不是基础的bean 是不是需要跳过的
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}


		// 返回匹配当前Bean的所有Advice\Advisor\Interceptor
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			//创建Bean对应的代理,SingletonTargetSource用于封装实现类的信息
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}
		//该Bean是不需要进行代理的,下次就不需要重复生成了
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值