当前位置: 代码迷 >> 综合 >> 【Java】- java动态代理分析(jdk,cglib)
  详细解决方案

【Java】- java动态代理分析(jdk,cglib)

热度:36   发布时间:2023-12-21 07:36:40.0

简介

JDK动态代理

利用反射机制生成一个实现代理接口的匿名类,将具体的调用方法织入InvokeHandler来处理。

CGlib动态代理

利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

具体使用

JDK动态代理

代理调用处理逻辑

public class ProxyHandler implements InvocationHandler {
    private Object object;public ProxyHandler(Object object) {
    this.object = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("Before invoke " + method.getName());method.invoke(object, args);System.out.println("After invoke " + method.getName());return null;}
}

代理目标接口与其实现类的声明


public interface Person {
    void eat();
}// 实现接口
public class User implements Person{
    private String name;@Overridepublic void eat() {
    System.out.println("eat apple ....");}
}// 继承类
public class AdminUser extends User{
    @Overridepublic void eat() {
    System.out.println("eat banana ....");}
}

实现代理

public class Client {
    public static void main(String[] args) {
    // 声明对象User person = new User("ssj");// 创建代理程序ProxyHandler proxyHandler = new ProxyHandler(person);// 创建代理接口对象Person proxyUser = (Person) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), proxyHandler);// 执行代理后的对象proxyUser.eat();// 声明对象 此对象是继承与基类User的AdminUser adminUser = new AdminUser();// 创建代理程序ProxyHandler proxyHandlerAdmin = new ProxyHandler(adminUser);// 创建代理接口对象Person proxyAdminUser = (Person) Proxy.newProxyInstance(proxyHandlerAdmin.getClass().getClassLoader(),proxyHandlerAdmin.getClass().getInterfaces(), proxyHandler);// 执行代理后的对象 proxyAdminUser.eat();}}

输出为:

Before invoke eat
eat apple ....
After invoke eat
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy1 cannot be cast to com.exp.entity.Personat com.exp.entity.Client.main(Client.java:32)
Disconnected from the target VM, address: '127.0.0.1:53819', transport: 'socket'Process finished with exit code 1

由此例我们可以发现,JDK动态代理只支持接口implement类的代理,并不支持extend的代理。当然JDK动态代理还有很多局限性,比如如果接口中有和hashcodeequalstoString等和Object中同名同参数的方法时候method.clazz属性是java.lang.Object而不是com.learn.proxy.Person,具体的限制可以查看javaDoc的Proxy进行详细了解。

CGlib动态代理

代理处理逻辑

public class CGLibProxy implements MethodInterceptor {
    private Object targetObject;public CGLibProxy(Object obj) {
    this.targetObject = obj;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    System.out.println(String.format("%s : before..", method.getName()));Object invoke = method.invoke(targetObject, args);System.out.println(String.format("%s : after..", method.getName()));return invoke;}
}

目标接口与实现类声明同jdk动态代理。

实现代理:

public class Client {
    private static final MethodInterceptor DEFAULT_METHOD_INTERCEPTOR = new CGLibProxy(new Object());public static void main(String[] args) {
    AdminUser adminUser = new AdminUser();// 普通方式创建// 创建增强器Enhancer enhancer = new Enhancer();// 设置父类enhancer.setSuperclass(adminUser.getClass());// 创建代理逻辑CGLibProxy cgLibProxy = new CGLibProxy(adminUser);// 设置代理逻辑enhancer.setCallback(cgLibProxy);// 创建代理对象AdminUser personProxy = (AdminUser) enhancer.create();personProxy.eat();System.out.println("=============");// 采用Factory模式创建 // Using this interface for new instances is faster than going through the Enhancer interface or using reflection. Factory proxyFactory = getProxyFactory(adminUser.getClass());AdminUser factoryUser = (AdminUser) proxyFactory.newInstance(cgLibProxy);factoryUser.eat();}public static Factory getProxyFactory(Class persistentClass) throws Exception {
    //note: interfaces is assumed to already contain HibernateProxy.classtry {
    return (Factory) Enhancer.create(persistentClass, DEFAULT_METHOD_INTERCEPTOR);} catch (Throwable t) {
    throw new Exception("CGLIB Enhancement failed", t);}}}

输出为:

eat : before..
eat banana ....
eat : after..
=============
eat : before..
eat banana ....
eat : after..

看一下cglib生成的代理类:

// 此处继承了 AdminUser 并实现了Factory接口 cglib.proxy.Factory 
public class AdminUser$$EnhancerByCGLIB$$2e18d2b extends AdminUser implements Factory {
    // ... 此处省略public final void eat() {
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;// 判断是否设置了 MethodInterceptorif (var10000 == null) {
    // 若没有 则重新进行绑定CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}// CGLIB$BIND_CALLBACKS 之后 判断是否设置成功if (var10000 != null) {
    // 若设置成功 代表有相关的切入逻辑var10000.intercept(this, CGLIB$eat$3$Method, CGLIB$emptyArgs, CGLIB$eat$3$Proxy);} else {
    // 若没有成功 则直接执行父类的相关方法super.eat();}}// ... 此处省略}

由于CGlib动态代理为采用字节码方式生成代理目标类子类重现相关方法的方式实现代理的,所以不可代理finalprivate修饰的相关对象或方法,否则会抛出IllegalArgumentException异常,且由于它的此种实现方式,使得它可以代理jdk动态代理无法代理的Object的一些原始方法(hashcodeequalstoString),但是getClasswait等方法不会,因为它是final方法。

总结:

1.执行效率: JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;

2.局限性: JDK代理 > cglib代理,cglib代理可以根据继承方式的局限性来进行理解,而jdk代理则以接口实现类为基准。

参考:
https://javadoc.io/doc/cglib/cglib/latest/index.html
https://blog.csdn.net/weixin_36759405/article/details/82770422

  相关解决方案