SpringBoot中使用LoadTimeWeaving技术实现AOP功能

1. 关于LoadTimeWeaving

1.1 LTW与不一样的切面织入时机java

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

  • 1.运行期织入apache

  • 这是最多见的,好比在运行期经过为目标类生成动态代理的方式实现AOP就属于运行期织入,这也是Spring AOP中的默认实现,而且提供了两种建立动态代理的方式:JDK自带的针对接口的动态代理和使用CGLib动态建立子类的方式建立动态代理。编程

  • 2.编译期织入数组

  • 使用特殊的编译器在编译期将切面织入目标类,这种比较少见,由于须要特殊的编译器的支持。bash

  • 3.类加载期织入eclipse

  • 经过字节码编辑技术在类加载期将切面织入目标类中,这是本篇介绍的重点。它的核心思想是:在目标类的class文件被JVM加载前,经过自定义类加载器或者类文件转换器将横切逻辑织入到目标类的class文件中,而后将修改后class文件交给JVM加载。这种织入方式能够简称为LTW(LoadTimeWeaving)。jvm

1.2 JDK实现LTW的原理maven

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

* 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功能。整个过程能够入下所示

SpringBoot中使用LoadTimeWeaving技术实现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加载类文件时将切面织入目标类中,其工做原理以下所示

SpringBoot中使用LoadTimeWeaving技术实现AOP功能


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

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

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

  • 相关的maven依赖和插件

<dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 <scope>test</scope>
 </dependency>
</dependencies>
<build>
 <plugins>
 <plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-surefire-plugin</artifactId>
 <configuration>
 <argLine>
 -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"
 <!-- -Dspring.profiles.active=test-->
 </argLine>
 </configuration>
 </plugin>
 <plugin>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-maven-plugin</artifactId>
 <configuration>
 <agent>
 ${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar
 </agent>
 <agent>
 ${settings.localRepository}/org/springframework/spring-instrument/${spring.version}/spring-instrument-${spring.version}.jar
 </agent>
 </configuration>
 </plugin>
 </plugins>
</build>复制代码

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

  • 织入目标类

/**
 * @author: takumiCX
 * @create: 2018-12-19
 **/
@Component
public class LtwBean {
 public void test(){
 System.out.println("process.......");
 }
}复制代码

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

  • 切面类

/**
 * @author: takumiCX
 * @create: 2018-12-19
 **/
@Aspect
public 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文件

SpringBoot中使用LoadTimeWeaving技术实现AOP功能

aop.xml文件内容

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
 <!--要织入切面的目标类-->
 <weaver>
 <include within="com.takumiCX.ltw..*" />
 </weaver>
 <!--切面类-->
 <aspects>
 <aspect name="com.takumiCX.ltw.aspect.LogMethodInvokeAspect" />
 </aspects>
</aspectj>复制代码

这样咱们的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();
 }
}复制代码

最后的结果以下

SpringBoot中使用LoadTimeWeaving技术实现AOP功能

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

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

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

SpringBoot中使用LoadTimeWeaving技术实现AOP功能

从新运行测试代码

SpringBoot中使用LoadTimeWeaving技术实现AOP功能

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

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

/**
 * @author: takumiCX
 * @create: 2018-12-19
 **/
@Aspect
@Component
public 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在运行期经过动态代理的方式实现的。

SpringBoot中使用LoadTimeWeaving技术实现AOP功能

相关文章
相关标签/搜索