Java:使用javassist模拟实现cglib代理操作

当前版本:jdk1.8

1. 声明

当前内容主要为使用javassist实现cglib的代理功能,当前javassist内容参考官方文档官方文档

主要核心思想:

  1. 使用javassist动态创建需要操作类的子类
  2. 在子类中重写方法并构建需要转移的InvocationHandler和方法调用
  3. 这里直接使用java.lang.reflect.InvocationHandler来实现转移操作

2. 基本demo

/**
 * 
 * @author hy
 * @createTime 2022-03-06 16:16:28
 * @description 主要为了模拟cglib的功能
 *
 */
public class JavassistTest {
	public interface IShow{
		void show();
	}
	public static class Father implements IShow{
		public void show() {
			System.out.println("father");
			//return "show called";
		}
		
		public String getResult() {
			return "ok";
		}
		/*
		 * @Override public String toString() { return "Father object"; }
		 */
	}
	public static void main(String[] args) throws Exception {
		Object target = new Father();
		Father proxy = (Father)generatorProxy(Father.class,new InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				System.out.println("before===>");
				Object invoke = method.invoke(target, args);
				System.out.println("after===>");
				return invoke;
			}
		});
		((IShow)proxy).show();
		String result = proxy.getResult();
		System.out.println(result);
		System.out.println(proxy);
	}
	
	// 生成代理(只处理实体类的情况)
	private static Object generatorProxy(Class<?> clazz,InvocationHandler invocationHandler) throws Exception {
		ClassPool pool = ClassPool.getDefault();
		CtClass fatherClass = pool.get(clazz.getName());
		CtClass invocationhandlerClass = pool.get(InvocationHandler.class.getName());
		//	直接创建一个子类
		CtClass childClass = pool.makeClass("com.hy.java.bytecode.generator.Proxy$1", fatherClass);
		
		// 为其添加所有的接口
		generatorInterfaces(childClass,clazz,pool);
		//	为该子类添加一个invocationHandler的字段
		generatorField(childClass);
		//	添加有参数的构造函数
		generatorConstructor(invocationhandlerClass,childClass);
		
		// 生成类的所有方法重写,并完成执行转发操作
		List<CtMethod> generatorMethods = generatorMethod(clazz,childClass);
		for (CtMethod ctMethod : generatorMethods) {
			childClass.addMethod(ctMethod);
		}

		Class<?> generatorClass = childClass.toClass();
		Constructor<?> argConstructor = generatorClass.getConstructor(InvocationHandler.class);
		Object newInstance = argConstructor.newInstance(new Object[] {invocationHandler});
		return newInstance;
	}
	
	// 为生成的类添加接口
	private static void generatorInterfaces(CtClass childClass,Class<?> clazz,ClassPool pool) throws NotFoundException {
		Class<?>[] interfaces = clazz.getInterfaces();
		for (Class<?> in : interfaces) {
			CtClass ctClass = pool.get(in.getName());
			childClass.addInterface(ctClass);
		}
	}
	
	// 为生成的类添加有参的构造函数,并调用赋值
	private static void generatorConstructor(CtClass invocationhandlerClass,CtClass childClass) throws CannotCompileException {
		CtConstructor constructor = new CtConstructor(new CtClass[] {invocationhandlerClass}, childClass);
		// 执行this.h = 第一个参数,就是给创建的类进行赋值操作
		constructor.setBody("$0.h=$1;");
		childClass.addConstructor(constructor);
	}
	
	// 为当前的类定义一个字段
	private static void generatorField(CtClass childClass) throws CannotCompileException {
		CtField hField = CtField.make("public java.lang.reflect.InvocationHandler h;", childClass);
		childClass.addField(hField);
		
	}
	
	// 为实体类创建代理方法,为所有的方法都实现代理操作
	private static List<CtMethod> generatorMethod(Class<?> clazz,CtClass childClass) throws CannotCompileException {
		// 实现所有的clazz中的所有方法
		Method[] declaredMethods = clazz.getDeclaredMethods();
		List<CtMethod> ctMethods =new ArrayList<>(declaredMethods.length);
		for (Method method : declaredMethods) {
			String name = method.getName();
			Class<?> returnType = method.getReturnType();
			Class<?>[] parameterTypes = method.getParameterTypes();
			List<Class<?>> asList = Arrays.asList(parameterTypes);
			String parameterName = getParameterName(asList);
			CtMethod ctMethod = CtMethod.make("public "+returnType.getName()+" "+name+"("+parameterName+");", childClass) ;
			if(returnType==Void.class) {
				//clazz.getMethod("equals", parameterTypes)
				ctMethod.setBody("{($r)$0.h.invoke($0,"+clazz.getName()+".class.getMethod(\""+name+"\",$sig),$args);}");
			}else {
				// return this.h.invoke(this,method,args);
				ctMethod.setBody("{return ($r)$0.h.invoke($0,"+clazz.getName()+".class.getMethod(\""+name+"\",$sig),$args);}");
			}
			ctMethods.add(ctMethod);
			
		}
		return ctMethods;
		
	}
	
	// 生成参数列表
	private static String getParameterName(List<Class<?>> asList) {
		if(asList.isEmpty()) {
			return "";
		}
		int size = asList.size();
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < size; i++) {
			String name = asList.get(i).getName();
			stringBuilder.append(name);
			if(i<size-1) {
				stringBuilder.append(",");
			}
		}
		return stringBuilder.toString();
	}
}

这里我们构造一个类这个类继承传递的类,并实现传递类的接口,具有以下功能

  1. 具有有参的构造函数,参数为InvocationHandler,并在构造函数内为字段h赋值
  2. 具有一个字段InvocationHandler h
  3. 所有的方法都直接通过调用h的方式进行调用转发的操作

缺点:只能代理实体类

3. 测试和总结

在这里插入图片描述
实现成功!!!

1. 其中最主要的部分为($r)表示使用来将invoke的结果Object进行强制转换为返回值类型,保证结果(主要是为了解决有返回值的方法调用问题)

2. 对于方法的创建必须要手动进行访问修饰符以及方法参数的构建操作,这个比较麻烦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值