AOP静态代理解析1-标签解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: AOP静态代理使用示例见Spring的LoadTimeWeaver(代码织入)Instrumentation使用示例见java.lang.instrument使用AOP的静态代理主要是在虚拟机启动时通过改变目标对象字节码的方式来完成对目标对象的增强,它与动态代理相比具有更高的效率,因为在动态代理调用的过程中,还需要一个动态创建代理类并代理目标对象的步骤,而静态代理则是在启动时便完成了字节码增强,当系统再次调用目标类时与调用正常的类并无差别,所以在效率上会相对高些。

AOP静态代理使用示例见Spring的LoadTimeWeaver(代码织入)

Instrumentation使用示例见java.lang.instrument使用

AOP的静态代理主要是在虚拟机启动时通过改变目标对象字节码的方式来完成对目标对象的增强,它与动态代理相比具有更高的效率,因为在动态代理调用的过程中,还需要一个动态创建代理类并代理目标对象的步骤,而静态代理则是在启动时便完成了字节码增强,当系统再次调用目标类时与调用正常的类并无差别,所以在效率上会相对高些。

AspectJ所做的事

在Spring中的静态AOP直接使用了AspectJ提供的方法,而AspectJ又是在Instrument基础上进行的封装。就以上面的两个使用示例来看,至少在AspectJ中会有如下功能。

(1)读取META-INF/aop.xml。

(2)将aop.xml中定义的增强器通过自定义的ClassFileTransformer织入对应的类中。

这都是AspectJ所做的事情,并不在我们讨论的范畴,Spring是直接使用AspectJ,也就是将动态代理的任务直接委托给了AspectJ,那么,Spring怎么嵌入AspectJ的呢?从配置文件入手。

标签解析入口

spring.handlers

http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler

public class ContextNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }

}

继续跟进LoadTimeWeaverBeanDefinitionParser,作为BeanDefinitionParser接口的实现类,他的核心逻辑是从parse函数开始的,而经过父类的封装,LoadTimeWeaverBeanDefinitionParser类的核心实现被转移到了doParse函数中

    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {
            RootBeanDefinition weavingEnablerDef = new RootBeanDefinition();
            weavingEnablerDef.setBeanClassName(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);
            parserContext.getReaderContext().registerWithGeneratedName(weavingEnablerDef);

            if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) {
                new SpringConfiguredBeanDefinitionParser().parse(element, parserContext);
            }
        }
    }

其实之前在分析动态AOP也就是在分析配置中已经提到了自定义配置的解析流程,对于的解析无非是以标签作为标志,进而进行相关处理类的注册,那么对于自定义标签其实是起到了同样的作用。上面函数的核心作用其实就是注册一个对于ApectJ处理的类org.Springframework.context.weaving.AspectJWeavingEnabler,它的注册流程总结起来如下。

(1)是否开启AspectJ。

<context:load-time-weaver aspectj-weaving="autodetect" />

这个标签中还有一个属性aspectj-weaving,这个属性有3个备选值,on、off和autodetect,默认为autodetect,也就是说,如果我们只是使用了,那么Spring会帮助我们检测是否可以使用AspectJ功能,而检测的依据便是文件META-INF/aop.xml是否存在,看看在Spring中的实现方式。

    protected boolean isAspectJWeavingEnabled(String value, ParserContext parserContext) {
        if ("on".equals(value)) {
            return true;
        }
        else if ("off".equals(value)) {
            return false;
        }
        else {
            // Determine default...
            ClassLoader cl = parserContext.getReaderContext().getResourceLoader().getClassLoader();
            return (cl.getResource(AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE) != null);
        }
    }

(2)将org.Springframework.context.weaving.AspectJWeavingEnabler封装在BeanDefinition中注册。

当通过AspectJ功能验证后便可以进行AspectJWeavingEnabler的注册了,注册的方式很简单,无非是将类路径注册在新初始化的RootBeanDefinition中,在RootBeanDefinition的获取时会转换成对应的class。(weavingEnablerDef.setBeanClassName(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);)尽管在init方法中注册了AspectJWeavingEnabler,但是对于标签本身Spring也会以bean的形式保存,也就是当Spring解析到标签的时候也会产生一个bean,而这个bean中的信息是什么呢?

在LoadTimeWeaverBeanDefinitionParser类中有这样的函数:

private static final String WEAVER_CLASS_ATTRIBUTE = "weaver-class";
    private static final String ASPECTJ_WEAVING_ATTRIBUTE = "aspectj-weaving";
    private static final String DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME =
            "org.springframework.context.weaving.DefaultContextLoadTimeWeaver";
    private static final String ASPECTJ_WEAVING_ENABLER_CLASS_NAME =
            "org.springframework.context.weaving.AspectJWeavingEnabler";
    @Override
    protected String getBeanClassName(Element element) {
        if (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) {
            return element.getAttribute(WEAVER_CLASS_ATTRIBUTE);
        }
        return DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME;
    }
    @Override
    protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
        return ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME;//loadTimeWeaver
}

其中,可以看到:

WEAVER_CLASS_ATTRIBUTE="weaver-class"

DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME ="org.Springframework.context.weaving.DefaultContextLoadTimeWeaver";

ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME=”loadTimeWeaver”

凭以上的信息我们至少可以推断,当Spring在读取到自定义标签后会产生一个bean,而这个bean的id为loadTimeWeaver,class为org.Springframework.context.weaving.DefaultContextLoadTimeWeaver,也就是完成了DefaultContextLoadTimeWeaver类的注册。

完成了以上的注册功能后,并不意味这在Spring中就可以使用AspectJ了,因为我们还有一个很重要的步骤忽略了,就是LoadTimeWeaverAwareProcessor的注册。在AbstractApplicationContext中的prepareBeanFactory函数中有这样一段代码:

if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {//loadTimeWeaver
  beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
  // Set a temporary ClassLoader for type matching.
  beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader (beanFactory.getBeanClassLoader()));
}

在AbstractApplicationContext中的prepareBeanFactory函数是在容器初始化时候调用的,也就是说只有注册了LoadTimeWeaverAwareProcessor才会激活整个AspectJ的功能。

 

总结:

在解析load-time-weaver标签时,从getBeanClassName方法中可以看到,如果没有指定weaver-class属性,会自动给容器中注入一个org.springframework.context.weaving.DefaultContextLoadTimeWeaver类型的bean,从resolveId方法中看到,该bean的名称为loadTimeWeaver。在doParse方法中,还会注册一个类型为org.springframework.context.weaving.AspectJWeavingEnabler的匿名bean。

从此可以看出下面两段配置完全是等价的:

<bean id="loadTimeWeaver"  
    class="org.springframework.context.weaving.DefaultContextLoadTimeWeaver"></bean>  
<bean class="org.springframework.context.weaving.AspectJWeavingEnabler"></bean> 
<context:load-time-weaver aspectj-weaving="autodetect" />

 与

<context:load-time-weaver aspectj-weaving="autodetect" />

 

 

 

 

目录
相关文章
|
4月前
|
XML Java API
Spring AOP切点和通知机制的深度解析
Spring AOP切点和通知机制的深度解析
80 4
|
3天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
8 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
1月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
4天前
|
JavaScript API
深入解析JS中的visibilitychange事件:监听浏览器标签间切换的利器
深入解析JS中的visibilitychange事件:监听浏览器标签间切换的利器
11 0
|
1月前
|
存储 JavaScript Java
使用NekoHTML解析HTML并提取META标签内容
关于NekoHTML的代码样例,这里提供一个简单的示例,用于展示如何使用NekoHTML来解析HTML文档并提取其中的信息。请注意,由于NekoHTML的具体实现和API可能会随着版本更新而有所变化,以下代码仅供参考。 ### 示例:使用NekoHTML解析HTML并提取META标签内容 ```java import org.cyberneko.html.parsers.DOMParser; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml
|
2月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
37 0
Spring高手之路22——AOP切面类的封装与解析
|
2月前
|
缓存 安全 Java
Spring AOP 中两种代理类型的限制
【8月更文挑战第22天】
22 0
|
2月前
|
Java Spring
|
3月前
|
缓存 安全 Java
Spring高手之路21——深入剖析Spring AOP代理对象的创建
本文详细介绍了Spring AOP代理对象的创建过程,分为三个核心步骤:判断是否增强、匹配增强器和创建代理对象。通过源码分析和时序图展示,深入剖析了Spring AOP的工作原理,帮助读者全面理解Spring AOP代理对象的生成机制及其实现细节。
44 0
Spring高手之路21——深入剖析Spring AOP代理对象的创建
|
2月前
|
Ubuntu 应用服务中间件 nginx
Docker 解析:如何将 Nginx 容器化并用作代理
Docker 解析:如何将 Nginx 容器化并用作代理
135 0

推荐镜像

更多