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

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 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" />

 

 

 

 

目录
相关文章
|
24天前
|
数据采集
动态代理与静态代理在爬虫解析的优缺点
随着科技和互联网的发展,越来越多企业需要使用代理进行数据抓取。本文介绍了HTTP动态代理与静态代理的区别,帮助您根据具体需求选择最佳方案。动态代理适合大规模、高效率的爬取任务,但稳定性较差;静态代理则适用于小规模、高稳定性和速度要求的场景。选择时需考虑目标、数据量及网站策略。
43 4
|
28天前
|
Android开发 开发者 Python
通过标签清理微信好友:Python自动化脚本解析
微信已成为日常生活中的重要社交工具,但随着使用时间增长,好友列表可能变得臃肿。本文介绍了一个基于 Python 的自动化脚本,利用 `uiautomator2` 库,通过模拟用户操作实现根据标签批量清理微信好友的功能。脚本包括环境准备、类定义、方法实现等部分,详细解析了如何通过标签筛选并删除好友,适合需要批量管理微信好友的用户。
41 7
|
1月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
68 8
|
1月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
46 4
|
1月前
|
安全 Java 开发者
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟
【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。
72 4
|
2月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
55 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
3月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
2月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
150 9
|
2月前
|
JavaScript API
深入解析JS中的visibilitychange事件:监听浏览器标签间切换的利器
深入解析JS中的visibilitychange事件:监听浏览器标签间切换的利器
173 0
|
3月前
|
存储 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

推荐镜像

更多