Spring Aop标签解析原理详解

作者 : 开心源码 本文共8389个字,预计阅读时间需要21分钟 发布时间: 2022-05-12 共185人阅读

对于Spring Aop的实现,是非常复杂的,其实现过程主要包含xml标签的解析,切面表达式的解析,判断bean能否需要应用切面逻辑,以及使用Jdk代理商或者者是Cglib代理商生成代理商类。本文主要讲解Xml标签的解析的实现原理,在接下来几篇文章中,会依次对Spring Aop剩余的实现过程进行讲解。

关于Spring Aop的实现,因为其是使用自己设置标签进行驱动的,因此读者朋友假如对Spring如何实现自己设置标签比较熟习,那么可以继续往下阅读。

1. Aop使用示例

首先我们公告了一个切面类如下:

@Aspectpublic class DogAspect {  @Around("execution(public void com.business.Dog.*(..))")  public Object aspect(ProceedingJoinPoint joinPoint) throws Throwable {    System.out.println("before run.");    Object result = joinPoint.proceed();    System.out.println("after run.");    return result;  }}

该切面类主要用于环绕com.business.Dog类中的public类型的,并且返回值是void的所有方法,下面我们就在com.business包中公告一个Dog类如下:

public class Dog {  public void run() {    System.out.println("Tidy is running.");  }}

这里切面类和目标类都已经公告完成,但假如不将其加入Spring容器中,其是不会工作的,加入容器的方式非常简单,下面就是一种方式:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">    <bean id="dog" class="com.business.Dog"/>    <bean id="aspect" class="com.business.DogAspect"/>    <aop:aspectj-autoproxy/></beans>

这里需要说明的是,将DogAspect公告为一个bean并不能使其工作,由于其也仅仅只是一个bean而已,要使其工作还需要使用上面的<aop:aspectj-autoproxy/>标签实现切面的自动装配。下面使我们运行整个程序的驱动类:

public class DogApp {  public static void main(String[] args) {    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");    Dog dog = context.getBean(Dog.class);    dog.run();  }}

执行结果如下:

before run.Tidy is running.after run.

可以看到,我们在驱动类中获取的是Dog的实例,并且运行其run()方法,但是最终的运行结果中也运行了切面类中的环绕逻辑。

2. 实现原理

根据前面对Spring自己设置标签使用的讲解,我们知道这里<aop:aspectj-autoproxy/>就是一个自己设置标签,并且该标签会在相应jar包的META-INF目录下有一个spring.handlers文件,该文件中公告理解析该标签的类。通过查看该类我们得到如下配置:

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

这里我们打开AopNamespaceHandler,其实现如下:

public class AopNamespaceHandler extends NamespaceHandlerSupport {    @Override    public void init() {        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());    }}

可以看到,我们需要解析的标签解析器在这个类中进行了注册,即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);        }    }}

这里我们继续看AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);方法,该方法的实现如下:

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(            ParserContext parserContext, Element sourceElement) {    // 注册AnnotationAwareAspectJAutoProxyCreator的BeanDefinition    BeanDefinition beanDefinition =         AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(    parserContext.getRegistry(), parserContext.extractSource(sourceElement));    // 解析标签中的proxy-target-class和expose-proxy属性值,    // proxy-target-class主要控制是使用Jdk代理商还是Cglib代理商实现,expose-proxy用于控制    // 能否将生成的代理商类的实例防御AopContext中,并且暴露给相关子类使用    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);    // 将注册的BeanDefinition封装到BeanComponentDefinition中    registerComponentIfNecessary(beanDefinition, parserContext);}private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry,        @Nullable Element sourceElement) {    if (sourceElement != null) {        // 解析标签中的proxy-target-class属性值        boolean proxyTargetClass =            Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));        if (proxyTargetClass) {            // 将解析得到的proxy-target-class属性值设置到上面生成的            // AnnotationAwareAspectJAutoProxyCreator的BeanDefinition的proxyTargetClass            // 属性中            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);        }        // 解析标签中的expose-proxy属性值        boolean exposeProxy =             Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));        if (exposeProxy) {            // 将解析得到的expose-proxy属性值设置到            // AnnotationAwareAspectJAutoProxyCreator的exposeProxy属性中            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);        }    }}private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition,        ParserContext parserContext) {    // 假如生成的AnnotationAwareAspectJAutoProxyCreator的BeanDefinition成功,则将其封装到    // BeanComponentDefinition中,并且将其增加到ParserContext中    if (beanDefinition != null) {        BeanComponentDefinition componentDefinition =            new BeanComponentDefinition(beanDefinition,                 AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);        parserContext.registerComponent(componentDefinition);    }}

这里可以看到AnnotationAwareAspectJAutoProxyCreator的BeanDefinition在第一步进行了注册,而后读取标签中的proxy-target-class和expose-proxy属性,并且将属性值设置到生成的BeanDefinition中。最后将生成的BeanDefinition注册到ParserContext中。这里我们继续看AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement))方法,其实现如下:

@Nullablepublic static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,       @Nullable Object source) {    // 注册AnnotationAwareAspectJAutoProxyCreator类型的BeanDefinition    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class,       registry, source);}@Nullableprivate static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls,       BeanDefinitionRegistry registry, @Nullable Object source) {    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");    // 假如已经注册过AnnotationAwareAspectJAutoProxyCreator的Definition,假如其    // 和当前将要注册的BeanDefinition是同一个类型,则不再注册,假如不同,则判断其优先级比    // 当前将要注册的BeanDefinition要高,则将其类名设置为当前要注册的BeanDefinition的名称    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;    }    // 假如不存在已经注册的Aop的bean,则生成一个,并且设置其执行优先级为最高优先级,并且标识    // 该bean为Spring的系统Bean,设置完之后则对该bean进行注册    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;}

可以看到,在真正生成AnnotationAwareAspectJAutoProxyCreator的BeanDefinition的时候,首先会判断能否已经生成过该bean,这里不会将已经生成的bean进行覆盖;假如没有生成该bean,则创立一个并进行注册。这里需要说明的是,Spring注册该bean的时候使用的order是Ordered.HIGHEST_PRECEDENCE,这么设置的起因在于Spring使用该bean进行切面逻辑的织入,因此这个bean必需在所有客户自己设置的bean实例化之前进行实例化,而客户自己设置的bean的实例化优先级是比较低的,这样才能实现织入代理商逻辑的功能。

  1. 小结
    本文首先使用一个简单的示例展现了Spring Aop的使用方式,而后对标签中的<aop:aspectj-autoproxy/>解析过程进行了讲解。可以看到,该标签的解析过程最终是生成了一个AnnotationAwareAspectJAutoProxyCreator的BeanDefinition。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Spring Aop标签解析原理详解

发表回复