美文网首页
Java--反射机制(二)——动态代理

Java--反射机制(二)——动态代理

作者: 年少懵懂丶流年梦 | 来源:发表于2017-06-16 23:41 被阅读103次

一、代理模式

定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。

1、代理模式的理解

代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问。

现实世界的代理人被授权执行当事人的一些事宜,无需当事人出面,从第三方的角度看,似乎当事人并不存在,因为他只和代理人通信。而事实上代理人是要有当事人的授权,并且在核心问题上还需要请示当事人。

在软件设计中,使用代理模式的意图也很多,比如因为安全原因需要屏蔽客户端直接访问真实对象,或者在远程调用中需要使用代理类处理远程方法调用的技术细节,也可能为了提升系统性能,对真实对象进行封装,从而达到延迟加载的目的。

2、代理模式的参与者

代理模式的角色分四种:


  • 主题接口: Subject 是委托对象和代理对象都共同实现的接口,即代理类的所实现的行为接口。Request() 是委托对象和代理对象共同拥有的方法。
  • 目标对象:ReaSubject 是原对象,也就是被代理的对象。
  • **代理对象: **Proxy 是代理对象,用来封装真实主题类的代理类。
  • 客户端 :使用代理类和主题接口完成一些工作。
3、代理模式的分类

静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。

动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。

4、代理模式的实现思路
  1. 代理对象和目标对象均实现同一个行为接口。

  2. 代理类和目标类分别具体实现接口逻辑。

  3. 在代理类的构造函数中实例化一个目标对象。

  4. 在代理类中调用目标对象的行为接口。

  5. 客户端想要调用目标对象的行为接口,只能通过代理类来操作。

interface Subject{
    void request();
}

class RealSubject implements Subject{
    public void request(){
        System.out.println("request");
    }
}

class Proxy implements Subject{
    private Subject subject;
    public Proxy(RealSubject subject){
        this.subject = subject;
    }
    public void request(){
        System.out.println("PreProcess");
        subject.request();
        System.out.println("PostProcess");
    }
}

public class ProxyDemo {
    public static void main(String args[]){
        RealSubject subject = new RealSubject();
        Proxy p = new Proxy(subject);
        p.request();
    }
}

目标对象(RealSubject)以及代理对象(Proxy)都实现了主题接口(Subject)。在代理对象(Proxy)中,通过构造函数传入目标对象(RealSubject ),然后重写主题接口(Subject)的request()方法,在该方法中调用目标对象(RealSubject )的request()方法,并可以添加一些额外的处理工作在目标对象(RealSubject )的request()方法的前后。

看到的一个很形象的例子,对于理解代理还是很有意思的。

5、代理模式的好处

假如有这样的需求,要在某些模块方法调用前后加上一些统一的前后处理操作,比如在添加购物车、修改订单等操作前后统一加上登陆验证与日志记录处理,该怎样实现?首先想到最简单的就是直接修改源码,在对应模块的对应方法前后添加操作。如果模块很多,你会发现,修改源码不仅非常麻烦、难以维护,而且会使代码显得十分臃肿。

这时候就轮到代理模式上场了,它可以在被调用方法前后加上自己的操作,而不需要更改被调用类的源码,大大地降低了模块之间的耦合性,体现了极大的优势。

静态代理比较简单,上面的简单实例就是静态代理的应用方式,下面介绍动态代理。

二、使用反射生成 JDK 动态代理

动态代理的思路和上述思路一致,下面主要讲解如何实现。

1、动态代理介绍

动态代理是指在运行时动态生成代理类。即,代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。与静态处理类相比,动态类有诸多好处。

  • 不需要为目标对象(RealSubject )写一个形式上完全一样的封装类,假如主题接口(Subject)中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则目标对象和代理类都要修改,不利于系统维护;

  • 使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。

2、动态代理涉及的主要类

在 Java 的 java.lang.reflect 包下提供了一个 Proxy 类和一个 InvocationHandler 接口,内部主要通过反射来实现的。

java.lang.reflect.Proxy: 这是生成代理类的主类,通过 Proxy 类生成的代理类都继承了 Proxy 类。
Proxy提供了用户创建动态代理类和代理对象的静态方法,它是所有动态代理类的父类。

java.lang.reflect.InvocationHandler: 这里称他为 "调用处理器",它是一个接口。
当调用动态代理类中的方法时,将会直接转接到执行自定义的InvocationHandler中的 invoke() 方法。
即我们动态生成的代理类需要完成的具体内容需要自己定义一个类,而这个类必须实现 InvocationHandler 接口,通过重写 invoke() 方法来执行具体内容。


Proxy 提供了如下两个方法来创建动态代理类和动态代理实例。

  1. static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 创建一个动态代理类所对应的 Class 对象。
    第一个参数:类加载器对象(即哪个类加载器来加载这个代理类到 JVM 的方法区)。
    第二个参数:接口(表明你这个代理类需要实现哪些接口),该代理类将实现 interface 所指定的多个接口。

  2. static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 直接创建一个动态代理对象实例。
    第一个参数:类加载器对象。
    第二个参数:该代理对象的实现类实现了 interface 指定的系列接口。
    第三个参数:调用处理器类实例(指定代理类中具体要干什么)。执行代理对象的每个方法时都会被替换执行 InvocationHandler 对象的 invoke() 方法。

对应上述两种方法创建动态代理对象的方式:

//创建一个InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy生成一个动态代理类
Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
 //获取proxyClass类中一个带InvocationHandler参数的构造器
Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
//调用constructor的newInstance方法来创建动态实例
RealSubject real = (RealSubject)constructor.newInstance(handler);
//创建一个InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy直接生成一个动态代理对象
RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);

Proxy 类还有一些静态方法,比如:

InvocationHandler getInvocationHandler(Object proxy) // 获得代理对象对应的调用处理器对象。

Class getProxyClass(ClassLoader loader, Class[] interfaces) // 根据类加载器和实现的接口获得代理类。

InvocationHandler 接口中有方法:

invoke(Object proxy, Method method, Object[] args)
这个函数是在代理对象调用任何一个方法时都会调用的,方法不同会导致第二个参数method不同,第一个参数是代理对象(表示哪个代理对象调用了method方法),第二个参数是 Method 对象(表示哪个方法被调用了),第三个参数是指定调用方法的参数。

3、动态代理模式的简单实现
/**
 * 主题接口
 */
interface Subject{
    void request();
}

/**
 * 目标对象类
 */
class RealSubject implements Subject{
    public void request(){
        System.out.println("====RealSubject Request====");
    }
}
/**
 * 代理类的调用处理器
 */
class ProxyHandler implements InvocationHandler{
    private Subject subject;
    public ProxyHandler(Subject subject){
        this.subject = subject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
        System.out.println("====before====");
       //调用RealSubject中的方法
        Object result = method.invoke(subject, args);
        System.out.println("====after====");
        return result;
    }
}

public class DynamicProxyDemo {
    public static void main(String[] args) {
        //1.创建目标对象
        RealSubject realSubject = new RealSubject();    
        //2.创建调用处理器对象
        ProxyHandler handler = new ProxyHandler(realSubject);    
       //3.动态生成代理对象
        Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
                                                        RealSubject.class.getInterfaces(), handler);   
        //4.通过代理对象调用方法   
        proxySubject.request();    
    }
}


Class动态加载类

1、静态加载类,是编译时刻加载;动态加载类,是运行时刻加载
2、new创建对象:是静态加载类,在编译时刻就需要加载所有的【可能使用到的类】。有一个类有问题(如不存在),都不能通过编译,会报错。通过Class.forName()方法【动态加载类】,可以解决掉这个问题。当用到一个类时,才进行加载。

相关文章

  • Java--反射机制(二)——动态代理

    一、代理模式 定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过...

  • 从头捋了一遍 Java 代理机制,收获颇丰!

    前文提到,动态代理机制使用了反射,Spring 中的 AOP 由于使用了动态代理,所以也相当于使用了反射机制。那么...

  • Java反射机制详解(二)

    本篇文章继续介绍Java反射机制,不同的是侧重于介绍动态代理。动态代理是代理模式中的一种,是通过Java反射机制来...

  • Java反射机制总结(二)

    本篇文章继续介绍Java反射机制,不同的是侧重于介绍动态代理。动态代理是代理模式中的一种,是通过Java反射机制来...

  • Java 动态代理

    前言 关于动态代理 JDK动态代理 CGLIB动态代理 区别 java动态代理是利用反射机制生成一个实现代理接口的...

  • java面试宝典 五分钟了解spring代理 @Transact

    spring代理分为jdk动态代理和cglib代理 jdk动态代理 jdk动态代理是利用反射机制生成一个实现代理接...

  • 动态代理原理及在 Android 中的应用

    一、动态代理简介 1、什么是动态代理? 通过反射机制动态生成代理者对象的一种设计模式。 2、如何区分静态代理和动态...

  • Spring笔记

    1.Spring用到的动态代理分为java动态代理和CGILB动态代理,相同点是都是通过反射机制创建运行时被代理类...

  • java代理模式2_动态代理_jdk

    动态代理 相对于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由java反射机制动...

  • java代理模式2.2_动态代理_cglib

    动态代理 相对于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由java反射机制动...

网友评论

      本文标题:Java--反射机制(二)——动态代理

      本文链接:https://www.haomeiwen.com/subject/jgumqxtx.html