cglib动态代理浅析

谈到Java当中的动态代理,主流的就两种:JDK内置的动态代理和cglib动态代理。
JDK方式的动态代理要求被代理类必须要实现于接口(因为生成的代理对象首先要继承于java.lang.reflect.Proxy,不能再多继承了),而且执行代理对象方法时候是使用反射的方式调用,故而在频繁调用代理方法时候,会有一定的性能问题。JDK方式动态代理不是今天讨论的重点,有兴趣可以留言,我再写一篇。

今天要讨论的主角是cglib方式的动态代理。

首先,我们可以假设市面上没有cglib动态代理的这种解决方案,然后在实际开发过程中发现,一旦要用到动态代理(主要场景就是aop),就必须多写一个接口,虽然说面向接口编程是挺好的,但程序员不就是因为懒才能推动技术进步么,能不能只写一个类且还能实现我的需求呢?

// 常规的写法

public interface UserService {

	void eat();

	void wc();
}

public class UserServiceImpl implements UserService {

	@Override
	public void eat() {
		System.out.println("吃饭");
	}

	@Override
	public void wc() {
		System.out.println("上厕所");
	}
}

然我我就想啊想,应该是可以的,画一个简易的流程图
在这里插入图片描述

这样一来,确实是可以实现我们的目的,拦截器里边可以把被代理对象的Method传进来,然后就可以为所欲为,什么前置、后置、环绕的处理都是可以进行拓展。

那就简单的用伪代码实现之

// 被代理类
public class MyService {

    public void show() {
        System.out.println("实现类show");
    }
}

// 拦截器
public interface MyInterceptor {

    /**
     *
     * @param var1 代理对象
     * @param var2 被代理类的原方法
     * @param var3 方法参数
     * @param var4 方法代理
     * @return
     * @throws Throwable
     */
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}

// 自定义代理逻辑
public class MyProxy implements MyInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        System.out.println("前置处理");
        o = methodProxy.invokeSuper(o, args);
        System.out.println("后置处理");

        return o;
    }
}
// 增强器 用来生成代理对象
public class MyEnhancer {

    private Class<?> superClass; // 父类类型

    private MyInterceptor interceptor; // 拦截器

    public MyEnhancer(Class<?> superClass, MyInterceptor interceptor) {
        this.superClass = superClass;
        this.interceptor = interceptor;
    }

    // 创建代理对象
    public Object create() {
        // 使用字节码生成技术 将真实对象类的class文件加载进来,通过修改字节码生成其子类,覆盖父类相应的方法。
        // ... 忽略
        return new MyService();
    }
}
public class Test {

    public static void main(String[] args) {

        // 创建增强器
        MyEnhancer enhancer = new MyEnhancer(MyService.class, new MyProxy());

        // 创建代理对象
        MyService proxy = (MyService) enhancer.create();

        // 执行代理方法
        proxy.show();
    }
}

根据流程图大致用伪代码实现出来了。
增强器里的创建代理对象,其实就是生成一个新的Class对象放到方法区中,JDK动态代理是使用Sun的ProxyGenerator来生成字节码,而cglib是使用ASM的字节码框架,后者效率更高,具体就不展开了,有兴趣可以去研究。

问题来了!
我现在生成了代理对象,那么该如何调用呢?cglib是使用继承并重写父类方法的方式来实现动态代理,对于private、final修饰的方法(也就是不能被代理的方法),又该如何正确的调用呢?

这就头大了。来分析一波,首先这些方法嘛,有没有唯一的id呢?对了!方法签名(方法名字+方法参数)。那我可不可以再搞个类出来,它就专门负责方法路由,根据方法签名,或者可以取签名的hash值,路由到指定方法上去执行。

cglib确实就是这么做的,而且它生成的路由类是两个,一个负责代理方法的路由,另一个负责未被代理的方法路由。


好了,扯了一堆,现在看看cglib实际使用吧

public class PeopleService {

    public final int eat(){
        System.out.println("吃饭");
        wc(); // 此处调用wc()方法是会走被代理过的方法 因为会重新走路由的 JDK方式是反射执行 就不可以走路由
        return 1;
    }
    public void wc(){
        System.out.print("上厕所");
    }
}
public class MyProxy implements MethodInterceptor {

	public Object createProxy(Class<?> c){
		//代理类class文件存入本地磁盘
		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\Users\\www\\Desktop\\my_challenge\\mynote\\src\\main\\java\\proxy\\mytest\\cglib\\classes");
		Enhancer e = new Enhancer();
		e.setSuperclass(c);
		e.setCallback(this);
		return e.create();
	}


	@Override
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		if ("eat".equals(method.getName())){
			System.out.print("先洗手再----->");
		}
		o = methodProxy.invokeSuper(o, objects);

		if ("wc".equals(method.getName())){
			System.out.print("---->之后要洗手");
		}
		return o;
	}
}
public class Test {

	public static void main(String[] args) throws InterruptedException {

		MyProxy myProxy = new MyProxy();
		PeopleService proxy = (PeopleService) myProxy.createProxy(PeopleService.class);
		System.out.println(proxy.eat());
	}
}

生成了3份字节码
在这里插入图片描述
在这里插入图片描述
这样子的话,修改下流程图,应该长这样
在这里插入图片描述
总结一下cglib的特点:

  • 在JDK动态代理中,调用目标对象的方法使用的是反射,而在cglib动态代理中使用的是FastClass机制。
  • cglib生成字节码的底层原理是使用ASM字节码框架。
  • cglib动态代理需创建3份字节码,所以在第一次使用时会比较耗性能,但是后续使用较JDK动态代理方式更高效。适合单例bean场景。
  • cglib由于是采用动态创建子类的方法,对于final方法,无法进行代理。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值