Spring源码解析之AOP篇(三)----Spring开启AOP的两种方式

一.基于XML的方式

在Spring的早些版本,流行xml的配置方式。只要在xml中配置如下的标签以及属性,Spring就会帮我们解析。

    <!--开启自动扫描-->
    <context:component-scan base-package="com.seaway.curatorframework"/>
    <!--开启自动代理-->
    <aop:aspectj-autoproxy/>

为什么配置了<aop:aspectj-autoproxy>就能实现AOP功能呢?这里就要涉及到Spring的另一个知识点了,即Spring自定义标签解析。我在另一篇文章有详细讲解过,大家有兴趣的可以去看看细说Spring自定义标签,这里就不再赘述。大概意思就是这一类自定义标签,Spring有它独特的解析方式,<aop:aspectj-autoproxy>的解析类为AopNamespaceHandler

public class AopNamespaceHandler extends NamespaceHandlerSupport {
    public AopNamespaceHandler() {
    }

    public void init() {
        this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
        this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }
}

这里先重点关注aspectj-autoproxy的解析,进入registerBeanDefinitionParser方法看看都做了什么事情。

private final Map<String, BeanDefinitionParser> parsers = new HashMap();

protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
    this.parsers.put(elementName, parser);
}

就是将当前解析到的标签封装成一个parser,存入map中,待后面进行解析。那什么时候会执行AopNamespaceHandler中的init方法呢?这里有一个逻辑所有的spring的配置文件,最终会被解析成BeanDefinitionMap存储起来,便于以后进行bean的创建等过程。

protected void doRegisterBeanDefinitions(Element root) {
	//具体的解析过程由BeanDefinitionParserDelegate实现,
	//BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素
	BeanDefinitionParserDelegate parent = this.delegate;
	this.delegate = createDelegate(getReaderContext(), root, parent);

	if (this.delegate.isDefaultNamespace(root)) {
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
				if (logger.isInfoEnabled()) {
					logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
							"] not matching: " + getReaderContext().getResource());
				}
				return;
			}
		}
	}

	//在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性
	preProcessXml(root);
	//从Document的根元素开始进行Bean定义的Document对象
	parseBeanDefinitions(root, this.delegate);
	//在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性
	postProcessXml(root);

	this.delegate = parent;
}

这里是DefaultBeanDefinitionDocumentReader的方法,逻辑便是将解析到的xml的document对象传入,然后转义成BeanDefinition的过程。而调用parseBeanDefinitions进行解析时,会分别对默认标签和自定义标签进行单独解析。默认标签即<bean>之类的。

//Bean定义的Document的元素节点使用的是Spring默认的XML命名空间
if (delegate.isDefaultNamespace(ele)) {
	//使用Spring的Bean规则解析元素节点
	parseDefaultElement(ele, delegate);
}
else {
	//没有使用Spring默认的XML命名空间,则使用用户自定义的解//析规则解析元素节点
	delegate.parseCustomElement(ele);
}

进入parseCustomElement方法,获取命名空间,并调用init方法的就是这里面的逻辑。

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	//获取节点的命名空间
	String namespaceUri = getNamespaceURI(ele);
	if (namespaceUri == null) {
		return null;
	}
	//这个地方是重点,先获取到命名空间解析器,然后进行解析
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	if (handler == null) {
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
	//调用命名空间的parse方法进行解析,转义成BeanDefinition
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

此处的resolver是DefaultNamespaceHandlerResolver,所以会调用该类的resolve方法,继续跟下去。

public NamespaceHandler resolve(String namespaceUri) {
	//获取所有的handler文件中的属性保存在map中
	Map<String, Object> handlerMappings = getHandlerMappings();
	//通过命名空间从map中取
	Object handlerOrClassName = handlerMappings.get(namespaceUri);
	if (handlerOrClassName == null) {
		return null;
	}
	else if (handlerOrClassName instanceof NamespaceHandler) {
		return (NamespaceHandler) handlerOrClassName;
	}
	else {
		String className = (String) handlerOrClassName;
		//获取对应的class对象
		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");
		}
		//通过初始化策略生成对应的handler实例
		NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
		//调用init方法,将对应的parser注册到map中,便于后续解析
		namespaceHandler.init();
		//存入缓存,防止重复解析配置文件
		handlerMappings.put(namespaceUri, namespaceHandler);
		return namespaceHandler;
	}
}

此方法首先会获取扫描所有jar下的META-INF/spring.handlers文件,并对其进行解析,以键值对的形式存于缓存中。

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

这是aop包下的spring.handlers文件的内容,此处就获取到了AopNamespaceHandler。通过反射创建实例,然后调用了init方法,将对应的parser注册到map中,便于后续的解析。所以到了这里,最初的逻辑也得到了验证。接下来继续看是如何进行解析的,又是将什么东西解析到了BeanDefinitionMap中?让我们进入对应parse方法一探究竟。

registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());

此处加入map中的parser是AspectJAutoProxyBeanDefinitionParser,因此进入该类的parse方法进行解析。

public BeanDefinition parse(Element element, ParserContext parserContext) {
	//向ioc容器注册一个AnnotationAwareAspectJAutoProxyCreator类型的BeanDefinition
	AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
	//继续解析element的子节点如<aop:include/>,将当前的BeanDefnition的属性完善
	extendBeanDefinition(element, parserContext);
	return null;
}

此处主要是向ioc容器注册一个AnnotationAwareAspectJAutoProxyCreator类型的BeanDefinition,第二部逻辑就是对属性的完善,让我们重点看下第一步的逻辑。

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
		ParserContext parserContext, Element sourceElement) {
	//此处才是真正的注册逻辑
	BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			parserContext.getRegistry(), parserContext.extractSource(sourceElement));
	//如果设置了proxy-target-class和expose-proxy属性,则在此进行赋值
	useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
	//向容器中注入组件
	registerComponentIfNecessary(beanDefinition, parserContext);
}

真正的注册逻辑其实在第一句,后面的两句代码只是对第一行的BeanDefinition进行属性的修改,继续跟进。

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
		@Nullable Object source) {
	//注册或者升级该代理创建器
	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

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

	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	//如果beanDefintionMap中有则不需要再构建了
	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) {
				apcDefinition.setBeanClassName(cls.getName());
			}
		}
		return null;
	}
	//如果beanDefintionMap中没有则创建一个
	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;
}

最后会执行如下代码this.beanDefinitionMap.put(beanName, beanDefinition);向IOC容器注册了该bean,beanName为org.springframework.aop.config.internalAutoProxyCreator。如果设置了proxy-target-classexpose-proxy属性,则会修改BeanDefinition的属性,相当于打个标记,是否需要强制使用Cglib代码,是否需要暴露代理对象。对于XML形式的解析就讲解到这了,至于为什么要注册这么一个bean,在下一篇博文我会做出解答。继续看下基于注解方式是如何开启AOP的吧!

二.基于Annotation的方式

接下来我会基于SpringBoot是实现AOP,首先引入相关jar包。

<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.8.9</version>
</dependency>

<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjrt</artifactId>
	<version>1.8.9</version>
</dependency>

然后在启动类上配置@EnableAspectJAutoProxy注解即可开启,因为是SpringBoot的缘故,会帮我们自动装配,默认是开启AOP的,也可以不加该注解。接下来着重分析这个注解到底干了什么事情。

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

    boolean exposeProxy() default false;
}

当前注解中有一个@Import注解,就是讲有关类加入到Spring容器中。该注解的用法有三种,第一种是直接在后面添加一个或多个类,如下形式。

@Import({HelloRequest.class, HelloResponse.class})
public class imp {
}

第二种,则是在@Import注解上配置选择器ImportSelector,配置这个选择器的作用就是有选择性的注入,会进行筛选。

@Import(selector.class)
public class imp {
}

public class selector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //在注入前,进行筛选
        return new String[]{"com.seaway.mibank.request.HelloRequest","com.seaway.mibank.response.HelloResponse"};
    }
}

第三种,则是在@Import注解上配置注册器ImportBeanDefinitionRegistrar,这个接口中有默认实现registerBeanDefinitions方法,其中的一个入参为BeanDefinitionRegistry,那就意味着我们可以自己自定义的向容器注入自己想要注入的Bean。

@Import(Registar.class)
public class imp {
}
public class Registar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(HelloRequest.class);
        registry.registerBeanDefinition("helloRequest", beanDefinition);
    }
}

此处注解传入的是一个Registar,所以我们看下AspectJAutoProxyRegistrar的registerBeanDefinitions中的逻辑。

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	//像容器中注册一个bean
	AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
	//获取到注解的属性
	AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
	if (enableAspectJAutoProxy != null) {
		if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
			//如果为true,则强制使用Cglib代理
			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
		}
		if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
			//如果为true,则会暴露代理对象
			AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
		}
	}
}

这里面的逻辑可以说是跟XML形式一模一样啊,this.beanDefinitionMap.put(beanName, beanDefinition);向IOC容器注册了该bean,beanName为org.springframework.aop.config.internalAutoProxyCreator。如果设置了proxy-target-classexpose-proxy属性,则会修改BeanDefinition的属性,相当于打个标记,是否需要强制使用Cglib代码,是否需要暴露代理对象。

总结

本文讲解了两种开启AOP的方式,核心逻辑就是在beanDefinitionMap中注册AnnotationAwareAspectJAutoProxyCreator。那么我们是否有疑问,为什么注册此Bean呢?它有什么用,有了它就能实现AOP吗?我只能说着仅仅只是一个开始,下一篇文章,我会详细解答。如感兴趣,请持续关注AOP系列博文,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值