一.基于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-class和expose-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-class和expose-proxy属性,则会修改BeanDefinition的属性,相当于打个标记,是否需要强制使用Cglib代码,是否需要暴露代理对象。
总结
本文讲解了两种开启AOP的方式,核心逻辑就是在beanDefinitionMap中注册AnnotationAwareAspectJAutoProxyCreator。那么我们是否有疑问,为什么注册此Bean呢?它有什么用,有了它就能实现AOP吗?我只能说着仅仅只是一个开始,下一篇文章,我会详细解答。如感兴趣,请持续关注AOP系列博文,谢谢!