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" />

 

 

 

 

目录
相关文章
|
3月前
|
XML Java API
Spring AOP切点和通知机制的深度解析
Spring AOP切点和通知机制的深度解析
67 4
|
19天前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
17 0
Spring高手之路22——AOP切面类的封装与解析
|
28天前
|
缓存 安全 Java
Spring AOP 中两种代理类型的限制
【8月更文挑战第22天】
13 0
|
28天前
|
Java Spring
|
2月前
|
缓存 安全 Java
Spring高手之路21——深入剖析Spring AOP代理对象的创建
本文详细介绍了Spring AOP代理对象的创建过程,分为三个核心步骤:判断是否增强、匹配增强器和创建代理对象。通过源码分析和时序图展示,深入剖析了Spring AOP的工作原理,帮助读者全面理解Spring AOP代理对象的生成机制及其实现细节。
30 0
Spring高手之路21——深入剖析Spring AOP代理对象的创建
|
1月前
|
Ubuntu 应用服务中间件 nginx
Docker 解析:如何将 Nginx 容器化并用作代理
Docker 解析:如何将 Nginx 容器化并用作代理
37 0
|
1月前
|
域名解析 缓存 负载均衡
深度解析Nginx正向代理的原理与实现
Nginx虽然主要被用作反向代理,但也可以通过一些特殊配置用作正向代理。虽然不是它的主流用途,但它仍能以其高性能和高稳定性为用户提供代理服务。不过,出于安全性和匿名性的考虑,在使用它作为正向代理时须谨慎配置,并根据实际需求做出调整。
56 0
|
2月前
|
数据安全/隐私保护 iOS开发
详细步骤解析:Undetectable指纹浏览器使用IPXProxy代理IP
对于品牌来说,社交媒体已经成为寻找目标受众的丰富资源。在社交媒体平台通过评论和留言进行推广具有很高的转化率,并且推广成本较低。为了获得可观的利润,大家可能需要管理至少几个社交媒体账号,然而在一台电脑上管理多个账号会比较困难。因此使用可靠的工具成为大家的必要选择,其中Undetectable指纹浏览器和IPXProxy代理IP就是两个不错的工具。下面给大家带来Undetectable指纹浏览器配置IPXProxy代理IP的详细教程。
154 0
|
2月前
|
设计模式 Java 程序员
解析Java中的动态代理与静态代理的区别
解析Java中的动态代理与静态代理的区别
|
3月前
|
前端开发 开发者 SEO
HTML基础标签解析:H1-H6、DIV与P的正确使用方法
HTML基础标签解析:H1-H6、DIV与P的正确使用方法

热门文章

最新文章

推荐镜像

更多