我们可以在类加载期通过字节码编辑技术将切面织入目标类,这种方式叫做 LTW(Load Time Weaving)。

AspectJ LTW 使用 Java 5.0 提供的代理功能实现织入工作 。JDK 的代理功能能够让代理器访问到 JVM 的底层部件,借此向 JVM 注册类文件转换器,在类加载时对类文件的字节码进行转换 。 AspectJ LTW 是基于 JDK 动态代理技术实现的,所以它的作用范围是整个 JVM ,因此这种方式较为粗放,对于单一 JVM 多个应用的场景并不适用 。

相对于 AspectJ LTW, Spring LTW 提供了细粒度的控制,支持在单个 ClassLoader 范围内实施类文件转换,且配置更为简单。

1 instrument 包的工作原理

JDK5.0 新增了 java.lang.instrument 包,它包含能对 JVM 底层组件进行访问的类 。 我们可以在启动时通过 JVM 的 java agent 代理参数获取 JVM 内部组件的引用,以便在后续操作中使用 。 借助 JDK 动态代理,我们可以在 JVM 启动时装配并应用 ClassTransformer,对类字节码进行转换,从而实现 AOP 功能。

java.lang.instrument 包含两个重要接口。

1.1 ClassFileTransformer

它是 Class文件转换器接口,它定义了一个方法:

 byte[]transform(  ClassLoader         loader,String              className,Class<?>            classBeingRedefined,ProtectionDomain    protectionDomain,byte[]              classfileBuffer)throws IllegalClassFormatException;

该方法实现对 Class 文件的字节码进行转换, classfileBuffer 是类文件对应的字节码数组,返回的 byte[] 为转换后的字节码 。 如果返回 null ,则表示不进行字节码转换。

1.2 Instrumentation

代表 JVM 内部的一个构件 可以通过该接口的方法向 JVM 的 Instrumentation 注册一些 ClassFileTransformer ,注册转换器的接口方法为:

voidaddTransformer(ClassFileTransformer transformer, boolean canRetransform);

当 ClassFileTransformer 实例注册到 JVM 后, JVM 在加载 Class 文件时,会先调用这个 ClassFileTransformer 的 transform() 方法对 Class 文件的字节码进行转换 。 如果向 JVM 中注册多个 ClassFileTransformer ,它们将按注册的顺序被调用 。 这样 ClassFileTransformer 的实现者就可以从 JVM 层面截获所有类的字节码,并加入希望添加的逻辑 。

下图描绘了拥有多个转换器的 JVM 从加载类到最终生成对应的类字节码的过程:

2 使用 LTW 织入切面

Spring LTW 支持 AspectJ 定义的切面,既可以是直接采用 AspectJ 语法定义的切面,也可以是采用基于 @AspectJ 注解通过 Java 类定义的切面。Spring LTW 采用了与 AspectJ LTW 相同的基础架构,即利用类路径下的 META-INF/aop.xml 配置文件找到切面定义及切面所要实施的候选目标类,通过 LoadTimeWeaver 在 ClassLoader 加载类文件时将切面织入目标类中。

利用特定 Web 容器的 ClassLoader ,通过 LoadTimeWeaver 将 Spring 提供的 ClassFileTransformer 注册到容器的 ClassLoader 中 。在类加载期间,注册的 ClassFileTransformer 会读取 AspectJ 的配置文件,即类路径下的 META-INF/aop.xml 文件,获取切面,接着对 Bean 类进行字节码转换,织入切面 。Spring 容器初始化 Bean 实例时,采用的 Bean 类就是已经被织入切面的类。

2.1 LoadTimeWeaver

大多数的 Web 应用服务器 ( 除了 Tomcat ) 的 ClassLoader 无需通过 javaagent 参数指定代理,即可支持直接访问 Instrument。拥有这种能力的 ClassLoader 被称之为 “ 组件使能( instrumentation-capable ) ”。 通过组件使能能力,我们就可以方便地访问 ClassLoader 的 Instrument。 Spring 使用 Web 应用服务器类加载器,为这些应用服务器分别提供了专门的 LoadTimeWeaver, 以便向特定的 ClassLoader 注册 ClassFileTransformer ,然后再对指定的类进行字节码转换,实现切面的织入 。

Spring 的 org.springframework.instrument.classloading.LoadTimeWeaver 接口定义了类加载期织入器的高层协议,该接口包含三个方法。

方法 说明
void addTransformer(ClassFileTransformer transformer) 新增 transformer 到织入器。
ClassLoader getInstrumentableClassLoader() 返回具有 Instrument 功能的 ClassLoader。
ClassLoader getThrowawayClassLoader() 返回可丢弃的 ClassLoader。

2.2 配置 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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 启用装载期织入--><context:load-time-weaver/>
</beans>

2.3 配置 Tomcat

这里以 Tomcat6.x 和 Tomcat7.x 为例。

  1. 首先在该页面中下载对应 Spring 框架版本的 spring-instrument-tomcat-{version}.jar,然后复制到 <TOMCAT_HOME>/lib 下,该 JAR 只有两个类,其中一个即是 TomcatInstrumentableClassLoader 类。
  2. 接着在 META-INF 目录下,新建 context.xml,内容为:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/xxx" docBase="xxx"><loader
            loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"useSystemClassLoaderAsParent="false"/>
</Context>

通过以上配置,Tomcat 容器就支持直接在类加载期实现织入功能啦O(∩_∩)O~

2.4 具有组件使能的 Web 应用服务器

具有组件使能的 Web 应用服务器有以下这几种:

  • BEA WebLogic 10.0+
  • OC4J 10.1.3.1+
  • Resin 3.1+
  • JBoss 5.x+

如果应用是运行在这些Web 应用服务器中,那么我们就只需在 Spring 中配置 <context:load-time-weaver/> 就可以啦O(∩_∩)O~

说说在 Spring AOP 中如何实现类加载期织入(LTW)相关推荐

  1. Spring AOP中定义切点PointCut详解

    1.AOP是什么? 软件工程有一个基本原则叫做"关注点分离"(Concern Separation),通俗的理解就是不同的问题交给不同的部分去解决,每部分专注于解决自己的问题.这年 ...

  2. 第十篇 Spring AOP中Load Time Weaver

    文章目录 前言 一.先聊Load Time 二.再说Weaver 三.回看Load Time Weaver 四.LTW初始化过程 1.激活 2. Weave过程 总结 前言 本文介绍Spring AO ...

  3. Spring AOP中定义切点(PointCut)和通知(Advice)

    本文讨论一下Spring AOP编程中的两个关键问题,定义切点和定义通知,理解这两个问题能应付大部分AOP场景. 如果你还不熟悉AOP,请先看AOP基本原理,本文的例子也沿用了AOP基本原理中的例子. ...

  4. 正确理解Spring AOP中的Around advice

    Spring AOP中,有Before advice和After advice,这两个advice从字面上就可以很容易理解,但是Around advice就有点麻烦了. 乍一看好像是Before ad ...

  5. Spring : Spring AOP 中的增强(Advice)或者通知

    1.美图 2.概述 概念参考:Spring :Spring AOP 中的一些术语 很多人将增强理解为通知,但是理解为增强会更加准确,因为它表示在连接点上执行的行为,这个行为是目标类类所没有的,是为目标 ...

  6. Spring :Spring AOP 中的一些术语

    1.美图 2.概述 2.1 连接点(Jointpoint) 连接点(Jointpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化.方法执行.方法调用.字段调用或处理异常等等,S ...

  7. Spring Aop中解析spel表达式,实现更灵活的功能

    前言 在Spring Aop中,我们可以拿到拦截方法的参数,如果能结合spel表达式,就能实现更加灵活的功能.典型的实现有Spring的缓存注解: @Cacheable(value = "u ...

  8. Spring AOP 中 advice 的四种类型 before after throwing advice around

    Spring  AOP(Aspect-oriented programming) 是用于切面编程,简单的来说:AOP相当于一个拦截器,去拦截一些处理,例如:当一个方法执行的时候,Spring 能够拦截 ...

  9. java @around_正确理解Spring AOP中的Around advice

    Spring AOP中,有Before advice和After advice,这两个advice从字面上就可以很容易理解,但是Around advice就有点麻烦了. 乍一看好像是Before ad ...

最新文章

  1. Windows 2003 AD升级到 Windows 2008 AD
  2. 《精通J2EE网络编程》中讲的JNDI 6.3总结
  3. 技术停滞_检测和测试停滞的流– RxJava常见问题解答
  4. GO语言学习之路23
  5. ug12.0安装教程(超级详细安装步骤)
  6. tableau 地图不显示怎么回事
  7. 9WinMap 映射
  8. 对数幅度谱图像matlab,fft2绘制图像的对数幅度谱,比较图像旋转、平移和缩放后的频谱...
  9. 组学生信| Front Immunol |基于血清蛋白质组早期诊断标志筛选的简单套路
  10. 抛弃Eclipse,投入IDEA 的独孤求败江湖
  11. html自动定时弹窗,html网页弹窗代码 setinterval 定时任务啊
  12. xftp、xshell安装出现1603错误解决,亲测有用
  13. 【算法leetcode每日一练】面试题 08.04. 幂集
  14. 记一次逗逼的codecraft算法大赛的参赛经历
  15. 【项目管理一点通】(44) 用户测试(Alpha测试)
  16. steam验证登录失败_如何向Steam添加两方面身份验证
  17. 关闭服务器windows server的IE浏览器的增强安全配置
  18. 【网络安全】跨站脚本攻击漏洞(了解)
  19. 实验一.MATLAB求解优化问题
  20. Rust 引入其他的 rs 文件

热门文章

  1. 简单典型二阶系统_2021考研概率必看典型例题
  2. 如何使用BurpSuite
  3. Flink Standalone模式HA部署
  4. Git-移动记录仪 贴心小棉袄 reflog
  5. anime.js 动画_Anime.js –轻量级JavaScript动画库
  6. IOS开发之酷酷爱魔兽
  7. 多台电脑共用鼠标键盘(局域网内)
  8. 安卓简单VMP思路笔记
  9. 微信小程序快速上手【1】
  10. Error while executing: npm ERR! D:\Program Files\Git\cmd\git.EXE ls-remote -h -t git://github.com/ad