Spring 高级 AOP 实现之 ajc 编译器
一、代码
package com.itheima.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Aspect // ⬅️注意此切面并未被 Spring 管理 public class MyAspect { private static final Logger log = LoggerFactory.getLogger(MyAspect.class); @Before("execution(* com.itheima.service.MyService.foo())") public void before() { log.debug("before()"); } }
package com.itheima.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @Service public class MyService { private static final Logger log = LoggerFactory.getLogger(MyService.class); public static void foo() { log.debug("foo()"); } }
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itheima</groupId> <artifactId>aspectj_01</artifactId> <version>0.0.1-SNAPSHOT</version> <name>aspectj_01</name> <description>aspectj_01</description> <properties> <java.version>8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.14.0</version> <configuration> <complianceLevel>1.8</complianceLevel> <source>8</source> <target>8</target> <showWeaveInfo>true</showWeaveInfo> <verbose>true</verbose> <Xlint>ignore</Xlint> <encoding>UTF-8</encoding> </configuration> <executions> <execution> <goals> <!-- use this goal to weave all your main classes --> <goal>compile</goal> <!-- use this goal to weave all your test classes --> <goal>test-compile</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
package com.itheima; import com.itheima.service.MyService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; /* 注意几点 1. 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16 2. 一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器 */ @SpringBootApplication public class A09 { private static final Logger log = LoggerFactory.getLogger(A09.class); public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A09.class, args); MyService service = context.getBean(MyService.class); log.debug("service class: {}", service.getClass()); service.foo(); context.close(); // new MyService().foo(); /* 学到了什么 1. aop 的原理并非代理一种, 编译器也能玩出花样 */ } }
二、测试
发现确实执行了切面类里面的方法,现了对目标方法的增强处理,当时打印目标方法对象时,并不是代理类,说明此时AOP并不是用代理来实现的,是用的Aspect的编译器进行增强。
查看源码,发现是改写了Class文件实现对目标方法的增强
既然Class被改写了,说明该类没有被Spring容器管理
测试 自己直接创建对象,发现目标方法还是被增强了
收获💡
- 编译器也能修改 class 实现增强
- 编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强
三、补充:要实现以上效果,要在POM.xml里面加上下面的插件
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.14.0</version> <configuration> <complianceLevel>1.8</complianceLevel> <source>8</source> <target>8</target> <showWeaveInfo>true</showWeaveInfo> <verbose>true</verbose> <Xlint>ignore</Xlint> <encoding>UTF-8</encoding> </configuration> <executions> <execution> <goals> <!-- use this goal to weave all your main classes --> <goal>compile</goal> <!-- use this goal to weave all your test classes --> <goal>test-compile</goal> </goals> </execution> </executions> </plugin>