实例说JDK动态代理与CGLIB动态代理

     "代理"是我们日常生活中非常常见的一个词一种身份,所谓“代理”简而言之就是充当职权充当责任委托,比如明星的经纪人或品牌的法定代言人,我们需要跟某明星签订演唱会的协议或其他的合作协议,不需要你费劲千辛万苦且徒劳无功去找这位明星,只需要联系他的经纪人就好了。

      在Java中,代理分为静态代理和动态代理,所谓静态代理就是由程序员自定义代理的业务逻辑,在程序编译前就把接口、实现类和代理类都一次性定义好了,我们Java设计模式中的代理设计模式就是经典的静态代理;动态代理一般由第三方提供接口,调用者直接引入对应的依赖包,然后实现(implements)对应的接口并重写(@Override)对应的方法即可,对应需要代理补充的业务逻辑就可以在重写的方法里面实现,比如常见的JDK动态代理(InvocationHandler)与CGLIB动态代理(MethodInterceptor),下文将以这两种动态代理进行举例说明。

 1  JDK动态代理: 

   JDK动态代理只能针对接口进行代理,从早期的jdk1.6版本就开始有,在spring aop面向切面编程中使用较为广泛,在spring中当Bean实现接口时,Spring就会用JDK做动态代理,代理类主要重写InvocationHandler接口中的nvoke(Object proxy, Method method, Object[] args)方法,并在里面实现被代理对象的前置和后置逻辑,如下是详细的实例(一个加法计算器的例子,假如单位是美分):

1.1 计算器接口:

public interface Calculator {
    public int add(int a,int b);
}

1.2  计算器代理类

public class LogHandler implements InvocationHandler {
    Object obj;

    public LogHandler(Object obj) {
        this.obj = obj;
    }

    public LogHandler() {
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("进入代理对象invoke方法");
        System.out.println("调用前");
        System.out.println("method name:"+method.getName());
        System.out.println("method args:"+ Arrays.toString(args));
        Object ret=method.invoke(obj,args);
        ret=Integer.parseInt(ret.toString())*100;//单位是美分,后置结果乘以*100
        System.out.println("调用后");
        return ret;
    }


    public static  T newMapperProxy(Class mapperInterface,Object object) {
        ClassLoader classLoader = mapperInterface.getClassLoader();
        Class[] interfaces = new Class[]{mapperInterface};
        LogHandler proxy = new LogHandler();
        proxy.obj=object;//object是被代理的实例对象,这个对象可以自定义实现需要代理的接口
        return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
    }

}

 1.3 主函数:


public class MainDanamicProxy {

    @Test
    public void  test1 (){

        //自定义实现的计算器接口
       Calculator newObject=new Calculator() {
            @Override
            public int add(int a, int b) {
                return a+b;
            }
        };

        Calculator calculator = LogHandler.newMapperProxy(Calculator.class,newObject);
        int sum=calculator.add(10,20);
        System.out.println("sum(美分):"+sum);
    }

 

}

控制台打印的结果:

实例说JDK动态代理与CGLIB动态代理_第1张图片

 2  CGlib动态代理: 

  CGLIB(Code Generator Library)是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架,在spring框架中中当Bean没有实现接口时,就使用CGlib做动态代理,那为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了,并且CGLIB代理主要通过对字节码的操作,比使用Java反射效率要高。代理类主要重写MethodInterceptor接口中的intercept(Object object, Method method, Object[] args, MethodProxy proxy)方法,并实现前置和后置业务逻辑,如下是详细的实例(一个加法计算器的例子,假如单位是美分):

 2.1 被代理的类

public class CalculatorImpl {
    public int add(int a, int b) {
        System.out.println("进入被代理的方法");
        int sum=a+b;
        return sum;
    }
}

 2.2 代理类

public class CalculatorCgLIB implements MethodInterceptor {

    //创建代理对象
    public Object getInstance(Object target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        // 设置回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("事务开始。。。");
        System.out.println("object name:"+object.getClass());
        System.out.println("method name:"+method.getName());
        System.out.println("MethodProxy proxy:"+proxy.getSuperName());//代理的函数名称
        System.out.println("method args:"+ Arrays.toString(args));
        Object result = proxy.invokeSuper(object, args);//调用被代理的对象并传入对应的参数
        System.out.println("事务结束。。。");
        result=Integer.parseInt(result.toString())*100;//单位是美分,后置结果乘以*100
        return result;
    }
}

   2.3 主函数

public class MainClass {
    @Test
    public void  test1(){
        CalculatorCgLIB cglib = new CalculatorCgLIB();
        CalculatorImpl calculator = (CalculatorImpl) cglib.getInstance(new CalculatorImpl());//创建代理对象
        int sum=calculator.add(10,20);
        System.out.println("sum(美分):"+sum);
    }
}

   控制台打印的结果:

实例说JDK动态代理与CGLIB动态代理_第2张图片

 如上关于jdk动态代理和CGLIB动态代理示例就完全了, CGLIB动态代理需要注意asm.jar  版本的问题,如果版本不匹配,容易出现 java.lang.NoClassDefFoundError: org/objectweb/asm/Type 的错误,

本实例所使用的asm 3.3.1 jar可在这边下载https://download.csdn.net/download/aiyun5520342/10563508,

所使用的cglib-2.2.2.jar可在这边下载https://download.csdn.net/download/yufang131/8593431

你可能感兴趣的:(实例说JDK动态代理与CGLIB动态代理)