文章目录
动态代理
JDK动态代理
基于接口实现,为实现类生成代理对象
1、举例
- 接口
public interface UserRepository {
int delete(Integer id);
}
- 实现类
public class UserRepositoryImpl implements UserRepository {
@Override
public int delete(Integer id) {
System.out.println("删除id = "+ id +"数据");
return 1;
}
}
- InvocationHandler:调用处理器
public class MyInvocationHandler implements InvocationHandler {
// 代理类
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
// 重写InvocationHandler接口的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1.前置增强
System.out.println("前置增强");
// 2.代理对象方法执行
Object result = method.invoke(target, args);
// 3.后置增强
System.out.println("后置增强");
return result;
}
}
- 测试
public static void main(String[] args) {
// 1.目标类
UserRepository target = new UserRepositoryImpl();
// 2.调用处理器
MyInvocationHandler h = new MyInvocationHandler(target);
// 3.生成Jdk动态代理类
UserRepository proxy = (UserRepository)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
h);
// 4.动态代理类调用目标类的方法
int result = proxy.delete(1);
}
代理对象的生成
1、生成动态代理对象的方法:Proxy.newProxyInstance简略如下
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h){
final Class<?>[] intfs = interfaces.clone();
// 步骤一:生成接口对应的实现类的代理类的字节码.class
Class<?> cl = getProxyClass0(loader, intfs);
// 步骤二:生产代理对象的构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//步骤三:生成代理类的实例并把InvocationHandlerImpl的实例传给构造方法
return cons.newInstance(new Object[]{h});
}
2、解析步骤一:生成接口对应的实现类的代理类的字节码.class
WeakCache#get方法简略如下
public V get(K key, P parameter) {
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
// 函数式接口Supplier.get,本质是WeakCache内部类Factory.get,返回代理对象字节码.class
V value = supplier.get();
if (value != null) {
return value;
}
}
}
3、Factory.get方法简略如下
@Override
public synchronized V get() {
// 通过valueFactory.apply(key, parameter)获取代理类字节码.class
// BiFunction<K, P, V> valueFactory函数式接口.apply,底层是
// Proxy类中内部类ProxyClassFactory.apply方法
V value = Objects.requireNonNull(valueFactory.apply(key, parameter));
return value;
}
4、ProxyClassFactory.apply方法简略如下
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// 1. 为代理类生成名字
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 2.真正生成代理类的字节码文件的地方!!!
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
// 3.使用类加载器将代理类的字节码文件加载到JVM中(这里先不关注)
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
5、真正生成代理类字节码的方法ProxyGenerator.generateProxyClass
- 这里我们也使用这种方式手写个demo,将代理类的字节码.class,反编译生成内容,便于查看学习
public static void main(String[] args) throws Exception {
// 1.生成UserRepositoryImpl目标类的代理对象.class内容
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
"$Proxy1",
UserRepositoryImpl.class.getInterfaces());
// 2.使用流读取.class内容,结果存放的路径
String path = "D:\\spring源码\\spring-framework-5.1.4.RELEASE\\spring5_mjp\\src";
// 3.使用流读取.class内容,并输出
try (FileOutputStream out = new FileOutputStream(new File(path + "$Proxy1.class"))) {
out.write(proxyClassFile);
}
}
6、手写生成的代理类字节码反编译简略结果如下
public final class $Proxy1 extends Proxy implements UserRepository {
private static Method m3;
public $Proxy1(InvocationHandler var1) throws {
super(var1);
}
public final int delete(Integer var1) throws {
return (Integer)super.h.invoke(this, m3, new Object[]{var1});
}
static {
m3 = Class.forName("com.mjp.aop.stopwatch.UserRepository")
.getMethod("delete",Class.forName("java.lang.Integer"));
}
}
1)类
继承了Proxy类、实现了我们自定义的接口UserRepository
所以,Jdk动态代理是基于实现接口的方式
2)静态代理块中,反射获取delete方法信息
通过反射,获取到目标类的方法Method m3信息
- 方法声明处为:UserRepository,是在此接口中声明的
- 其方法名为:delete
- 其参数类型为:Integer
动态代理执行流程
public static void main(String[] args) {
// 1.目标类
UserRepository target = new UserRepositoryImpl();
// 2.调用处理器
MyInvocationHandler h = new MyInvocationHandler(target);
// 步骤三.生成Jdk动态代理类
UserRepository proxy = (UserRepository)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
h);
// 步骤四.动态代理类调用目标类的方法
int result = proxy.delete(1);
}
直接看步骤四:proxy.delete(1)
1、执行proxy.delete(1),结合上文生成的代理对象.class文件反编译结果$Proxy1看
public final int delete(Integer var1) throws {
return (Integer)super.h.invoke(this, m3, new Object[]{var1});
}
- (Integer):即delete方法的结果返回值类型
- super即父类Proxy
- super.h即父类Proxy中的InvocationHandler,因为步骤三种,在生成代理类时,将我们自定义的MyInvocationHandler作为方法入参h,传递过去了。所以这里的super.h,即我们自定义的MyInvocationHandler
- super.h.invoke,即MyInvocationHandler.invoke
2、myInvocationHandler.invoke(this, m3, new Object[]{var1})
- this:即代理对象$Proxy1
- m3:即反射生成的目标对象的delete方法
- new Object[]{var1}:即步骤四中proxy.delete(1)的方法入参1
进入我们MyInvocationHandler中查看重写的invoke方法详情
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1.前置增强
System.out.println("前置增强");
// 2.代理对象方法执行
Object result = method.invoke(target, args);
// 3.后置增强
System.out.println("后置增强");
return result;
}
-
先执行前置增强
-
在执行method.invoke(target, args)
底层就是反射执行target目标对象的method方法
- method:即上述传2中递过来的m3,即delete方法
- target:是通过MyInvocationHandler构造方法传递过来的目标对象
- args:是上述2中传递过来的new Object[]{var1},方法参数
-
最后执行后置增强
简化代码
// 步骤二.调用处理器
MyInvocationHandler h = new MyInvocationHandler(target);
// 步骤三.生成Jdk动态代理类
UserRepository proxy = (UserRepository)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
h);
1、匿名内部类
很明显,我们自定义的实现了了InvocationHandler接口的MyInvocationHandler类,可以通过匿名内部类的方式创建,这样就无需单独创建一个MyInvocationHandler类
- 步骤二、三的目的就是生成代理对象,故将二者封装在JdkProxyUtil#getProxy中
public class JdkProxyUtil {
public static UserRepository getProxy(UserRepository target){
// 步骤二:自定义处理器
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args){
System.out.println("前置增强");
Object result = null;
try {
result = method.invoke(target, args);
} catch (Exception e) {
// 异常处理
}
System.out.println("后置增强");
return result;
}
};
// 步骤三:生成目标类的代理对象
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
h);
return (UserRepository) proxy;
}
}
2、Lambda表达式
public class JdkProxyUtil {
public static UserRepository getProxy(UserRepository target){
InvocationHandler h = (proxy, method, args) -> {
System.out.println("前置增强");
Object result = null;
try {
result = method.invoke(target, args);
} catch (Exception e) {
// 异常处理
}
System.out.println("后置增强");
return result;
};
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
h);
return (UserRepository) proxy;
}
}
静态代理
了解了Jdk动态代理,我们再看下静态代理
- 静态代理类
public class StaticProxy implements UserRepository{
private UserRepository userRepository;
public StaticProxy(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public int delete(Integer id) {
// 1.前置增强
System.out.println("前置增强");
// 2.执行目标对象方法
int result = userRepository.delete(id);
// 3.后置增强
System.out.println("后置增强");
return result;
}
}
- 测试
UserRepository target = new UserRepositoryImpl();
StaticProxy staticProxy = new StaticProxy(target);
int delete = staticProxy.delete(1);
- 分析
- 顾名思义,静态代理是静态的即提前写死的代理类StaticProxy,不是在代码执行期间动态生成的$Proxy1代理类
- 耦合:静态代理类持有目标类对象
- 扩展性差:如果接口新增方法,则除了目标对象需要重写外,静态代理对象也需要重写方法。
Cglib动态代理
在jdk1.7之前,jdk动态代理(底层基于反射)性能不及cglib动态代理(底层基于Asm框架技术)。后续反射性能优化了,现在就是基于接口实现就用Jdk动态代理,基于继承就用cglib动态代理
1、举例
- 目标类(父类)
public class UserDao {
public int delete(Integer id) {
System.out.println("删除id = "+ id+ " 数据");
return 1;
}
}
- MyInterceptor
public class MyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 1.前置增强
System.out.println("前置增强");
// 2.代理对象(子类o),调用其父类(目标对象UserDao)的目标方法(delete)
Object result = methodProxy.invokeSuper(o, objects);
// 3.后置增强
System.out.println("后置增强");
return result;
}
}
- 测试
public static void main(String[] args) {
// 1.创建调用的对象
Enhancer enhancer = new Enhancer();
// 2.设置父类(目标对象)
enhancer.setSuperclass(UserDao.class);
// 3.设置回调对象-自定义拦截器
enhancer.setCallback(new MyInterceptor());
// 4.生成目标对象(父类)的cglib代理对象(子类)
UserDao proxy = (UserDao) enhancer.create();
// 5.通过cglib代理对象(子类),调用目标类(父类)的目标方法
int result = proxy.delete(1);
}
代理类的生成
0、new Enhance(),先通过AbstractClassGenerator#generate创建Enhance的属性EnhancerKey的代理对象
public interface EnhancerKey {
public Object newInstance(String type,
String[] interfaces,
WeakCacheKey<CallbackFilter> filter,
Type[] callbackTypes,
boolean useFactory,
boolean interceptDuringConstruction,
Long serialVersionUID);
}
因为后续要使用其内部的newInstance对象
1、生成cglib动态代理对象的入口:enhancer.create()
enhancer.create() -->> createHelper() -->> super.create(key)
– >> data.get -->> generatedClass.generate -->> loader.apply -->> AbstractClassGenerator#gen.generate
- 获取类加载器classLoader
- 设置代理类的名称className
- 并生成代理类的字节码byte b[]
最后利用反射,创建代理类
Class gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain, contextClass)
2、手动将动态代理创建的.class文件存储到本地,便于查看
- 生成动态代理.class的方式
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\spring源码\\spring-framework-5.1.4.RELEASE\\spring5_mjp\\src");
// 1.创建调用的对象
Enhancer enhancer = new Enhancer();
// 步骤二.设置父类(目标对象)
enhancer.setSuperclass(UserDao.class);
// 步骤三.设置回调对象-自定义拦截器
enhancer.setCallback(new MyInterceptor());
// 4.生成目标对象(父类)的cglib代理对象(子类)
UserDao proxy = (UserDao) enhancer.create();
// 5.通过cglib代理对象(子类),调用目标类(父类)的目标方法
int result = proxy.delete(1);
动态代理执行流程
1、测试
UserDao proxy = (UserDao) enhancer.create();
int result = proxy.delete(1);
2、proxy.delete(1)
结合生成的cglib字节码文件,查看此方法
- cglib动态代理类(目标类父类的子类)delete方法概述如下
public class UserDao$$EnhancerByCGLIB$$77046e5 extends UserDao implements Factory {
// 2.callback
private MethodInterceptor CGLIB$CALLBACK_0;
// 3.1 目标方法即要拦截的方法即要增强的方法
private static final Method CGLIB$delete$0$Method;
// 3.2 表示要触发父类(目标类)的方法对象
private static final MethodProxy CGLIB$delete$0$Proxy;
//一、目标方法
public final int delete(Integer var1) {
// 1).获取callBack
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
// 2).如果设置了callBack,则调用callback的intercept方法
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$delete$0$Method, new Object[]{var1}, CGLIB$delete$0$Proxy);
return var2 == null ? 0 : ((Number)var2).intValue();
} else {
// 3).如果未设置callback,则直接调用cglib动态代理类(子类)的父类(目标类UserDao)的目标delete方法
return super.delete(var1);
}
}
// 二、设置callback
public void setCallback(int var1, Callback var2) {
switch (var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
// 三、静态代码块
static void CGLIB$STATICHOOK1() {
//3.1 目标方法Method
CGLIB$delete$0$Method = ReflectUtils.findMethods(new String[]{"delete", "(Ljava/lang/Integer;)I"}, (var1 = Class.forName("com.mjp.aop.cglib.UserDao")).getDeclaredMethods())[0];
// 3.2.MethodProxy
CGLIB$delete$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Integer;)I", "delete", "CGLIB$delete$0");
}
}
1)this.CGLIB$CALLBACK_0
- 这个this.CGLIB$CALLBACK_0参数是个全局变量,在setCallback方法中被赋值了
本质是在步骤三: enhancer.setCallback(new MyInterceptor())完成了Callback内容的赋值
自定义MyInterceptor类,实现了Interceptor接口,Interceptor接口继承了CallBack接口
3)如果没有步骤三,即代码中未设置CallBack,则会直接调用父类目标方法,就相当于没有实现增强
2)如果有步骤三,即代码中设置了CallBack,即设置了Interceptor拦截器实现类,则会
var10000.intercept(this, CGLIB$delete$0$Method, new Object[]{var1}, CGLIB$delete$0$Proxy)
- var10000,即自定义myInterceptor
- var10000.intercept即myInterceptor.intercept
- this:即cglib动态代理类(子类)
- CGLIB$delete 0 0 0Method:在静态代码块CGLIB$STATICHOOK1()3.1中完成了赋值,本质是UserDao目标类的#delete方法,方法入参为Integrate类型,表示要拦截的方法即要增强的方法
- new Object[]{var1}:目标方法的入参值1
- CGLIB$delete 0 0 0Proxy:表示要触发父类(目标类)的方法对象,在静态代理块3.2中完成了赋值
public class MyInterceptor implements MethodInterceptor {
/**
* 拦截器
*
* @param o :cglib动态代理类UserDao$$EnhancerByCGLIB$$77046e5(子类)
* @param method :目标方法即要拦截的方法UserDao#delete
* @param objects :目标方法参数:delete(1),即1
* @param methodProxy :要触发父类(目标类)的方法对象
* @return :目标方法即要拦截的方法delete方法的执行结果
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 1).前置增强
System.out.println("前置增强");
// 2).目标方法
Object result = methodProxy.invokeSuper(o, objects);
// 3).后置增强
System.out.println("后置增强");
return result;
}
}
3、myInterceptor.intercept
- 前置增强
2)methodProxy.invokeSuper(o, objects)
–>> MethodProxy#invokeSuper
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
}
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
}
–>>fci.f2.invoke
–>> 抽象类FastClass.invoke
本质会调用为了便于查看代理内容,而生成的UserDao F a s t C l a s s B y C G L I B FastClassByCGLIB FastClassByCGLIB1cb5c806#invoke,此类实现了FastClass重写了invoke方法
public class UserDao$$FastClassByCGLIB$$1cb5c806 extends FastClass {
public Object invoke(int var1, Object var2, Object[] var3) {
UserDao var10000 = (UserDao)var2;
return new Integer(var10000.delete((Integer)var3[0]));
}
}
所以本质还是调用的UserDao#delete(1)返回Integer
3)后置增强
bytebuddy
1、作用
正常情况下,.java通过编译生成.class后才能运行,但是bytebuddy可以在Java程序运行期间生成或修改字节码
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.15</version>
</dependency>
常用api
生成一个类
// 1.生成的字节码还未加载到jvm
DynamicType.Unloaded<Demo1> unloaded = new ByteBuddy()
// 为了性能不校验自定义的name是否合法
.with(TypeValidation.of(false))
// 指定父类
.subclass(Demo1.class)
// 指定字节码的包.名
.name("com.mjp.Demo1Sub")
.make();
// 2.获取生成的字节码内容
byte[] bytes = unloaded.getBytes();
// 3.将字节码写入指定文件
String path = Demo1.class.getClassLoader().getResource("").getPath();
unloaded.saveIn(new File(path));
// 4.将生成的字节码注入某个jar
unloaded.inject(new File("jar的绝对路径.jar"));
对实例方法进行插桩
stub:对字节码进行修改增强
- 父类
public class Demo1 {
public String func() {
return "hello";
}
}
- 增强
// 1.生成的字节码还未加载到jvm
DynamicType.Unloaded<Demo1> unloaded = new ByteBuddy()
.with(TypeValidation.of(false))
.subclass(Demo1.class)
.name("com.mjp.Demo1Sub")
// 拦截Demo1的func方法
.method(ElementMatchers.named("func"))
// 拦截到相应方法后,如何处理(这里使用自带的增强器)
.intercept(FixedValue.value("拦截增强"))
.make();
// 2.将生成的字节码加载到jvm(loaded和unloaded都是继承DynamicType接口)
DynamicType.Loaded<Demo1> loaded = unloaded.load(getClass().getClassLoader());
loaded.saveIn(new File(Demo1.class.getClassLoader().getResource("").getPath()));
// 3.获取加载到jvm后的class对象
Class<? extends Demo1> loadedLoaded = loaded.getLoaded();
// 4.实例化对象(这里可以使用Demo1接收是因为其为父类)
Demo1 instance = loadedLoaded.newInstance();
String result = instance.func();
System.out.println(result);
- 子类.class文件
public class Demo1Sub extends Demo1 {
public String func() {
return "拦截增强";
}
public Demo1Sub() {
}
}
实例方法进行插桩的扩展
1)匹配方法
- 父类
public class Demo1 {
public String func() {
return "hello";
}
public Integer func(Integer id) {
return 1;
}
}
- 增强
DynamicType.Unloaded<Demo1> unloaded = new ByteBuddy()
.with(TypeValidation.of(false))
.subclass(Demo1.class)
.name("Demo1Sub")
.method(
// 拦截Demo1的func方法
ElementMatchers.named("func")
// 并且
.and(
// 方法的返回值是String类型
returns(String.class)
// 或 方法的返回值是Integer类型
.or(returns(Integer.class))
)
)
.intercept(FixedValue.nullValue())
.make();
DynamicType.Loaded<Demo1> loaded = unloaded.load(getClass().getClassLoader());
loaded.saveIn(new File(Demo1.class.getClassLoader().getResource("").getPath()));
Class<? extends Demo1> aClass = loaded.getLoaded();
Demo1 instance = aClass.newInstance();
System.out.println(instance.func());
System.out.println(instance.func(1));
- 子类
public class Demo1Sub extends Demo1 {
public String func() {
return null;
}
public Integer func(Integer var1) {
return null;
}
public Demo1Sub() {
}
}
2)拦截多个方法
.method()可以多次使用,达到拦截多个方法的作用
三种增强方式
1)subClass
2)rebase
变基
- 不再像subClass基于继承
- 原方法会被重命名为xxOriginal(),拦截后的方法名为xx
(在idea-view-show bytecode下查看)
- 用于静态方法(静态方法不可被继承,所以不能使用subclass)
- 不指定命名地址,则生成位置与目标类在同路径下
3)redifine
-
变基,原方法不再保留,只有拦截后的方法名称xx
-
不指定命名地址,则生成位置与目标类在同路径下
-
类
public class Demo1 {
public String func() {
return "hello";
}
public Integer func(Integer id) {
return 1;
}
}
- 增强
DynamicType.Unloaded<Demo1> unloaded = new ByteBuddy()
.with(TypeValidation.of(false))
//变基
.redefine(Demo1.class)
.name("Demo1Sub")
.method(
// 拦截Demo1的func方法
ElementMatchers.named("func")
// 并且
.and(
// 方法的返回值是String类型
returns(String.class)
// 或 方法的返回值是Integer类型
.or(returns(Integer.class))
)
)
.intercept(FixedValue.nullValue())
.make();
unloaded.saveIn(new File(Demo1.class.getClassLoader().getResource("").getPath()));
- 新生成的类
public class Demo1Sub {
public Demo1Sub() {
}
public String func() {
return null;
}
public Integer func(Integer var1) {
return null;
}
}
插入新方法
1)redefine方式
- 类
public class Demo1 {
public String func() {
return "hello";
}
}
- 增强
DynamicType.Unloaded<Demo1> unloaded = new ByteBuddy()
.with(TypeValidation.of(false))
.redefine(Demo1.class)
.name("Demo1Sub")
// 生成新的方法:方法名称、方法返回值类型、方法的修饰符类型
.defineMethod("newFunc", String.class, Modifier.PUBLIC + Modifier.STATIC)
// 新方法的方法入参类型
.withParameter(List.class, "userNameList")
// 拦截生成的新方法,方法的返回值内容
.intercept(FixedValue.value("生成新的方法返回值"))
.make();
unloaded.saveIn(new File(Demo1.class.getClassLoader().getResource("").getPath()));
- 增强类
public class Demo1Sub {
public Demo1Sub() {
}
// 原方法不动
public String func() {
return "hello";
}
// 生成新的方法
public static String newFunc(List userNameList) {
return "生成新的方法返回值";
}
}
2)subClass方式
public class Demo1Sub extends Demo1 {
// 直接生成新的方法
public static String newFunc(List userNameList) {
return "生成新的方法返回值";
}
public Demo1Sub() {
}
}
方法委托
自定义拦截逻辑
- 父类
public class Demo1 {
public String func(Integer id) {
return "hello " + id;
}
}
- 增强
DynamicType.Unloaded<Demo1> unloaded = new ByteBuddy()
.with(TypeValidation.of(false))
.subclass(Demo1.class)
.name("Demo1Sub")
// 要拦截的方法名称
.method(ElementMatchers.named("func"))
// 委托给自定义的MyInterceptor拦截器进行处理
.intercept(MethodDelegation.to(new MyInterceptor()))
.make();
DynamicType.Loaded<Demo1> loaded = unloaded.load(getClass().getClassLoader());
loaded.saveIn(new File(Demo1.class.getClassLoader().getResource("").getPath()));
Class<? extends Demo1> aClass = loaded.getLoaded();
Demo1 instance = aClass.newInstance();
System.out.println(instance.func(1));
- 自定义拦截器
public class MyInterceptor {
/**
* 参考cglib,自定义MethodInterceptor,重写intercept方法中的参数
*
* @RuntimeType :修饰拦截方法
* @param o :代理对象(Demo1Sub),只有拦截实例方法、构造方法时可用
* @param method : 要拦截的方法,只有拦截实例方法、静态方法时可用
* @param objects :要拦截的方法参数,任意方法的拦截都可用
* @param targetObj :目标对象,只有拦截实例方法、构造方法时可用
* @param callable :用于调用目标对象的拦截方法(类似cglib中MethodProxy触发目标类的方法调用的对象)
*/
@RuntimeType
public Object intercept(@This Object o,
@Origin Method method,
@AllArguments Object[] objects,
@Super Object targetObj,
@SuperCall Callable<?> callable) {
System.out.println("o: " + o);//Demo1Sub@12591ac8
System.out.println("method: " + method);//public java.lang.String Demo1.func(java.lang.Integer)
System.out.println("objects: " + Arrays.toString(objects));//[1]
System.out.println("targetObj: " + targetObj);//Demo1Sub@12591ac8
System.out.println("callable: " + callable);//Demo1Sub$auxiliary$WCUkqaLd@5a7fe64f
Object call = null;
try {
System.out.println("只有前置增强");
// 调用目标方法本身
call = callable.call();//hello 1
} catch (Exception e) {
}
return call;
}
}
动态修改入参
- 父类
public class Demo1 {
public Integer func(Integer id) {
return id;
}
}
- 自定义Callable
public interface MyCallable {
public Object call(Object[] args);
}
- 增强
DynamicType.Unloaded<Demo1> unloaded = new ByteBuddy()
.with(TypeValidation.of(false))
.subclass(Demo1.class)
.name("Demo1Sub")
.method(ElementMatchers.named("func"))
.intercept(MethodDelegation
.withDefaultConfiguration()
// 指定参数类型是MyCallable
.withBinders(Morph.Binder.install(MyCallable.class))
.to(new MyInterceptor())
)
.make();
DynamicType.Loaded<Demo1> loaded = unloaded.load(getClass().getClassLoader());
loaded.saveIn(new File(Demo1.class.getClassLoader().getResource("").getPath()));
Class<? extends Demo1> aClass = loaded.getLoaded();
Demo1 instance = aClass.newInstance();
System.out.println(instance.func(1));//101
- 自定义拦截器
public class MyInterceptor {
/**
* 参考cglib,自定义MethodInterceptor,重写intercept方法中的参数
*
* @RuntimeType :修饰拦截方法
* @param o :代理对象(Demo1Sub),只有拦截实例方法、构造方法时可用
* @param method : 要拦截的方法,只有拦截实例方法、静态方法时可用
* @param objects :要拦截的方法参数,任意方法的拦截都可用
* @param targetObj :目标对象,只有拦截实例方法、构造方法时可用
* @param callable :用于调用目标对象的拦截方法(类似cglib中MethodProxy触发目标类的方法调用的对象)自定义可以传参的
*/
@RuntimeType
public Object intercept(@This Object o,
@Origin Method method,
@AllArguments Object[] objects,
@Super Object targetObj,
@Morph MyCallable callable) {
System.out.println("o: " + o);//Demo1Sub@12591ac8
System.out.println("method: " + method);//public java.lang.String Demo1.func(java.lang.Integer)
System.out.println("objects: " + Arrays.toString(objects));//[1]
System.out.println("targetObj: " + targetObj);//Demo1Sub@12591ac8
System.out.println("callable: " + callable);//Demo1Sub$auxiliary$HGW8FpsQ@7dfb0c0f
Object call = null;
try {
System.out.println("只有前置增强");
// 调用目标方法本身之前,将方法的入参进行动态的修改,值+100
if (objects != null && objects.length > 0) {
objects[0]= (Integer)objects[0] + 100;
}
// 调用目标方法本身
call = callable.call(objects);//1 + 100 = 101
} catch (Exception e) {
}
return call;
}
}
对构造方法进行插桩
- 父类
public class Demo1 {
public String func(Integer id) {
return "hello " + id;
}
}
- 增强
DynamicType.Unloaded<Demo1> unloaded = new ByteBuddy()
.with(TypeValidation.of(false))
.subclass(Demo1.class)
.name("Demo1Sub")
// 拦截任意的构造方法
.constructor(ElementMatchers.any())
.intercept(
// 在构造方法执行之后,委托给拦截器进行拦截
SuperMethodCall.INSTANCE.andThen(
MethodDelegation.to(new MyInterceptor2())
)
)
.make();
DynamicType.Loaded<Demo1> loaded = unloaded.load(getClass().getClassLoader());
loaded.saveIn(new File(Demo1.class.getClassLoader().getResource("").getPath()));
Class<? extends Demo1> aClass = loaded.getLoaded();
Demo1 instance = aClass.newInstance();
System.out.println(instance.func(1));
- 拦截
public class MyInterceptor2 {
/**
* @RuntimeType :修饰拦截方法
* @param o :代理对象(Demo1Sub),实例化方法和构造方法的拦截可用
* @return
*/
@RuntimeType
public Object intercept(@This Object o) {
System.out.println("实例化增强");
return null;
}
}
对静态方法进行插桩
- 父类
public class Demo1 {
public static Integer func(Integer id) {
return id;
}
}
- 增强
DynamicType.Unloaded<Demo1> unloaded = new ByteBuddy()
.with(TypeValidation.of(false))
// 这里必须使用rebase
.rebase(Demo1.class)
.name("Demo1Sub")
// 拦截静态方法
.method(ElementMatchers.named("func").and(isStatic()))
.intercept(MethodDelegation.to(new MyInterceptor3()))
.make();
DynamicType.Loaded<Demo1> loaded = unloaded.load(getClass().getClassLoader());
loaded.saveIn(new File(Demo1.class.getClassLoader().getResource("").getPath()));
Class<? extends Demo1> aClass = loaded.getLoaded();
Method method = aClass.getMethod("func", Integer.class);
Object result = method.invoke(null, 1);
System.out.println(result);
- 拦截器
public class MyInterceptor3 {
/**
* 参考cglib,自定义MethodInterceptor,重写intercept方法中的参数
*
* @RuntimeType :修饰拦截方法
* @param method : 要拦截的方法
* @param objects :要拦截的方法参数
* @param callable :用于调用目标对象的拦截方法(类似cglib中MethodProxy触发目标类的方法调用的对象)
* @return
*/
@RuntimeType
public Object intercept(@Origin Class<?> aclass,
@Origin Method method,
@AllArguments Object[] objects,
@SuperCall Callable<?> callable) {
System.out.println("class: " + aclass);//class Demo1Sub
System.out.println("method: " + method);//public java.lang.String Demo1.func(java.lang.Integer)
System.out.println("objects: " + Arrays.toString(objects));//[1]
System.out.println("callable: " + callable);//Demo1Sub$auxiliary$HV1ZPPVt@3e0e1046
Object call = null;
try {
System.out.println("只有前置增强");
// 调用目标静态方法本身
call = callable.call();
} catch (Exception e) {
}
return call;
}
}
清空方法体
为了保护源码,在编译期,将方法体先保存到数据库或者别的地方,然后使用redefine + method + 指定拦截器,将方法内容清空
DynamicType.Unloaded<Demo1> unloaded = new ByteBuddy()
.with(TypeValidation.of(false))
.rebase(Demo1.class)
.name("Demo1Sub")
// 拦截所有方法
.method(any())
// 将方法体清空
.intercept(StubMethod.INSTANCE)
.make();
javaagent
拦截实例方法
1、背景
拦截输出SpringMvc的某个Controller的某个方法的耗时
2、实现
- 启动类
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class},
scanBasePackages = "com.mjp")
public class ApplicationLoader {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(ApplicationLoader.class);
springApplication.run(args);
System.out.println("=============启动成功=============");
}
}
- controller类
@RestController
public class UserController {
@GetMapping("/query/{id}")
public List<UserPO> query(@PathVariable("id") Long id){
List<UserPO> userPOS = Lists.newArrayList();
return userPOS;
}
@GetMapping("/select")
public Integer select(){
try {
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e) {
}
return 1;
}
}
- 定义agent
import net.bytebuddy.agent.builder.AgentBuilder;
import java.lang.instrument.Instrumentation;
import static net.bytebuddy.matcher.ElementMatchers.*;
/**
* Author:majinpeng
* Date: 2024/05/11 15:26
*/
public class MyAgent {
private static final String REST_CONTROLLER_NAME = "org.springframework.web.bind.annotation.RestController";
private static final String CONTROLLER_NAME = "org.springframework.stereotype.Controller";
/**
* agentmain对应动态加载方式:
* 1、在MANIFEST.MF中指定Agent-Class: com.mjp.interceptmvc.MyAgent
* 2、打jar包
* 3、在main方法中,通过代码的方式执行javaagent
* premain对应静态加载方式:
* 1、在MANIFEST.MF中指定Premain-Class: com.mjp.interceptmvc.MyAgent
* 2、打jar包
* 3、指定VM Option参数为jar的路径
* @param args
* @param instrumentation
*/
public static void premain(String args, Instrumentation instrumentation) {
AgentBuilder builder = new AgentBuilder
.Default()
// 忽略拦截的类
.ignore(nameStartsWith("org.xxx")
.or(nameContains("MyService")))
// 指定拦截的类(这里我们拦截被@XxxController注解修饰的类 或 直接指定具体的类名)
.type(
isAnnotatedWith(
named(REST_CONTROLLER_NAME).or(named(CONTROLLER_NAME))
).or(nameContains("UserController"))
)
// 指定要拦截的方法
.transform(new MyTransform())
// 指定监听器
.with(new MyListener());
builder.installOn(instrumentation);
}
}
- 定义transform
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.utility.JavaModule;
import java.security.ProtectionDomain;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.nameContains;
import static net.bytebuddy.matcher.ElementMatchers.nameEndsWith;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.not;
/**
* Author:majinpeng
* Date: 2024/05/11 15:39
*/
public class MyTransform implements AgentBuilder.Transformer {
private static final String MAPPING_PAG = "org.springframework.web.bind.annotation";
private static final String MAPPING_SUFFIX = "Mapping";
/**
* 拦截指定的方法,并返回拦截链
* 当指定的类第一次被加载时,会进入此transform方法
* @param builder : 拦截链(类似于Stream流),我们之前的subclass方法的返回值就是Builder
* 其后续的各种链式方法的返回值都是Builder类型,直到make()方法返回Unloaded
* @param typeDescription : 被拦截的类的信息
* @param classLoader : 类加载器
* @param javaModule
* @param protectionDomain
* @return
*/
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule javaModule,
ProtectionDomain protectionDomain) {
DynamicType.Builder.MethodDefinition.ReceiverTypeDefinition<?> intercept = builder
// 这里我们拦截被@XxxMapping修饰的方法 或 方法名称含有select的
.method(
not(isStatic())
.and(
isAnnotatedWith(nameStartsWith(MAPPING_PAG)
.and(nameEndsWith(MAPPING_SUFFIX)))
)
.or(nameContains("select"))
)
// 指定拦截器处理
.intercept(MethodDelegation.to(new MyInterceptor()));
return intercept;
}
}
- Callable
public interface MyCallable {
Object call(Object[] args);
}
- 定义拦截器
import java.lang.reflect.Method;
/**
* Author:majinpeng
* Date: 2024/05/11 15:55
*/
public class MyInterceptor {
/**
* 参考cglib,自定义MethodInterceptor,重写intercept方法中的参数
*
* @RuntimeType :修饰拦截方法
* @param o :代理对象(Demo1Sub),只有拦截实例方法、构造方法时可用
* @param method : 要拦截的方法,只有拦截实例方法、静态方法时可用
* @param objects :要拦截的方法参数,任意方法的拦截都可用
* @param targetObj :目标对象,只有拦截实例方法、构造方法时可用
* @param callable :用于调用目标对象的拦截方法(类似cglib中MethodProxy触发目标类的方法调用的对象)
*/
@RuntimeType
public Object intercept(@This Object o,
@Origin Method method,
@AllArguments Object[] objects,
@Super Object targetObj,
@Morph MyCallable callable) {
long start = System.currentTimeMillis();
Object call = null;
try {
// 调用目标方法本身
call = callable.call(objects);
} catch (Exception e) {
} finally {
long end = System.currentTimeMillis();
System.out.println("方法总共耗时:" + (end - start));
}
return call;
}
}
- 定义Listeren
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.utility.JavaModule;
/**
* Author:majinpeng
* Date: 2024/05/11 16:02
*/
public class MyListener implements AgentBuilder.Listener {
@Override
public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {
// 当某个类被加载时,就会回调此方法
// 和拦截基本无关,因为每个类都要被加载到内存,都会调用此方法
// 一般不做处理
}
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {
// 当某个类完成了Transformation之后会回调此方法
// 和拦截相关,表明类被拦截处理完成后
System.out.println(typeDescription.getActualName() + ": 被拦截处理完成了");
}
@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {
// 被MyAgent中的ignore忽略的类 被加载时,会触发此方法
// 一般不做处理
}
@Override
public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {
// bytebuddy在transform过程中发生异常时,会调用此方法
// 可以此处打印异常信息,并做异常处理等
}
@Override
public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {
// 当某个类被(transform、ignore、onError)三个方法执行完成后,都会调用此方法,类似finally
// 一般不做处理
}
}
3、调用
1)静态
- MANIFEST.MF
resources/META-INF/
Premain-Class: com.mjp.interceptmvc.MyAgent
Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true
- pom
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<finalName>agent-1.0-SNAPSHOT</finalName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
- VM Option指定生成的jar路径
-javaagent:D:\CodeBetter\target\agent-1.0-SNAPSHOT.jar
2)动态
- MANIFEST.MF
Agent-Class: com.mjp.interceptmvc.MyAgent
Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true
- pom(和静态相同)
- 代码
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import java.util.List;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}, scanBasePackages = "com.mjp")
public class ApplicationLoader {
public static void main(String[] args) {
List<VirtualMachineDescriptor> list = VirtualMachine.list();
for (VirtualMachineDescriptor virtualMachineDescriptor : list) {
try {
VirtualMachine virtualMachine = VirtualMachine.attach(virtualMachineDescriptor);
virtualMachine.loadAgent("D:\\CodeBetter\\target\\agent-1.0-SNAPSHOT.jar");
} catch (Exception e) {
}
}
SpringApplication springApplication = new SpringApplication(ApplicationLoader.class);
springApplication.run(args);
System.out.println("=============启动成功=============");
}
}