aop之agent增强
除了使用 AspectJ AOP 和 Spring AOP 提供的基于动态代理和字节码增强的方式,还可以使用 JVM 级别的 Java Agent 技术进行 AOP 增强。
Java Agent 是一个可以以动态的方式向 JVM 注入字节码的程序,它可以修改正在运行的应用程序的字节码,从而实现对应用程序的增强、监控及调试等功能。通过 Java Agent 技术进行 AOP 增强,可以避免修改源代码的情况,并提供了对所有类和所有方法的增强能力,具有更高的灵活性和可靠性。
代码
A10
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. 运行时需要在 VM options 里加入 -javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
把其中 C:/Users/manyh/.m2/repository 改为你自己 maven 仓库起始地址
*/
@SpringBootApplication
public class A10 {
private static final Logger log = LoggerFactory.getLogger(A10.class);
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A10.class, args);
MyService service = context.getBean(MyService.class);
// ⬇️MyService 并非代理, 但 foo 方法也被增强了, 做增强的 java agent, 在加载类时, 修改了 class 字节码
log.debug("service class: {}", service.getClass());
service.foo();
context.close();
/*
学到了什么
1. aop 的原理并非代理一种, agent 也能, 只要字节码变了, 行为就变了
*/
}
}
MyAspect
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.*())")
public void before() {
log.debug("before()");
}
}
MyService
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);
final public void foo() {
log.debug("foo()");
this.bar(); //如果一个方法里面调用了内部方法的调用,代理类不会做增强
}
public void bar() {
log.debug("bar()");
}
}
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
当然这个增强也不依赖于spring,但是我们运行时需要注意下面的问题:
注意几点:
- 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
- 运行时需要在 VM options 里加入 -javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
把其中 C:/Users/manyh/.m2/repository 改为你自己 maven 仓库起始地址
运行一下其class文件可以看一下,是没有更改代码的,增强的时机是在运行时动态修改字节码
(增强生效,出现一些多出来的下面红框里的东西说明成功了):
在这里说一下,要是一个方法里调用了另一个该类的方法代理是不会增强的,因为其实是用this调用的,不会走代理:
想看修改的字节码也可以同过一些工具查看,如arthas工具、
下载好工具后,在arthas-bin目录下调用如下命令启动
选择我们要连接的项目(需要在运行中的下项目才能看到),这里我们连接第三个
里面有很多的功能我们 help一下看看,我们要的就是反编译这个功能
jad 跟上需要反编译的包名即可
这里就很清除的可以看出为什么会增强了
总的来说,Java Agent 可以替代更灵活地增强任何已加载的类,适用于分布式 AOP 等更加复杂的应用场景,但需要更多的技术和安全性控制。
在spring中常用的还是代理的方法实现aop
收获💡
- 类加载时可以通过 agent 修改 class 实现增强