静态代理,JDK 动态代理,CGLIB 动态代理都是些什么鬼?

代理模式(静态代理)

代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。代理类和被代理类通常会存在关联关系(代理类持有的被代理对象的引用 private RealImage realImage;),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object.

  • 优点:可以很好的保护实际对象的业务逻辑对外暴露,从而提高安全性。
  • 缺点:不同的接口要有不同的代理类实现,会很冗余
  1. 创建一个接口(Image),然后创建被代理的类(RealImage)实现该接口并且实现该接口中的抽象方法。
public interface Image {

    String findImage(String name);

    String display();
}

public class RealImage implements Image {

    private String fileName;

    public RealImage() {
    }

    public RealImage(String fileName){
        this.fileName = fileName;
    }

    @Override
    public String findImage(String name) {
        System.out.println("原有的findImage方法");
        return "find by " + name;
    }

    @Override
    public String display() {
        System.out.println("原有的display方法");
        return "Displaying " + fileName;
    }
}
  1. 创建一个代理类(ProxyImage),同时也实现这个接口。
  2. 在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
public class ProxyImage implements Image {

    private RealImage realImage;

    private String fileName;

    public ProxyImage(String fileName){
        this.fileName = fileName;
        realImage = new RealImage(fileName);
    }


    @Override
    public String findImage(String name) {

        String image = realImage.findImage(name);
        System.out.println("增强被代理的方法");
        return image;
    }

    @Override
    public String display() {

        String display = realImage.display();
        System.out.println("增强被代理的方法");
        return display;
    }
}

public class ProxyPatternDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage("test_10mb.jpg");

        String display = image.findImage("6666.avi");
        System.out.println(display);

        System.out.println("");

        String display1 = image.display();
        System.out.println(display1);
    }
}

动态代理

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

  • InvocationHandler 该接口中仅定义了一个方法invoke(Object obj,Method method, Object[] args)
  • Proxy 动态代理类

动态代理步骤

  1. 创建被代理的类以及接口
  2. 创建一个实现接口InvocationHandler的类,它必须实现invoke方法
public class InvocationHandlerImpl implements InvocationHandler {

    // 要代理的对象
    private Object target;

    // 给要代理的真实对象赋初值
    public InvocationHandlerImpl(Object target) {
        this.target = target;
    }

	/* 	proxy:  - 指代我们所代理的那个真实对象
    	method: - 指代的是我们所要调用真实对象的某个方法的Method对象
    	args:  - 指代的是调用真实对象某个方法时接受的参数*/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before..............");
        System.out.println("要增强的方法:" + method.getName());

        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        Object value = method.invoke(target, args);
        System.out.println("after..............");
        return value;
    }
}
  1. 通过Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler handler)创建一个代理
  2. 通过代理调用方法
public class DynamicProxyDemonstration {
    public static void main(String[] args) {
        Image realImage =  new RealImage("66666.img");
        /**
         * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
         * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
         * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
         */
        InvocationHandler handler = new InvocationHandlerImpl(realImage);

        ClassLoader loader = realImage.getClass().getClassLoader();
        
        Class[] interfaces = realImage.getClass().getInterfaces();

        /**
         * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
         *
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真		
         * 实对象,这样我就能调用这组接口中的方法了
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        Image imageProxy = (Image) Proxy.newProxyInstance(loader, interfaces, handler);


        String display = imageProxy.display();
        System.out.println(display);

        String name = imageProxy.findImage("789.avi");
        System.out.println(name);
    }
}

CGLIB 动态代理

CGLIB 创建动态代理类的模式是:

  1. 查找目标类上的所有非 final 的 public 类型的方法 (final 的不能被重写)
  2. 将这些方法的定义转成字节码
  3. 将组成的字节码转换成相应的代理的 Class 对象然后通过反射获得代理类的实例对象
  4. 实现 MethodInterceptor 接口, 用来处理对代理类上所有方法的请求
public class CglibInterceptor implements MethodInterceptor {

    /**
     * CGLIB 增强类对象,代理类对象是由 Enhancer 类创建的,
     * Enhancer 是 CGLIB 的字节码增强器,可以很方便的对类进行拓展
     */
    private Enhancer enhancer = new Enhancer();

    /**
     *
     * @param object  被代理的对象
     * @param method 代理的方法
     * @param objects 方法的参数
     * @param methodProxy CGLIB方法代理对象
     * @return  cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升
     * @throws Throwable
     */
    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法增强前........");
        Object o = methodProxy.invokeSuper(object, objects);
        System.out.println("方法增强后........");
        return o;
    }

    /**
     * 创建一个代理对象
     */
    public Object newProxyInstance(Class<?> c) {
        /**
         * 设置产生的代理对象的父类,增强类型
         */
        enhancer.setSuperclass(c);
        /**
         * 定义代理逻辑对象为当前对象,要求当前对象实现 MethodInterceptor 接口
         */
        enhancer.setCallback(this);
        /**
         * 使用默认无参数的构造函数创建目标对象,被代理的类要提供无参构造方法
         *  有参构造 create(Class[] argumentTypes, Object[] arguments)
         */
        //return enhancer.create();
        return enhancer.create(new Class[]{String.class}, new Object[]{"daqiao.avi"});
    }
}


// 测试类
public class CglibProxyDemo {
    public static void main(String[] args) {
        CglibInterceptor cglibInterceptor = new CglibInterceptor();

        Image image = (Image )cglibInterceptor.newProxyInstance(RealImage.class);

        String display = image.display();
        System.out.println(display);

        String image1 = image.findImage("123.avi");
        System.out.println(image1);
    }
}

JDK 动态代理和 CGLIB 动态代理的区别

  1. JDK 动态代理基于 Java 反射机制实现, 必须要实现了接口的业务类才能用这种方法生成代理对象,因为动态生成的类默认继承Proxy类。而 CGLIB 动态代理基于 ASM 框架通过生成业务类的子类来实现。
  2. JDK 动态代理的优势是最小化依赖关系,代码实现简单。因为是基于接口设计实现的,如果没有接口,会抛异常。而基于 CGLIB 框架的优势是无须实现接口。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值