JDK动态代理和CGLIB动态代理简单区别介绍--实例

静态代理的缺点很明显:一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行,而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。

JDK动态代理所用到的代理类,在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的业务实现类对象以及方法名,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用,JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数,如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了,或者如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的。实例如下:

package cn.ehine.service.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TestMode {
    public static void main(String[] args) {
        //创建原始的NewRobot作为target对象
        NewRobot newRobot = new NewRobot();
        //以指定的target对象来创建动态代理
        Robot Robot = (Robot) MyProxyFactory.getProxy(newRobot);
        Robot.introduction();
        Robot.HMI();
    }
}

/**
 * Robot接口类
 */
interface Robot {
    public void introduction();
    public void HMI();
}

/**
 * 实现Robot接口
 */
class NewRobot implements Robot {
    public void introduction() {
        System.out.println("哈喽!我是机器人...");
    }

    public void HMI() {
        System.out.println("你可以和我交流哟...");
    }
}

/**
 * 动态代理a的一些方法,做些b对象做不了的事情
 */
class RobotUtil {
    public void menthodOne() {
        System.out.println("第一个通用方法...");
    }

    public void menthodTwo() {
        System.out.println("第二个通用方法...");
    }

    public void menthodThree() {
        System.out.println("第三个通用方法...");
    }
}

/**
 * 做主要的处理,其中的invoke方法是主要处理过程,在里面可以自定义动态代理该做什么
 */
class MyInvocationHandler implements InvocationHandler {
    // 需要被代理的对象
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    // 执行动态代理的所有方法时,都会执行如下invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        RobotUtil robotUtil = new RobotUtil();
        // 执行robotUtil的方法menthodOne、menthodThree
        robotUtil.menthodOne();
        robotUtil.menthodThree();
        // 以target作为主调来执行method方法,如果没有则不能执行GunDog里面的方法
        result = method.invoke(target, args);
        // 执行robotUtil的方法menthodTwo
        robotUtil.menthodTwo();
        System.out.println("invoke");
        return result;
    }
}

// 指定的target动态创建代理对象
class MyProxyFactory {
    // 为指定的target动态创建代理对象
    public static Object getProxy(Object target) {
        // 创建一个MyInvocationHandler对象
        MyInvocationHandler handler = new MyInvocationHandler();
        // 为MyInvocation对象设置target对象
        handler.setTarget(target);
        System.out.println("setTagget");
        // 通过反射机制,创建一个代理类对象实例并返回,用户进行方法调用时使用
        // 创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法是调用真正的业务方法)、接口、handler实现类
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
    }
}

CGLIB动态代理是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理,因为采用的是继承,所以不能对final修饰的类进行代理,实例如下:

package cn.ehine.service.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 TestModeTwo {
    public static void main(String[] args) {
        //创建原始的GunDog作为target对象
        NewRobot newRobot = new NewRobot();
        //以指定的target对象来创建动态代理
        NewRobot proxyNewRobot = (NewRobot) MyProxyFactory.getProxy(newRobot);
        proxyNewRobot.introduction();
        proxyNewRobot.HMI();
    }
}

/**
 * 实现Robot接口
 */
class NewRobot {
    public void introduction() {
        System.out.println("哈喽!我是机器人...");
    }

    public void HMI() {
        System.out.println("你可以和我交流哟...");
    }
}

/**
 * 动态代理a的一些方法,做些b对象做不了的事情
 */
class RobotUtil {
    public void menthodOne() {
        System.out.println("第一个通用方法...");
    }

    public void menthodTwo() {
        System.out.println("第二个通用方法...");
    }

    public void menthodThree() {
        System.out.println("第三个通用方法...");
    }
}

/**
 * 做主要的处理,其中的intercept方法是主要处理过程,在里面可以自定义动态代理该做什么
 */
class MyMethodInterceptor implements MethodInterceptor {
    // 需要被代理的对象
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    // 实现回调方法
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object result = null;
        RobotUtil robotUtil = new RobotUtil();
        // 执行robotUtil的方法menthodOne、menthodThree
        robotUtil.menthodOne();
        robotUtil.menthodThree();
        // 以target作为主调来执行method方法,如果没有则不能执行GunDog里面的方法
        result = method.invoke(target, args);
        // 执行robotUtil的方法menthodTwo
        robotUtil.menthodTwo();
        System.out.println("intercept");
        return result;
    }
}

// 指定的target动态创建代理对象
class MyProxyFactory {
    // 为指定的target动态创建代理对象
    public static Object getProxy(Object target) {
        // 创建一个MyInvocationHandler对象
        MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor();
        // 为myMethodInterceptor对象设置target对象
        myMethodInterceptor.setTarget(target);
        Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
        enhancer.setSuperclass(target.getClass());  // 为加强器指定要代理的业务类(即为下面生成的代理类指定父类)
        System.out.println("setTagget");
        // 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法
        enhancer.setCallback(myMethodInterceptor);
        // 创建动态代理类对象并返回
        return enhancer.create();
    }
}

JDK动态代理和CGLIB动态代理区别总结

JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法

CGLIB动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理

JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

CGLIB动态代理是采用ASM字节码生成框架,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理

Spring在选择用JDK还是CGLiB的依据

      当Bean实现接口时,Spring就会用JDK的动态代理

      当Bean没有实现接口时,Spring使用CGlib是实现

      可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)


版权声明:本文为weixin_37988928原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。