动态代理(JDK、Cglib、Bytebuddy)

动态代理

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

  1. 前置增强

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("=============启动成功=============");
    }
}
  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值