Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

这里主要是使用org.springframework:spring-beans:5.2.0.RELEASE进行分析

文章目录


1. 查看源码相关的快捷键

快捷键 作用
Ctrl + Shift+i 出现类似于预览的小窗口
Ctrl + Enter (接上步)完全打开源码
Ctrl + 鼠标左键 一步到位打开源码 = Ctrl + Shift+i –>Ctrl + Enter
Alt+7 查看类的有什么方法
ctrl+f12 查看继承方法
ctrl+h 查看接口的实现类
alt+7 查看类的有什么方法
2下shift 全局搜索整个项目

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

基于注解配置的属性源码分析

①. aspectj-autoproxy标签

使用注解方式应用aop需要在配置文件中配置aop:aspectj-autoproxy/, 而该标签有两个属性,proxy-target-class 和 expose-proxy。

1. proxy-target-class属性

如果被代理的目标对象至少实现了一个接口,则会使用JDK动态代理,所有实现该目标类实现的接口都将被代理;如果该目标对象没有实现任何接口,则创建CGLIB动态代理。但是可以通过proxy-target-class属性强制指定使用CGLIB代理。如果指定了proxy-target-class="true"则将强制开启CGLIB动态代理。

2. expose-proxy属性

解决目标对象内部的自我调用无法实施切面增强的问题。

②. aspectj-autoproxy标签解析

分析一下Spring是如何解析自定义标签aspectj-autoproxy的。

自定义标签解析入口DefaultBeanDefinitionDocumentReader

③. DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法

DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	// 1、解析默认命名空间
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					// 默认命名空间
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					// 自定义命名空间
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		// 2、解析自定义命名空间
		else {
			delegate.parseCustomElement(root);
		}
	}

④. 通过parseCustomElement解析自定义标

通过parseCustomElement解析自定义标签

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

	@Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	// 1、获取namespaceUri
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		// 2、根据namespaceUri得到命名空间解析器
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		 // 2、使用命名空间解析器解析自定义标签
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

对自定义标签的解析交给了NamespaceHandler接口,如果我们想在Spring中实现自己的自定义标签,那么就需要实现NamespaceHandler接口,并通过重写其中的方法,来完成对自定义标签的解析。

⑤. Namespacehandler接口

Namespacehandler接口

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

⑥. 获取NamespaceHandler

获取NamespaceHandler

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析
Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

@Override
	@Nullable
	public NamespaceHandler resolve(String namespaceUri) {
	 // 1.获取所有的namespaceUri,NamespaceHandler键值对map集合并得到
    // 当前namespaceUri对应的NamespaceHandler类
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				}
				// 2、通过BeanUtils实例化NamespaceHandler
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				//最后调用NamespaceHandler实例的init方法。
				namespaceHandler.init();
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;

⑦. AopNamespaceHandler的init方法

AopNamespaceHandler的init方法

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

	@Override
	public void init() {
		
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		//aspectj-autoproxy标签,
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
		
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}
}

⑧. 通过NamespaceHandler的parse方法解析aspectj-autoproxy标签

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析
具体的转换工作委托给了BeanDefinitionParser实例:

	public BeanDefinition parse(Element element, ParserContext parserContext) {
	    // 1、注册AnnotationAwareAspectJAutoProxyCreator
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		// 2、处理子标签<aop:include/>, 指定@Aspect类,支持正则表达式,符合该表达式的切面类才会被应用
		extendBeanDefinition(element, parserContext);
		return null;
	

⑨. AnnotationAwareAspectJAutoProxyCreator注册器分析

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

	public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {
 // 1、注册AnnotationAwareAspectJAutoProxyCreator
		BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 2、解析子标签 proxy-target-class 和 expose-proxy
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 3、注册组件并发送组件注册事件
		registerComponentIfNecessary(beanDefinition, parserContext);
	}

1. 注册AnnotationAwareAspectJAutoProxyCreator

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

@Nullable
	private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

// 如果registry已经包含了internalAutoProxyCreator,
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 如果已经注册的internalAutoProxyCreator不是AnnotationAwareAspectJAutoProxyCreator,则需要判断优先级并决定使用哪个
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
// 如果已经注册的internalAutoProxyCreator是AnnotationAwareAspectJAutoProxyCreator,则无需特殊处理
			return null;
		}

		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
// HIGHEST_PRECEDENCE --> 指定最高优先级
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
// ROLE_INFRASTRUCTURE --> 表示Spring的内部bean
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册BeanDefinition
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}

2 .解析子标签 proxy-target-class 和 expose-proxy

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

⑩. <aop:include/>标签解析

<aop:include/>标签的作用:例如有下面的配置文件,并指定了<aop:include name=“aspectJTest”></aop:include>,则aspectJTest切面类会被应用,且name支持正则表达式,如果配置文件中的切面类不符合name的表达式,则切面类不会被应用。

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

Spring源码学习(十八)---基于注解配置Aop的aspectj-autoproxy解析

⑪. AspectJAutoProxyBeanDefinitionParser 源码分析

AspectJAutoProxyBeanDefinitionParser 源码分析

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

    // 解析标签的时候将会执行的方法
    @Override
    @Nullable
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        // 注册一个类型为AnnotationAwareAspectJAutoProxyCreator的bean到Spring容器中
        AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        // 通过读取配置文件对扩展相关属性   
        extendBeanDefinition(element, parserContext);
        return null;
    }

    private void extendBeanDefinition(Element element, ParserContext parserContext) {
        // 获取前面注册的AnnotationAwareAspectJAutoProxyCreator对应的BeanDefinition
        BeanDefinition beanDef =
            parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
        // 解析当前标签的子标签
        if (element.hasChildNodes()) {
            addIncludePatterns(element, parserContext, beanDef);
        }
    }

    // 解析子标签中的name属性,其可以有多个,这个name属性最终会被添加到
    // AnnotationAwareAspectJAutoProxyCreator的includePatterns属性中,
    // Spring在判断一个类是否需要进行代理的时候会判断当前bean的名称是否与includePatterns中的
    // 正则表达式相匹配,如果不匹配,则不进行代理
    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) {
                Element includeElement = (Element) node;
                // 解析子标签中的name属性
                TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
                valueHolder.setSource(parserContext.extractSource(includeElement));
                includePatterns.add(valueHolder);
            }
        }

        // 将解析到的name属性设置到AnnotationAwareAspectJAutoProxyCreator
        // 的includePatterns属性中
        if (!includePatterns.isEmpty()) {
            includePatterns.setSource(parserContext.extractSource(element));
            beanDef.getPropertyValues().add("includePatterns", includePatterns);
        }
    }
}
上一篇:springboot核心技术(二)-----自动配置原理、日志


下一篇:Python实现高精度(一)加法计算