1. 关于LoadTimeWeaving

1.1 LTW与不同的切面织入时机

AOP——面向切面编程,通过为目标类织入切面的方式,实现对目标类功能的增强。按切面被织如到目标类中的时间划分,主要有以下几种:

  • 1.运行期织入
  • 这是最常见的,比如在运行期通过为目标类生成动态代理的方式实现AOP就属于运行期织入,这也是Spring AOP中的默认实现,并且提供了两种创建动态代理的方式:JDK自带的针对接口的动态代理和使用CGLib动态创建子类的方式创建动态代理。
  • 2.编译期织入
  • 使用特殊的编译器在编译期将切面织入目标类,这种比较少见,因为需要特殊的编译器的支持。
  • 3.类加载期织入
  • 通过字节码编辑技术在类加载期将切面织入目标类中,这是本篇介绍的重点。它的核心思想是:在目标类的class文件被JVM加载前,通过自定义类加载器或者类文件转换器将横切逻辑织入到目标类的class文件中,然后将修改后class文件交给JVM加载。这种织入方式可以简称为LTW(LoadTimeWeaving)。

1.2 JDK实现LTW的原理

可以使用JKD的代理功能让代理器访问到JVM的底层组件,借此向JVM注册类文件转换器,在类加载时对类文件的字节码进行转换。具体而言,java.lang.instrument包下定义了ClassFileTransformer接口,该接口的作用如下面的注释所描述

* An agent provides an implementation of this interface in order* to transform class files.* The transformation occurs before the class is defined by the JVM.

可以通过实现该接口,并重写如下抽象方法自定义类文件转换规则

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

classfileBuffer是原始类文件对应的字节码数组,返回的byte[]为转化后的字节码数组,如果返回null,则表示不进行字节码处理。

而java.lang.instrument包下的Instrumentation接口则可以将我们自定义的ClassTransFormer向JVM内部的组件进行注册

addTransformer(ClassFileTransformer transformer);

在实际使用中,可以通过JVM的-javaagent代理参数在启动时获取JVM内部组件的引用,将ClassFileTransformer实例注册到JVM中,JVM在加载Class文件时,会先调用这个ClassTransformer的transform()方法对Class文件的字节码进行转换,比如织入切面中定义的横切逻辑,实现AOP功能。整个过程可以入下所示

1.3 如何在Spring中实现LTW

Spring中默认通过运行期生成动态代理的方式实现切面的织入,实现AOP功能,但是Spring也可以使用LTW技术来实现AOP,并且提供了细粒度的控制,支持在单个ClassLoader范围内实施类文件转换。

Spring中的org.springframework.instrument.classloading.LoadTimeWeaver接口定义了为类加载器添加ClassFileTransfomer的抽象

* Defines the contract for adding one or more* {@link ClassFileTransformer ClassFileTransformers} to a {@link ClassLoader}.*public interface LoadTimeWeaver {

Spring的LTW支持AspectJ定义的切面,既可以是直接使用AspectJ语法定义的切面,也可以是使用@AspectJ注解,通过java类定义的切面。Spring LTW通过读取classpath下META-INF/aop.xml文件,获取切面类和要被切面织入的目标类的相关信息,通过LoadTimeWeaver在ClassLoader加载类文件时将切面织入目标类中,其工作原理如下所示

Spring中可以通过LoadTimeWeaver将Spring提供的ClassFileTransformer注册到ClassLoader中。在类加载期,注册的ClassFileTransformer读取类路径下META-INF/aop.xml文件中定义的切面类和目标类信息,在目标类的class文件真正被VM加载前织入切面信息,生成新的Class文件字节码,然后交给VM加载。因而之后创建的目标类的实例,就已经实现了AOP功能。

2. Springboot中使用LTW实现AOP的例子

实现一个简单的AOP需求,在方法调用前后打印出开始和结束的日志信息。

  • 相关的maven依赖和插件
org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-test testorg.apache.maven.plugins maven-surefire-plugin   -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" -javaagent:"${settings.localRepository}/org/springframework/spring-instrument/${spring.version}/spring-instrument-${spring.version}.jar"   org.springframework.boot spring-boot-maven-plugin   ${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar   ${settings.localRepository}/org/springframework/spring-instrument/${spring.version}/spring-instrument-${spring.version}.jar  

这里通过maven插件的方式为JVM设置代理,通过-javaagent参数指定织入器类包的路径,这样就可以在类加载期将切面织入,更多关于javaagent的知识可以参考javaagent

  • 织入目标类
/** * @author: takumiCX * @create: 2018-12-19 **/@Componentpublic class LtwBean { public void test(){ System.out.println("process......."); }}

只有一个test()方法,通过@Componet注解向容器注册。

  • 切面类
/** * @author: takumiCX * @create: 2018-12-19 **/@Aspectpublic class LogMethodInvokeAspect { @Pointcut("execution(public * com.takumiCX.ltw.*.*(..))") public void pointCut(){ } @Around("pointCut()") public void advise(ProceedingJoinPoint pjp) throws Throwable { Signature signature = pjp.getSignature(); System.out.println(signature+" start..... "); pjp.proceed(); System.out.println(signature+" end......"); }}

@Aspect注解表示这是一个切面类

  • 配置类
/** * @author: takumiCX * @create: 2018-12-19 **/@Configuration@ComponentScan("com.takumiCX.ltw")@EnableLoadTimeWeaving(aspectjWeaving=AUTODETECT)public class CustomLtwConfig{}

通过@@EnableLoadTimeWeaving开启LTW功能,可以通过属性aspectjWeaving指定LTW的开启策略

  1. ENABLED
  2. 开启LTW
  3. DISABLED
  4. 不开启LTW
  5. AUTODETECT
  6. 如果类路径下能读取到META-INF/aop.xml文件,则开启LTW,否则关闭
  • 在META-INF文件夹下编写aop.xml文件

aop.xml文件内容

<?xml version="1.0" encoding="UTF-8"?>  

这样我们的Spring容器就能加载该文件读取到描述目标类和切面类的相关信息,容器在加载目标类的class文件到jvm之前,会将切面类中定义的增强逻辑织入到class文件中,真正加载到jvm中的是织入切面后的class文件,因而通过该class文件创建出的目标类的实例,不需要经过动态代理就能实现AOP相关功能。

  • 测试类
/** * @author: takumiCX * @create: 2018-12-20 **/@RunWith(SpringRunner.class)@SpringBootTest(classes ={CustomLtwConfig.class})public class LTWTest { @Autowired private LtwBean ltwBean; @Test public void testLTW() throws InterruptedException { ltwBean.test(); }}

最后的结果如下

方法调用前后分别记录的开始和结束的日志信息,说明我们的切面成功的织入到了目标类。但是这里可能有一个疑问,这真的是LTW(Load TimeWeaving)通过在类加载期织入切面起到的作用吗?有没有可能是LTW没起作用,是Spring AOP默认通过运行期生成动态代理的方式实现的AOP。

我们的LogMethodInvokeAspect切面类上并没有加@Component注解向容器注册,并且配置类CustomLtwConfig上也没有加@EnableAspectJAutoProxy注解开启Aspectj的运行时动态代理,所以这里基于动态代理的AOP并不会生效。

为了验证我们的想法,将aop.xml文件删除

重新运行测试代码

AOP没起到作用,说明刚才的AOP功能确实是通过LTW技术实现的。

当我们给切面类加上@Component注解,给配置类加上@EnableAspectJAutoProxy

/** * @author: takumiCX * @create: 2018-12-19 **/@Aspect@Componentpublic class LogMethodInvokeAspect {/** * @author: takumiCX * @create: 2018-12-19 **/@Configuration@ComponentScan("com.takumiCX.ltw")@EnableAspectJAutoProxy@EnableLoadTimeWeaving(aspectjWeaving=AUTODETECT)public class CustomLtwConfig{}

再次运行测试类时,发现AOP又生效了,这时候类路径下并没有aop.xml,所以这时候AOP是Spring在运行期通过动态代理的方式实现的。

uni中动态加载class_SpringBoot中使用LoadTimeWeaving技术实现AOP功能相关推荐

  1. UGUI中动态加载图集中的图片(转)

    前言 图集打包的好处都有啥?不管你是新手还是大神,要是项目中ui图片很多,特别是小图很多,需要运行时动态加载或切换Image中的sprite的时候,基本上都会选择打包成图集这种解决方案. 图集的好处有 ...

  2. 在.Net framework中动态加载Assembly的loadFromRemoteSources配置

    简介 在插件类型的应用开发中,我们可能会在程序中动态加载一个assembly文件,创建其中的类对象并使用. 这时,就涉及到了CAS(code access security)和信任沙盒. 一般,我们的 ...

  3. 在VC中动态加载ODBC的方法

    在VC中动态加载ODBC的方法     在使用VC.VB.Delphi等高级语言编写数据库应用程序时,往往需要用户自己在控制面板中配置ODBC数据源.对于一般用户而言,配置ODBC数据源可能是一件比较 ...

  4. Node.js项目中动态加载环境变量配置

    NODE_MODULES:项目中动态加载环境变量配置 开始 在平时的 Node.js 项目开发中,我们需要在项目中添加各种各样的配置:服务端口.服务地址.图片上传.数据库.Redis 等等. 通常情况 ...

  5. bpl文件java,在LoadLibrary中动态加载BPL失败

    我想在Delphi 10 Seattle(Update 1)或Delphi 10.1 Berlin项目(Enterprise版本)中动态加载BPL模块 . 但LoadPackage函数失败并显示消息( ...

  6. Java中动态加载字节码的方法 (持续补充)

    文章目录 Java中动态加载字节码的方法 1.利用 URLClassLoader 加载远程class文件 2.利用 ClassLoader#defineClass 直接加载字节码 2.1 类加载 - ...

  7. python requests 动态加载_Python获取网页中动态加载的数据

    Python获取网页中动态加载的数据 0.XHR 是什么? XHR是 XMLHttpRequest 对象.既Ajax功能实现所依赖的对象,在JQuery中的Ajax是对 XHR的封装. 1.查看异步加 ...

  8. Android中WebView加载sdcard中的html时提示:ERR_FILE_NOT_FOUND和ERR_ACCESS_DENIED

    场景 Android中WebView加载sdcard中的html显示: Android中WebView加载sdcard中的html显示_BADAO_LIUMANG_QIZHI的博客-CSDN博客 在实 ...

  9. C#中动态加载和卸载DLL

    在C++中加载和卸载DLL是一件很容易的事,LoadLibrary和FreeLibrary让你能够轻易的在程序中加载DLL,然后在任何地方卸载.在C#中我们也能使用Assembly.LoadFile实 ...

最新文章

  1. zsh 每次打开Terminal都需要source bash_profile问题
  2. 在ubuntu上使用gdb-multiarch 跨平台调试
  3. 拼多多高速冲刺8亿用户新时代 黄峥卸任董事长 探索行业未来十年发展
  4. Markdown编辑器“MarkdownPad”下载安装(图)
  5. [linux]unixODBC的安装配置说明
  6. 设计模式适配器模式_适配器设计模式示例
  7. Mysql存储过程查询结果赋值到变量
  8. [SpriteKit] 系统框架中Cocos2d-x制作小游戏ZombieConga
  9. ES7新特性_介绍--JavaScript_ECMAScript_ES6-ES11新特性工作笔记047
  10. Windows访问Linux下的共享目录的配置方法(user安全级别)
  11. Chromium引擎控件DotNetBrowser V1.14发布 | 附下载
  12. linux [1045] Access denied for user 'root'@'localhost' (using password: YES)
  13. Enolsoft PDF Converter with OCR激活版
  14. DTW算法Python实现
  15. DM8 Out of space,错误码 code = -523问题解决
  16. 三菱PLC控制步进电机
  17. SVN和Git优劣对比
  18. 一米村长讲故事机器人_主持人李锐推出“村长讲故事”APP,已入驻喜马拉雅
  19. 常用搜索引擎的搜索方法有哪些?各有什么特点?
  20. 2021“智荟杯”浦发百度高校极客挑战赛——比赛总结

热门文章

  1. java null 转空_java 对象属性为 null 值转为 空串
  2. 2021亚太内容分发大会 阿里云荣获三项大奖
  3. 【国内首家!】阿里云专有云通过商用密码应用安全性评估
  4. Code Review 是苦涩但有意思的修行
  5. 手游引擎之战再现新挑战者,OGEngine来了
  6. 独立开发者分享:游戏本地化的6个建议
  7. MySQL复习资料(四)——MySQL-聚合函数
  8. IT界含金量高的认证考试
  9. 使用expdp导出时评估所需存储容量大小
  10. LINUX静态网络配置