Spring源码深度解析十四:@Aspect方式的AOP上篇 - @EnableAspectJAutoProxy

news2024/10/6 0:28:09

一、前言

文章目录:Spring源码深度解析:文章目录

二、简介

Aop 即面向切面编程,而 Aspect 是Aop 思想的一种实现。
并不是所有的AOP框架都相同,它们在连接点模型上可能有强弱之分,有些允许在字段修饰符级别的应用通知,有些只支持方法调用相关的连接点。需要注意的是 Spring 只支持方法级别的连接点。

Spring 提供了4种类型的AOP支持

  • 基于代理的经典Spring Aop
  • 纯Pojo切面
  • @Aspect注解驱动的切面
  • 注入式的Aspectj的切面
    前三种都是Spring Aop 实现的变体,Spring Aop 构建在动态代理之上,因此Spring 对Aop的支持局限于方法拦截。

本文分析的是 基于 @Aspect 注解的 Aop 源码。

1. Spring Aop 和 AspectJ 的关系

AspectJ 是一套AOP框架,是对java语言语法和语义的扩展,所以他自己提供了一套关键字,这也就是说,如果在没有安装 AspectJ的情况下,是无法使用 AspectJ 的。而Spring Aop 依赖的是Spring,仅仅能做到方法级别的拦截。所以在Spring中使用 @Aspect 注解实现的AOP 功能,其底层实现还是 Spring Aop。

2. @ApsectJ 的启用 - @EnableAspectJAutoProxy

在使用AspectJ AOP 功能时,我们需要使用注解@EnableAspectJAutoProxy(proxyTargetClass = true) 来开启Aop功能。那么我们的分析入口自然是从这个注解开始。

但实际上,启用AOP功能并不需要我们手动去声明。因为在只要我们引入了 SpringAop 对应的依赖,Spring就自动帮我们启用 Aop功能。其原因在 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration 类中,如下,Spring 会将 AopAutoConfiguration 加载作为配置类,其内部类也会加载,默认加载 CglibAutoProxyConfiguration。CglibAutoProxyConfiguration 上有 @EnableAspectJAutoProxy(proxyTargetClass = true) 注解,也就达到了默认开始AOP的功能。不过我们可以通过 spring.aop.auto=false 来关闭 Spring aop 的自动引入

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(Advice.class)
	static class AspectJAutoProxyingConfiguration {

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = false)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
				matchIfMissing = false)
		static class JdkDynamicAutoProxyConfiguration {

		}
		// 默认加载该类
		@Configuration(proxyBeanMethods = false)
		// 启用了 AspectJ 自动代理
		@EnableAspectJAutoProxy(proxyTargetClass = true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
				matchIfMissing = true)
		static class CglibAutoProxyConfiguration {

		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
			matchIfMissing = true)
	static class ClassProxyingConfiguration {

		ClassProxyingConfiguration(BeanFactory beanFactory) {
			if (beanFactory instanceof BeanDefinitionRegistry) {
				BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
				AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
		}
	}
}

不过我们仍旧可以知道,AOP的功能入口在于@EnableAspectJAutoProxy注解。

EnableAspectJAutoProxy#AspectJAutoProxyRegistrar()

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
	// 是否代理目标对象:即是否使用cglib 代理
	boolean proxyTargetClass() default false;
	// 是否暴露代理对象
	boolean exposeProxy() default false;

}

分析了这么久的源码,从上面我们可以看到@EnableAspectJAutoProxy注解 中使用了 @Import(AspectJAutoProxyRegistrar.class)注解引入了AspectJAutoProxyRegistrar 类,因此我们下面来看看AspectJAutoProxyRegistrar类的实现。

三、 AspectJAutoProxyRegistrar

在这里插入图片描述
AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,那么我们自然要看看他的registerBeanDefinitions方法了,registerBeanDefinitions方法的作用是在Spring进入下一步动作之前可以添加BeanDefinition,而Spring Aop 在这里将会将自动代理创建器AbstractAutoProxyCreator添加到Spring容器中,AbstractAutoProxyCreator是Spring 实现Aop的核心类。(Spring 在ConfigurationClassPostProcessor中完成了对 ImportBeanDefinitionRegistrar接口的处理,主要功能还是将BeanDefinition保存,等待Spring解析加载到容器中。具体可以看 :Spring源码深度解析:六、ConfigurationClassPostProcessor。

下面直接来看AspectJAutoProxyRegistrar#registerBeanDefinitions()方法 如下:

AspectJAutoProxyRegistrar#registerBeanDefinitions()

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 如有必要,注册AspectJ注释自动代理创建器。这里注册的自动代理创建器Aop实现的核心
		// 这里之所以说如有必要,是因为在调用该方法时,容器中可能已经创建了一个自动代理创建器,
		// 如果这个自动代理创建器优先级更高或者与当前需要创建的自动代理创建器是同一类型,则不需要创建。
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
		// 获取@EnableAspectJAutoProxy注解
		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			// 解析proxyTargetClass属性
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			//  解析exposeProxy属性
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

从上面代码我们可以看到,registerBeanDefinitions方法最主要的功能就是自动代理创建器的注册。(所谓的自动代理创建器,顾名思义就是可以用来自动创建代理的"机器",可以简单理解成Spring封装的一个创建代理对象的工具类,具有多种实现方式,这个下面会讲。这里使用AOP的实现方式,因此我们来看他的注册过程, 即 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); 方法 。

其中 registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法在经历数次跳转后最终调用了 AopConfigUtils#registerOrEscalateApcAsRequired()方法。

我们下面来看一下AopConfigUtils#registerOrEscalateApcAsRequired()方法的实现。

AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary()

	@Nullable
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
		return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
	}

AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary()

	@Nullable
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}

AopConfigUtils#registerOrEscalateApcAsRequired()

	// 保存候选的自动代理创建器集合。
	private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3);

	static {
		// Set up the escalation list...
		// 这里面三种都是 自动代理创建器,会根据情况选择一个自动代理创建器加载。
		// 需要注意的是,自动代理创建器只能加载一种,若已经加载一种,则会根据优先级选择优先级高的重新加载
		// 事务使用
		APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
		// Spring AOP 使用
		APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
	}

	// 这里的cls是AnnotationAwareAspectJAutoProxyCreator.class
	@Nullable
	private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable 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) {
					// 改变bean所对应的className 属性
					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;
	}

	/**
	 * 可以看到,所谓的优先级顺序实际上是在 APC_PRIORITY_LIST 集合的顺序
	 * @param registry
	 */
	public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			// 设置 proxyTargetClass 属性
			definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
		}
	}

	public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			// 设置 exposeProxy 属性
			definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
		}
	}

这里可以看到,整体是注册了一个beanName“org.springframework.aop.config.internalAutoProxyCreator”的bean,在 Aop 场景下,Bean 类型为AnnotationAwareAspectJAutoProxyCreator。到这里我们就可以知道Aop的功能完成肯定是在AnnotationAwareAspectJAutoProxyCreator中完成的,因此下面我们开始分析AnnotationAwareAspectJAutoProxyCreator的代码。

注:

  1. 这里之所以beanName (AUTO_PROXY_CREATOR_BEAN_NAME)bean的类型并不相同,是因为这个beanName特指内部的自动代理创建器,但是自动创建代理器会对应多种不同的实现方式。比如在默认的事务中,注入的bean类型却为InfrastructureAdvisorAutoProxyCreator,而AOP的实现却是 AnnotationAwareAspectJAutoProxyCreator。之所以注册不同是因为实现功能上的区别。对于事务的自动代理创建器来说,他只需要扫描被事务注解修饰的方法,并进行代理。而Spring Aop 则需要根据@PointCut注解 来动态的解析代理哪些方法。

  2. 关于优先级的问题,我们可以看到APC_PRIORITY_LIST集合的顺序,下标越大,优先级越高。因此可以得知优先级的顺序应该是
    InfrastructureAdvisorAutoProxyCreator < AspectJAwareAdvisorAutoProxyCreator < AnnotationAwareAspectJAutoProxyCreator

四、AnnotationAwareAspectJAutoProxyCreator

由于本文主要分析的是Spring Aop的过程,所以我们下面还是对 AnnotationAwareAspectJAutoProxyCreator自动代理创建器进行分析。AnnotationAwareAspectJAutoProxyCreator本身的逻辑并不多,核心逻辑在其父类AbstractAutoProxyCreator中。

上面我们可以看到,整个过程就是将AnnotationAwareAspectJAutoProxyCreator注册到 Spring 中并且设置一些属性。

那么我们来看看 AnnotationAwareAspectJAutoProxyCreator,其主要逻辑实际上还是在其父类AbstractAutoProxyCreator中完成。(包括事务的实现逻辑也主要在 AbstractAutoProxyCreator中,这一点后续关于事务的源码分析: Spring源码分析十四:事务实现① - AutoProxyRegistrar)
在这里插入图片描述
AnnotationAwareAspectJAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor接口的方法,SmartInstantiationAwareBeanPostProcessor接口方法穿插在Bean初始化的过程中,转念一想,Spring Aop的核心思想就是动态代理,那么必然会在bean初始化的时候"做手脚"。因此我们下面的重心就放在 SmartInstantiationAwareBeanPostProcessor的方法分析上。
注:关于 后处理器SmartInstantiationAwareBeanPostProcessor的内容 具体请参考 Spring源码深度解析:十二、后处理器 BeanPostProcessor

上面也说了,其主要逻辑在AbstractAutoProxyCreator中实现,这里是在AbstractAutoProxyCreator中实现的SmartInstantiationAwareBeanPostProcessor 方法,所以我们下面看的实际是AbstractAutoProxyCreator类,

五、AbstractAutoProxyCreator

这里额外提一下,Spring事务的实现也依赖于AbstractAutoProxyCreator类,并且逻辑与Aop 的实现基本一致,因为事务的实现的方式也是Aop代理。
下面是AbstractAutoProxyCreatorSmartInstantiationAwareBeanPostProcessor的一些实现方法的实现

@Override
	@Nullable
	public Class<?> predictBeanType(Class<?> beanClass, String beanName) {
		// 从代理缓存中获取代理类型
		if (this.proxyTypes.isEmpty()) {
			return null;
		}
		Object cacheKey = getCacheKey(beanClass, beanName);
		return this.proxyTypes.get(cacheKey);
	}

	@Override
	@Nullable
	public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(beanClass, beanName);

		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			// 是基础设施类 || 是被 @Aspect 注解修饰的类。则跳过 Aop代理
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}

		// Create proxy here if we have a custom TargetSource.
		// Suppresses unnecessary default instantiation of the target bean:
		// The TargetSource will handle target instances in a custom fashion.
		// 正常来说Aop的代理创建应当在Bean 创建后再进行代理类,但是这里在Bean创建前就可能进行了代理:
		// 对此,官方注释解释: 如果我们有自定义的TargetSource,请在此处创建代理。抑制目标Bean的不必要的默认实例化:
		// TargetSource将以自定义方式处理目标实例。
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if (targetSource != null) {
			if (StringUtils.hasLength(beanName)) {
				this.targetSourcedBeans.add(beanName);
			}
			// 获取代理增强点
			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
			// 创建代理类
			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		return null;
	}
	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) {
		return true;
	}

	@Override
	public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {

		return pvs;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		return bean;
	}

	/**
	 * Create a proxy with the configured interceptors if the bean is
	 * identified as one to proxy by the subclass.
	 * @see #getAdvicesAndAdvisorsForBean
	 */
	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

我们可以根据SmartInstantiationAwareBeanPostProcessor方法的调用顺序进行分析。如果想要生成代理,可以在bean初始化之后进行代理,也就是postProcessBeforeInstantiation方法中。但是在此之前,需要先判断一下当前bean是否需要代理,而这个判断应该在Bean 创建之前进行,即在postProcessBeforeInstantiation方法中。我们可以看到关键方法是在于wrapIfNecessary

不过在此之前我们先来看看postProcessBeforeInstantiation方法中的一段分析。

1. 是否跳过代理

实际上,并非所有满足织入条件的Bean 都会被代理。基础设施类无法代理,自身也无法代理自身。

我们这一部分主要是用来分析AbstractAutoProxyCreator#postProcessBeforeInstantiation中的这一段代码:

	// 是基础设施类 || 是被 @Aspect 注解修饰的类。则跳过 Aop代理
	if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return null;
	}

这段代码将在bean加载前判断bean是否交由Aop代理,亦或者换一种说法 : 判断将该Bean交由Spring容器创建还是交由Aop创建。在实际创建bean代理时候,Spring会根据cacheKey获取到值,为false则不需要代理。

我们可以看到关键的判断条件就是下面两个:

1.1. isInfrastructureClass(beanClass)

这里我们可以很清楚的看到,如果当前bean是基础类(AdvicePointcutAdvisorAopInfrastructureBean及其子类),则返回true。

AbstractAutoProxyCreator#isInfrastructureClass(Class<?> beanClass)

	protected boolean isInfrastructureClass(Class<?> beanClass) {
		boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
				Pointcut.class.isAssignableFrom(beanClass) ||
				Advisor.class.isAssignableFrom(beanClass) ||
				AopInfrastructureBean.class.isAssignableFrom(beanClass);
		if (retVal && logger.isTraceEnabled()) {
			logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
		}
		return retVal;
	}

1.2. shouldSkip(beanClass, beanName)

AspectJAwareAdvisorAutoProxyCreator#shouldSkip(Class<?> beanClass, String beanName)

protected boolean shouldSkip(Class<?> beanClass, String beanName) {
		// TODO: Consider optimization by caching the list of the aspect names
		// 寻找所有候选代理增强点。关于这个方法,在后面关于 getAdvicesAndAdvisorsForBean 的文章中会详细分析,这里就不分析
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		for (Advisor advisor : candidateAdvisors) {
			// 从前面的代码分析可以看出,如果是Aop的动态封装都是基于InstantiationModelAwarePointcutAdvisorImpl也就是InstantiationModelAwarePointcutAdvisor,自然是继承PointcutAdvisor
			// 如果代理类基于AspectJPointcutAdvisor && aspectName==beanName,即当前初始化的类是ApspectJ类本身。则返回true,跳过代理
			if (advisor instanceof AspectJPointcutAdvisor &&
					((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
				return true;
			}
		}
		return super.shouldSkip(beanClass, beanName);
	}

关于if语句判断条件:

  1. advisor instanceof AspectJPointcutAdvisorAdvisor两个子接口PointcutAdvisorIntroductionAdvisorIntroductionAdvisorPointcutAdvisor最本质上的区别就是,IntroductionAdvisor只能应用于类级别的拦截,只能使用Introduction型的Advice。而不能像PointcutAdvisor那样,可以使用任何类型的Pointcut,以及几乎任何类型的Advice
    而通过Spring Aop动态注入的是Advisor默认都是InstantiationModelAwarePointcutAdvisorImpl 都满此条件。所以这里为false的情况只有硬编码注入时IntroductionAdvisor类型的Advisor,所以这里基本都会返回true。

  2. ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) : 这里就是判断beanName是否是@Aspect注解修饰的beanname。即自身不能代理自身。

综上,跳过Aop代理的条件就是:Aop基础设施类或者 代理自身时 会跳过代理

2. AbstractAutoProxyCreator#wrapIfNecessary

上面一步是强行跳过了一部分不能代理的Bean,如果到达这一步说明当前Bean不需要跳过代理,那么则需要判断当前Bean是否满足代理条件。

postProcessAfterInitialization()方法中我们可看到关键方法wrapIfNecessary
wrapIfNecessary主要是用来判断当前bean是否需要代理,如果需要,则进行bean封装。

	// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		// 如果当前bean已经处理过了直接返回
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		// 无需增强。这个在AbstractAutoProxyCreator#postProcessBeforeInstantiation 方法中对 cacheKey 进行了判断缓存,this.advisedBeans.get(cacheKey) 的返回值代表当前bean是否需要aop代理。
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		// 给定的bean类是否是一个基础设施类(Advice、Pointcut、Advisor、AopInfrastructureBean) || 配置了指定bean不需要进行代理
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			// 如果不需要代理,则记录下来
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		// 如果存在增强方法则创建代理。
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		// DO_NOT_PROXY = null。很明显,即使当前Bean 需要进行代理,如果没有增强方法也没必要进行代理
		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;
	}

可以很明显的发现下面两个方法是关键。

// 获取适配的增强点
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
// 根据增强点创建对象
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);

需要注意的是,上面两句关键语句,在AbstractAutoProxyCreator#postProcessBeforeInstantiation()中也有过调用

2.1. 获取代理增强点 - getAdvicesAndAdvisorsForBean

篇幅所限,新开文章:Spring源码分析十五:@Aspect方式的AOP中篇 - getAdvicesAndAdvisorsForBean

2.2. 创建代理类 - createProxy

篇幅所限,新开文章: Spring源码深度解析十六:@Aspect方式的AOP 下篇 - createProxy

六、Spring Aop总结

1. @EnableAspectJAutoProxy (proxyTargetClass = true)开始Aop功能。实际上在引入aspectj包之后, Spring默认会通过AopAutoConfiguration配置类开启AOP功能

2. @EnableAspectJAutoProxy (proxyTargetClass = true)注解通过@Import(AspectJAutoProxyRegistrar.class)引入了AspectJAutoProxyRegistrar3. AspectJAutoProxyRegistrar中注册了自动代理创建器AnnotationAwareAspectJAutoProxyCreator。之后的操作都在AnnotationAwareAspectJAutoProxyCreator中进行。

4. AnnotationAwareAspectJAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor接口, 所以会在Bean创建的时候,
	发现并记录了合适的所有的Advisor并进行拦截代理。

	4.1. 发现所有的Advisor : 这里分为两步。
		第一步是扫描BeanFactory中的所有Advisor类型的bean。这里直接通过BeanFactory获取即可。假设这里获取到的Advisor集合为Advisors1。 
		第二步则是通过扫描@Aspect注解找到切面类,随后筛选切面类中的方法。找到被@Around@Before等注解修饰的通知(Advice) 进行解析,
			并封装成不同的Advice类型(这里不包括@Pointcut注解的解析), @Pointcut会被解析成切入点(Pointcut)。
			随后AdvicePointcut会被一起封装成一个Advisor (顾问,也即是Advisor) 。
			也就是说,在这里Spring封装出了Pointcut(实际上是AspectJExpressionPointcut)Advice(这里的Advice根据注解使用的不同分为多个种类,AspectJAroundAdviceAspectJMethodBeforeAdvice)以及
			包含他俩的Advisor(实际上是InstantiationModelAwarePointcutAdvisorImpl)。
			这个过程是在ReflectiveAspectJAdvisorFactory#getAdvisor和InstantiationModelAwarePointcutAdvisorImpl构造函数中完成的。
			这里就动态解析出了@Aspect 注解下的切入点,并被封装成了Advisor集合。假设这里的Advisor集合为Advisors2。
		这两步结束后,就解析出来了当前所有的Advisors =Advisors1 + Advisors2;
		
		注:可以简单的理解为一个Advisor即一个增强操作,一个Advisor包含AdvicePointcutAdvice定义了具体增强操作,如前置,后置,环绕等, 
			Pointcut定义了织入规则(即哪些类可以被代理),满足规则的bean方法才能被增强。

	4.2. 筛选所有合适的Advisor:上一-步筛选出来的Advisor可能并不适用于当前bean,所以需要筛选出合适于当前bean的Advisor。
		比如@Pointcut ("execution (* com.demo.service.impl.UserServiceImpl.findAll())");切入的是UserServiceImpl,而当前的bean如果是
		RoleServiceImpl就不应该被此Advisor所增强。

 

5. 第四步结束后,这里已经筛选出来了适合当前bean所使用的Advisor,下面需要创建代理类。创建代理则是委托给了ProxyFactory来完成。
   ProxyFactory 使用 Advisor 中的 Advice 创建出来了代理增强类并注入到Spring容器中。

	ProxyFactory中有筛选出来的Advisor集合,AdvisorsProxyFactory根据情况选择Jdk代理(JdkDynamicAopProxy)或者Cglib代理(ObjenesisCglibAopProxy)来完成创建代理类的操作。
	最终执行的代理方法,实际上都是MethodInterceptor#invoke方法。
	
	这里需要注意的是:对于AspectJAroundAdviceAspectJAfterThrowingAdvice他们直接实现了MethodInterceptor接口,所以可以直接使用,
	但是对于AspectJMethodBeforeAdviceAspectJAfterAdviceAspectJAfterReturningAdvice则需要进行一个适配。
	这个适配的是在创建代理类的过程中。在AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice方法中完成。

简单流程图如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/84418.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分享a股下单接口执行买入操作的流程代码

用户在使用a股下单接口&#xff0c;能够得到更多更准确的信息&#xff0c;让用户在股市当中&#xff0c;操作起来更加便捷和有效&#xff0c;对股市市场行情动向判断更加的准确一些。 下面看一下a股下单接口是如何执行买入操作的功能&#xff1a; std::cout << " …

小程序批发订货怎么做

小程序订货系统 批发订货是每个生产企业都不少不了的一个环节&#xff0c;伴随着信息化技术的不断更新&#xff0c;传统的订货方式已经不能满足生产企业的需求了&#xff0c;而鲜桥小程序订货系统基于微信生态&#xff0c;让你即买即用&#xff0c;轻松告别传统的订货方式&…

Java解析NC2018-2020降水量格点数据

经纬度范围 lon = 7849;lat = 5146;time = 12;double lon(lon=7849); :long_name = "longitude"; :unit = "degree"; double lat(lat=5146); :long_name = "latitude"; :unit = "degree"; double time(time=12); :long_nam…

【论文】Holistically-Nested Edge Detection

文章目录Development of Edge DetectionMulti-scale learning structureLoss FunctionNetwork Architecture&#x1f4c4;&#xff1a;Holistically-Nested Edge Detection&#x1f517;&#xff1a;https://openaccess.thecvf.com/content_iccv_2015/html/Xie_Holistically-Ne…

DSP篇--C6701功能调试系列之SRAM、ADC、喂狗测试

目录 1、SRAM测试 1.1 SRAM读写功能测试 1.2 EDAC测试 2、ADC测试 3、DSP喂狗测试 调试的前期准备可以参考前面的博文&#xff1a;DSP篇--C6701功能调试系列之前期准备_nanke_yh的博客-CSDN博客https://blog.csdn.net/nanke_yh/article/details/128277631 1、SRAM测试 1…

国税局验证码识别 识别不了我还不能input吗

前言 最近接到一个需求&#xff0c;就是在国税局的网页中输入 【发票代码】、【*发票号码】、【*开票日期】、【*开具金额(不含税)】、【*验证码】 然后将此发票进行下载&#xff0c;但是【验证码】真的是拦路虎啊&#xff0c;我也查询了好多大佬的论文&#xff0c;有的是通过J…

OTP语音芯片ic的工作原理,以及目前的现状和技术发展路线是什么?flash型

目录 otp工艺和flash工艺的区别和概念 衡量一个芯片的成本&#xff0c;简单可以归结为3个方面 芯片的生产工艺&#xff0c;比如OTP的工艺都是100nm左右的工艺、8寸晶圆&#xff0c;很便宜。并且OTP的技术需要光刻机光照的次数少&#xff0c;所以生产起来快&#xff0c;这样无…

openCV实战项目--人脸考勤

人脸任务在计算机视觉领域中十分重要&#xff0c;本项目主要使用了两类技术&#xff1a;人脸检测人脸识别。 代码分为两部分内容&#xff1a;人脸注册 和 人脸识别 人脸注册&#xff1a;将人脸特征存储进数据库&#xff0c;这里用feature.csv代替人脸识别&#xff1a;将人脸特…

【云计算与大数据技术】数据编码LZSS算法、Snappy压缩库及分布式通信系统的讲解(图文解释 超详细)

一、数据编码概述 数据编码概述 - 在分布式系统中需要处理大量的网络数据,为了加快网络数据的传输速度,通常需 要对传输数据进行编码压缩 数据压缩是以尽可能少的数码来表示信源所发出的信号&#xff0c;减少容纳给定的消息集合或数据采样集合的信号空间&#xff0c;这里讲的…

Linux安装Maven

目录 1.下载安装包 2.解压Maven 3.添加环境变量 4.检测Maven是否搭建成功 5. 编辑 Maven的settings.xml文件 5.1设置本地仓库 5.2添加阿里云镜像 1.下载安装包 访问查看需要的版本&#xff1a;Index of /dist/maven (apache.org) 安装包统一存放到/opt/software目录下…

Java基于springboot校园德育活动预约和评分管理系统+vue+elementUI

本系统结合计算机系统的结构、概念、模型、原理、方法&#xff0c;在计算机各种优势的情况下&#xff0c;采用JAVA语言&#xff0c;结合SpringBoot框架与Vue框架以及MYSQL数据库设计并实现的。本校园德育活动预约和评分管理系统主要包括个人中心、学生管理、活动信息管理、活动…

使用Docker实现容器之间的互通

目录 怎样实现容器之间的相互通信&#xff1f; 情况1&#xff1a;两个容器在同一网段上 情况2&#xff1a;两个容器在不同网段上 怎样实现容器之间的相互通信&#xff1f; 情况1&#xff1a;两个容器在同一网段上 命令拓展&#xff1a; 删除所有容器&#xff1a;docker rm …

NuSences 数据集解析以及 nuScenes devkit 的使用

文章目录一、官网介绍1.1 总览(Overview)1.1.1 数据搜集(Data collection)1.1.2 传感器同步&#xff08;Sensor synchronization&#xff09;1.2 数据格式&#xff08;Data format&#xff09;attributecalibrated_sensorcategoryego_poseinstancelidarseglogmapsamplesample_a…

安装包UI美化之路-升级安装与静默安装一键打包

在实际应用中&#xff0c;除了产品安装外&#xff0c;还需要能够持续升级到新的版本&#xff0c;这时候升级方案就显得比较重要&#xff1b;而还有一些使用场景&#xff0c;需要咱们的安装包在安装时&#xff0c;不要显示安装界面&#xff0c;直接在后台进行安装&#xff01; …

测试开发之前端篇-Web前端简介

自从九十年代初&#xff0c;人类创造出网页和浏览器后&#xff0c;Web取得了长足的发展&#xff0c;如今越来越多的企业级应用也选择使用Web技术来构建。 前面给大家介绍网络协议时讲到&#xff0c;您在阅读这篇文章时&#xff0c;浏览器是通过HTTP/HTTPS协议向服务器发送请求…

Python+Django的高考志愿填报辅助系统 计算机毕业设计

在各学校的教学过程中&#xff0c;学生的高考志愿填报辅助是一项非常重要的事情。随着计算机多媒体技术的发展和网络的普及&#xff0c;“基于网络的学习模式”正悄无声息的改变着传统的教室学习模式&#xff0c;“基于网络的教学平台”的研究和设计也成为教育技术领域的热点课…

音频转文字怎么转?三个方法教你音频转文字

昨天&#xff0c;朋友给我发了一条信息&#xff0c;大概的内容是“今天上班的时候&#xff0c;领导突然发了一段音频&#xff0c;说是会议的录音&#xff0c;让我朋友在下班之前整理成会议纪要发给他。无奈&#xff0c;我的朋友只能打开录音开始整理”。听到他还在听录音手动整…

Stata中的治疗效果:RA:回归调整、 IPW:逆概率加权、 IPWRA、 AIPW

今天的主题是Stata中的治疗效果。最近我们被客户要求撰写关于治疗效果的研究报告&#xff0c;包括一些图形和统计输出。 治疗效果估算器根据观察数据估算治疗对结果的因果关系。 我们将讨论四种治疗效果估计量&#xff1a; RA&#xff1a;回归调整IPW&#xff1a;逆概率加权I…

【卡尔曼滤波器】递归算法

大家好&#xff0c;我是小政。最近在学习卡尔曼滤波&#xff0c;本篇文章记录一下我学习的卡尔曼滤波器中的递归算法&#xff0c;通过举例子让大家更加清晰理解递归到底是什么&#xff1f;希望与同是卡尔曼滤波研究方向的同学进行一些交流。 递归算法1.为什么要用卡尔曼滤波器&…

zookeeper3.6.3升级jetty9.4.47解决安全漏洞CVE-2022-2048和CVE-2021-28169

客户扫描到zookeeper有CVE-2022-2048和CVE-2021-28169安全漏洞要求修复。 漏洞和官方解决办法如下&#xff1a; 一、# CVE-2022-2048 Jetty升级到这几个修复版本9.4.47. 10.0.10, 11.0.10 Eclipse Jetty 存在安全漏洞&#xff0c;该漏洞源于无效的 HTTP/2 请求可能占用连接导致…