从代码中体会代理思想,jdk动态代理和cglib代理

本文介绍Java动态代理和CGLIB代理的基本概念及应用,对比两种代理方式的特点,并提供示例代码,帮助理解如何为接口和普通类创建代理。

简单demo

定义一个usb接口

public interface USB{
void m1();
void m2();
void m3();
}

接口有2个实现类mouse fan鼠标风扇

package com.zzb.test;
public class Fan implements USB{
    @Override
    public void m1() {
        System.out.println("fan m1");
    }

    @Override
    public void m2() {
        System.out.println("fan m2");
    }

    @Override
    public void m3() {
        System.out.println("fan m3");
    }
}
package com.zzb.test;
public class Mouse implements USB{
    @Override
    public void m1() {
        System.out.println("mouse m1");
    }

    @Override
    public void m2() {
        System.out.println("mouse m2");
    }

    @Override
    public void m3() {
        System.out.println("mouse m3");
    }
}

定义一个代理类

package com.zzb.test;
public class USBProxy implements USB{
    private USB target;
    public USBProxy(USB target){
        this.target = target;
    }
    @Override
    public void m1() {
        long starTime = System.nanoTime();
        this.target.m1();
        long endTime = System.nanoTime();
        System.out.println(this.target.getClass() + ".m1()方法耗时(纳秒):" + (endTime - starTime));
    }

    @Override
    public void m2() {
        long starTime = System.nanoTime();
        this.target.m1();
        long endTime = System.nanoTime();
        System.out.println(this.target.getClass() + ".m1()方法耗时(纳秒):" + (endTime - starTime));
    }

    @Override
    public void m3() {
        long starTime = System.nanoTime();
        this.target.m1();
        long endTime = System.nanoTime();
        System.out.println(this.target.getClass() + ".m1()方法耗时(纳秒):" + (endTime - starTime));
    }
}

定义一个测试类

package com.zzb.test;
public class Test {
    public static void main(String[] args) {
        USB usb1 = new USBProxy(new Mouse());
        USB usb2 = new USBProxy(new Fan());
    }
}

从代码中可以看出,访问被代理的对象mouse和fan的时候,必须经过代理对象USBProxy,这里只写了计算调用方法所需时间来进行举例。
把实现类都通过代理类进行操作,添加了代理类独有的功能,不用再去挨个修改每个实体类了,节省了大量时间和资源。

缺点:为每个接口写一个代理类,同样很花费时间,能否写个通用的代理类呢?

通用代理类

方法1:jdk动态代理
缺点:只能为接口创建代理类
jdk中为实现代理提供了支持,主要有2个类java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler
getProxyClass方法
为指定的接口创建代理类,返回代理类的Class对象

public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)

newProxyInstance方法
创建代理类的实例对象

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
/*InvocationHandler是个接口,内含invoke方法,操作代码写在其中*/

isProxy方法
判断指定的类是否是一个代理类

public static boolean isProxyClass(Class<?> cl)

getInvocationHandler方法
获取代理对象的 InvocationHandler 对象

public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException

方式一:先获取代理类,在创建实例。getProxyClass getConstructor

package com.zzb.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<USB> proxyClass = (Class<USB>) Proxy.getProxyClass(USB.class.getClassLoader(), USB.class);
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("我是InvocationHandler,被调用的方法是:" + method.getName());
                return null;
            }
        };
        USB usb = proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
        usb.m1();
        usb.m2();
        usb.m3();
    }
}

方式二:直接通过newProxyInstance创建实例

package com.zzb.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("我是InvocationHandler,被调用的方法是:" + method.getName());
                return null;
            }
        };
        USB usb = (USB) Proxy.newProxyInstance(USB.class.getClassLoader(), new Class[]{USB.class}, invocationHandler);
        usb.m1();
        usb.m2();
        usb.m3();
    }
}

综合以上,重写简单demo中的代理类

package com.zzb.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class CommonProxy implements InvocationHandler {
    private Object target;
    public CommonProxy(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long starTime = System.nanoTime();
        Object result = method.invoke(this.target, args);
        long endTime = System.nanoTime();
        System.out.println(this.target.getClass() + ".m1()方法耗时(纳秒):" +
                (endTime - starTime));
        return result;
    }
    /**
     * @author zzb
     * @date 2021/4/11 22:52
     * @Param: target 接口实现类
     * @Param: targetInterface 接口类型
     * @return
     */
    public static <T> T createProxy(Object target, Class<T> targetInterface) {
        if (!targetInterface.isInterface()) {
            throw new IllegalStateException("targetInterface必须是接口类型!");
        } else if (!targetInterface.isAssignableFrom(target.getClass())) {
            throw new IllegalStateException("target必须是targetInterface接口的实现类!");
        }
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new CommonProxy(target)
        );
    }
}

这里建议和上面的简单demo的代理类对比着看
测试类:

package com.zzb.test;
public class Test {
    public static void main(String[] args) throws Exception {
        USB proxy = CommonProxy.createProxy(new Fan(), USB.class);
        proxy.m1();
    }
}

这段的重点是createProxy方法
如果要修改执行内容,修改invoke方法
jdk动态代理只能解决接口代理,而类的代理只能用下面讲的cglib了

cglib代理

可以解决为普通的类也实现代理功能。

cglib是什么?
cglib作为一个开源项目,不属于jdk。
cglib是一个字节码生成库,用于在运行时扩展java类和实现接口,本质上是通过动态生成一个子类来覆盖索要代理的类。所要代理的类是非final修饰的类。毕竟final类不可继承。
cglib底层使用ASM,一个字节码操作框架,用来操作字节码生成新的类。
Enhancer是CGLIB中最常用的一个类,和jdk中的Proxy对比,Enhancer既能够代理普通的class,也能够代理接口。

Enhancer类代理的原理?
它创建一个被代理对象的子类并且拦截所有的方法调用,包括toString和hashCode方法。不能拦截final方法,如Object.getClass()

spring已将第三方cglib jar包中所有的类集成到spring自己的jar包中,我这里是直接使用spring的jar包。

创建一个需要被代理的类

package com.zzb.test;

public class USB {
    public void m1(){
        System.out.println("usb 方法1");
    }
    public void m2(){
        System.out.println("usb 方法2");
    }
    public void m3(){
        System.out.println("usb 方法3");
    }
}

测试类,测试cglib代理
到这里就不是一个java工程了,是个web工程,jar包,另外pom.xml中别忘了添加spring依赖,毕竟spring中有着cglib的jar包,而这里写的是cglib代理程序

package com.zzb.test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Test {
    public static void main(String[] args) throws Exception {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(USB.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            /**
             * 代理对象方法拦截器
            * @Param: o 代理对象(被代理类的实例)
            * @Param: method 被代理类的方法
            * @Param: objects 调用方法传的参数
            * @Param: methodProxy 方法代理对象
             * @return
             */
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("调用方法:"+method);
                Object result = methodProxy.invokeSuper(o, objects);
                return result;
            }
        });
        USB proxy = (USB) enhancer.create();
        proxy.m1();
        proxy.m2();
        proxy.m3();
    }
}

这里用到了ENhancer类和MethodInterceptor接口

spring中的@configuration注解就是采用的cglib代理
举个例子

package com.zzb.test;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author zhangzhibin
 * @create 2021-04-12 0:06
 */
@Configuration
public class TestSpring {
    public static class Class1{}
    public static class Class2{
        private Class1 Class1;
        public Class2(Class1 Class1) {
            this.Class1 = Class1;
        }
    }
    public static class Class3{
        private Class1 Class1;
        public Class3(Class1 Class1) {
            this.Class1 = Class1;
        }
    }
    @Bean
    public Class1 getClass1(){
        return new Class1();
    }
    @Bean
    public Class2 getClass2(){
        Class1 Class1 = this.getClass1();
        return new Class2(Class1);
    }
    @Bean
    public Class3 getClass3(){
        Class1 Class1 = this.getClass1();
        return new Class3(Class1);
    }
}

这里Configuration能够保证,class2和class3的bean获取的class1的bean是同一个bean。

添加多个拦截器,分别拦截不同的方法,根据方法名判断使用不同的方法

package com.zzb.test;
import org.springframework.cglib.proxy.*;
import java.lang.reflect.Method;
public class Test {
    public static void main(String[] args) throws Exception {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(USB.class);
        Callback[] callbacks = {
                new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        System.out.println("第一个拦截器");
                        Object result = methodProxy.invokeSuper(o, objects);
                        return result;
                    }
                },
                new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        System.out.println("第二个拦截器");
                        Object result = methodProxy.invokeSuper(o, objects);
                        return result;
                    }
                }
        };
        enhancer.setCallbackFilter(new CallbackFilter() {
            @Override
            public int accept(Method method) {
                String methodName = method.getName();
                /*
                如果方法名字以1结尾,就返回0,使用第一个拦截器
				*/
                return methodName.endsWith("1") ? 0 : 1;
            }
        });
        enhancer.setCallbackFilter(new CallbackFilter() {
            @Override
            public int accept(Method method) {
                String methodName = method.getName();
                /*
                如果方法名字以2结尾,就返回1,使用第二个拦截器
				*/
                return methodName.endsWith("2") ? 1 : 0;
            }
        });
        enhancer.setCallbacks(callbacks);
        USB proxy = (USB) enhancer.create();
        proxy.m1();
        proxy.m2();
    }
}

下面是创建通用代理类,CommonProxy,可以和jdk动态代理的通用代理类对比着来看。

package com.zzb.test;


import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CommonProxy implements MethodInterceptor {
    private Object target;
    public CommonProxy(Object target) {
        this.target = target;
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long starTime = System.nanoTime();
        Object result = method.invoke(target, objects);
        long endTime = System.nanoTime();
        System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
        return result;
    }
    public static <T> T createProxy(T target) {
        CommonProxy commonProxy = new CommonProxy(target);
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(commonProxy);
        enhancer.setSuperclass(target.getClass());
        return (T) enhancer.create();
    }
}

测试类

package com.zzb.test;
public class Test {
    public static void main(String[] args) throws Exception {
        USB proxy = CommonProxy.createProxy(new USB());
        proxy.m1();
        proxy.m2();
    }
}

总结

CGLIB和Java动态代理的区别

  1. Java动态代理只能够对接口进行代理,不能对普通的类进行代理
  2. Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

静安书以沫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值