简介

JDK动态代理

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

CGlib动态代理

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

具体使用

JDK动态代理

代理调用处理逻辑

public class ProxyHandler implements InvocationHandler {
    private Object object;

    public ProxyHandler(Object object) {
        this.object = object;
    }

    @Override
    public 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;

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

// 继承类
public class AdminUser extends User{

    @Override
    public 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.Person
	at 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;
    }

    @Override
    public 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.class
        try {
            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;
        // 判断是否设置了 MethodInterceptor
        if (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

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐