Let’s learn SF framework series 7.3-spring-AOP-AOP configuration analysis together

The implementation of AOP in the SF framework is unified as a Bean to be parsed and processed, so the loading process is basically the same as that of a general bean (see " Learn SF Framework Series Together 5.8 - Module Beans - Annotation Bean Parsing 1 - Parsing Entry " for the detailed process), but the difference is The following are:
1. The namespace corresponding to AOP is "aop"
2. The NamespaceHandler of AOP corresponds to AopNamespaceHandler.

AopNamespaceHandler

The "aop" namespace parser.
It can be seen from the code of this class that AopNamespaceHandler inherits from NamespaceHandlerSupport, with only the init() method - mainly registering aop's BeanDefinitionParser

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());
		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());
	}
}

Among them:
aop:aspectj-autoproxy (@AspectJ configuration mode) corresponds to AspectJAutoProxyBeanDefinitionParser
aop:config (xml configuration mode) corresponds to ConfigBeanDefinitionParser

Parsing AspectJ configuration patterns

When parsing BeanDefinition in @AspectJ configuration mode, it actually does not load the application BeanDefinition of AOP annotations, but loads the AspectJ automatic proxy creator (org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator).

AspectJAutoProxyBeanDefinitionParser

// aop:aspectj-autoproxy元素解析入口
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
    
    
	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
    
    
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		// 注册元素属性或子节点
		extendBeanDefinition(element, parserContext);
		return null;
	}

	private void extendBeanDefinition(Element element, ParserContext parserContext) {
    
    
		// 获取解析容器中的Aop内部bean-org.springframework.aop.config.internalAutoProxyCreator的bean定义
		BeanDefinition beanDef = parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
		if (element.hasChildNodes()) {
    
    
			// 把子节点合并为元素管理集合,并作为属性值设置到内部管理bean上
			addIncludePatterns(element, parserContext, beanDef);
		}
	}

	private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
    
    
		// 创建元素管理集合
		ManagedList<TypedStringValue> includePatterns = new ManagedList<>();
		// 获取所有子节点元素
		NodeList childNodes = element.getChildNodes();
		// 逐一处理节点,把属性
		for (int i = 0; i < childNodes.getLength(); i++) {
    
    
			Node node = childNodes.item(i);
			if (node instanceof Element includeElement) {
    
    
				// 以子节点名创建子节点valueHolder 
				TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
				// 设置valueHolder源
				valueHolder.setSource(parserContext.extractSource(includeElement));
				// 加入到元素管理集合
				includePatterns.add(valueHolder);
			}
		}
		if (!includePatterns.isEmpty()) {
    
    
			// 设置元素管理集合的根源
			includePatterns.setSource(parserContext.extractSource(element));
			// 把元素管理集合作为属性值设置到内部管理bean上
			beanDef.getPropertyValues().add("includePatterns", includePatterns);
		}
	}
}

AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement)

//Register automatic proxy creator

	public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
    
    
		// 注册解析aop注解的bean:org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
		BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
		/** 设置代理两个参数:
		1、设置代理选择:CGLIB动态代理还是JDK动态代理,默认为后者
		2、设置代理的暴露方式:默认值为false,如果设置为true的话,应用中就可以通过AopContext.currentProxy()拿到当前代理对象
		*/
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		// 把解析aop注解的bean注册为BeanComponentDefinition
		registerComponentIfNecessary(beanDefinition, parserContext);
	}

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source)

//Register the AspectJ automatic proxy creator

	@Nullable
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {
    
    
		// 注册AspectJ注解的解析器AnnotationAwareAspectJAutoProxyCreator
		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}
	
	@Nullable
	private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    
    
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

		/* 如果自动代理创建器的beanDefinition 已存在,则按优先级确定以哪个为准 注1 */
		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) {
    
    
					// 当已存在自动代理创建器优先级小,则设置BeanDefinition的BeanClassName为传入的className。加载bean时就按传入class加载
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}

		/* 如果自动代理创建器的beanDefinition 不存在,则新创建 */
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		//优先级设置为最高
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		//注册到BeanDefinitionRegistry 中
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}

Note 1: The automatic proxy creator has 3 creators, and the priority order (increasing in order) is as follows: InfrastructureAdvisorAutoProxyCreator, AspectJAwareAdvisorAutoProxyCreator, AnnotationAwareAspectJAutoProxyCreator

Parse xml configuration schema

ConfigBeanDefinitionParser.parse(Element element, ParserContext parserContext)

	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
    
    
		// 生成CompositeComponentDefinition 
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		// 压栈(可能出现递归调用)
		parserContext.pushContainingComponent(compositeDef);

		/**配置Aop注解的解析器(实际调用AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element))
		因此“xml配置模式”实际包含了“AspectJ配置模式”
		*/
		configureAutoProxyCreator(parserContext, element);

		/* 解析子元素: ponitcut,advice或aspect */
		List<Element> childElts = DomUtils.getChildElements(element);
		for (Element elt: childElts) {
    
    
			String localName = parserContext.getDelegate().getLocalName(elt);
			if (POINTCUT.equals(localName)) {
    
    
				parsePointcut(elt, parserContext);
			}
			else if (ADVISOR.equals(localName)) {
    
    
				parseAdvisor(elt, parserContext);
			}
			else if (ASPECT.equals(localName)) {
    
    
				parseAspect(elt, parserContext);
			}
		}

		// 出栈,并把CompositeComponentDefinition注册到解析容器
		parserContext.popAndRegisterContainingComponent();
		return null;
	}

ConfigBeanDefinitionParser.parsePointcut(Element pointcutElement, ParserContext parserContext)

Analysis, the sample configuration is as follows:

<aop:pointcut id="pointcut" expression="execution(* com.learnsf.demo.spring.aop.BzServiceImplForXml.*(..))"/>

code show as below:

	private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
    
    
		String id = pointcutElement.getAttribute(ID);
		// 获取切入点表达式
		String expression = pointcutElement.getAttribute(EXPRESSION);
		// 切入点BeanDefinition是抽象的,防止作为普通BeanDefinition被实例化
		AbstractBeanDefinition pointcutDefinition = null;

		try {
    
    
			// 压栈
			this.parseState.push(new PointcutEntry(id));
			
			// 根据切入点表达式生成切入点BeanDefinition(对应class: AspectJExpressionPointcut) 注1
			pointcutDefinition = createPointcutDefinition(expression);
			pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));

			/* 向解析容器注册pointcutDefinition */
			String pointcutBeanName = id;
			if (StringUtils.hasText(pointcutBeanName)) {
    
    
				parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
			}
			else {
    
    
				// pointcutElement没定义id(即pointcutBeanName),注册时自动生成pointcutBeanName 
				pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
			}

			// 生成PointcutComponentDefinition并注册到解析容器
			parserContext.registerComponent(
					new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
		}
		finally {
    
    
			// 出栈
			this.parseState.pop();
		}

		return pointcutDefinition;
	}

Note 1: It is critical to generate PointcutDefinition. The pointcut expression can determine which beans need to create proxy objects. The code is as follows:

	protected AbstractBeanDefinition createPointcutDefinition(String expression) {
    
    
		// 生成AspectJExpressionPointcut的BeanDefinition
		RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
		beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
		// 该bean是合成的-即运行时不是直接生成bean本身,而是需要代理介入并基于PropertyValues进行合成
		beanDefinition.setSynthetic(true);
		// 设置bean属性值(对于合成bean来说,属性值就是合成bean的依据或条件)
		beanDefinition.getPropertyValues().add(EXPRESSION, expression);
		return beanDefinition;
	}

ConfigBeanDefinitionParser.parseAdvisor(Element advisorElement, ParserContext parserContext)

Analysis, the sample configuration is as follows:

<aop:advisor advice-ref="springAopLog" pointcut-ref="pointcut"/>

code show as below:

	private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
    
    
		// 创建advisor的BeanDefinition (对应class: DefaultBeanFactoryPointcutAdvisor);同时配置了必须的属性:advice-ref(通知对象(切面类)的bean名)
		AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
		String id = advisorElement.getAttribute(ID);

		try {
    
    
			this.parseState.push(new AdvisorEntry(id));
			
			/* 向解析容器注册pointcutDefinition */
			String advisorBeanName = id;
			if (StringUtils.hasText(advisorBeanName)) {
    
    
				parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
			}
			else {
    
    
				advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
			}

			/* 设置属性:pointcut(必须设置),ponitcut属性可能是pointcut-ref 也可能是 pointcut */
			Object pointcut = parsePointcutProperty(advisorElement, parserContext);
			if (pointcut instanceof BeanDefinition) {
    
    
				/*是pointcut配置,则创建一个pointcut的BeanDefinition*/
				//设置属性
				advisorDef.getPropertyValues().add(POINTCUT, pointcut);
				//生成并注册AdvisorComponentDefinition
				parserContext.registerComponent(
						new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
			}
			else if (pointcut instanceof String) {
    
    
				/*是pointcut-ref配置,pointcut就是pointcut名字 */
				//设置属性,置为运行期对pointcut的bean引用(RuntimeBeanReference)
				advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
				//生成并注册AdvisorComponentDefinition
				parserContext.registerComponent(
						new AdvisorComponentDefinition(advisorBeanName, advisorDef));
			}
		}
		finally {
    
    
			this.parseState.pop();
		}
	}

ConfigBeanDefinitionParser.parseAspect(Element aspectElement, ParserContext parserContext)

Analysis, the sample configuration is as follows:

        <!--配置切面-->
        <aop:aspect id="aspect" ref="springAopLog">
	        <!--配置切入点-->
    	    <aop:pointcut id="pointcut" expression="execution(* com.learnsf.demo.spring.aop.BzServiceImplForXml.*(..))"/>
	        
	        <!--配置通知-->
	        <aop:before method="before" pointcut-ref="pointcut"/>
	        <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="returnVal "/>
	        <aop:around method="around" pointcut-ref="pointcut"/>
	        <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/>
	        <aop:after method="after" pointcut-ref="pointcut" />

	        <!--配置引入:把附加类bzServiceImpl方法植入到BzServiceExtraImpl(运行时可把BzServiceExtraImpl转换成bzServiceImpl,并调用bzServiceImpl的方法;而编码时两者没有关系) -->
            <aop:declare-parents types-matching="com.learnsf.demo.spring.aop.BzServiceExtraImpl+"
                                 implement-interface="com.learnsf.demo.spring.aop.BzService"
                                 delegate-ref="bzServiceImpl" />
        </aop:aspect >

code show as below:

	private void parseAspect(Element aspectElement, ParserContext parserContext) {
    
    
		String aspectId = aspectElement.getAttribute(ID);
		// 获取切面bean名(引用)
		String aspectName = aspectElement.getAttribute(REF);

		try {
    
    
			this.parseState.push(new AspectEntry(aspectId, aspectName));
			
			List<BeanDefinition> beanDefinitions = new ArrayList<>();
			List<BeanReference> beanReferences = new ArrayList<>();

			// 查找名称匹配DECLARE_PARENTS的子元素(只在直接子级中查找) 注1
			List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
			/* 解析DECLARE_PARENTS,构建DeclareParentsAdvisor的BeanDefinition,加入到BeanDefinition集合 */
			for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
    
    
				Element declareParentsElement = declareParents.get(i);
				beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
			}

			// We have to parse "advice" and all the advice kinds in one loop, to get the
			// ordering semantics right.
			/* 解析包含的所有advisor */
			NodeList nodeList = aspectElement.getChildNodes();
			boolean adviceFoundAlready = false;
			for (int i = 0; i < nodeList.getLength(); i++) {
    
    
				Node node = nodeList.item(i);
				/* advice节点才处理 */
				if (isAdviceNode(node, parserContext)) {
    
    
					if (!adviceFoundAlready) {
    
    
						adviceFoundAlready = true;
						if (!StringUtils.hasText(aspectName)) {
    
    
							parserContext.getReaderContext().error(
									"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
									aspectElement, this.parseState.snapshot());
							return;
						}
						// 每个advice节点都生成一个新的bean引用-引用当前切面
						beanReferences.add(new RuntimeBeanReference(aspectName));
					}
					// 调用advice解析(见前面方法)
					AbstractBeanDefinition advisorDefinition = parseAdvice(
							aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
					// 把advisorDefinition加入到BeanDefinition集合
					beanDefinitions.add(advisorDefinition);
				}
			}
		
			// 生成切面的ComponentDefinition
			AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
					aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
			parserContext.pushContainingComponent(aspectComponentDefinition);
			/* 解析切面的子元素pointcut */
			List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
			for (Element pointcutElement : pointcuts) {
    
    
				parsePointcut(pointcutElement, parserContext);
			}
			// 把切面的ComponentDefinition加入到解析容器
			parserContext.popAndRegisterContainingComponent();
		}
		finally {
    
    
			this.parseState.pop();
		}
	}

Note 1: The DECLARE_PARENTS ("declare-parents") element is Introduction, which means introducing new attributes and methods to the specified target class.

Guess you like

Origin blog.csdn.net/davidwkx/article/details/131737814