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

来源:互联网 发布:大数据毕业设计本科 编辑:程序博客网 时间:2024/06/08 04:17

导读:
1. 代理模式
2. 静态代理
3. 动态代理
4. 小结

一、代理模式

代理模式所涉及的角色有:

  • 抽象主题角色:声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以使用代理主题。
  • 真实主题角色:定义了代理角色所代表的真实对象。
  • 代理主题(Proxy)角色:代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;代理主题角色提供了一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题;控制对真实主题对象的引用,负责在需要的时候创建真实主题对象;代理角色通常在将客户端调用传递给真实对象的主题之前或者之后,都是执行某个操作,而不是简单地将调用传递给真实对象主题。

代理模式的一个简单的示意性实现,实现的类图如下图所示:
这里写图片描述

二、静态代理

  设计模式中通常提到的代理模式,指的就是静态代理。下面通过懒加载的例子来了解Java中代理模式是如何实现的。

  所谓懒加载,就是延迟加载,核心思路是如果当前对象没有真正地使用某个对象时,就不去创建这个对象,而是在真正需要使用该对象的时候才去创建真正的对象。懒加载的作用主要是降低系统初始化时创建的对象过多而影响系统性能。

1. 抽象主题角色定义
package staticproxy;/* * 抽象主题角色 */public abstract class Subject {    /*     * 声明一个抽象的请求方法     */    public abstract void request();}
2. 真实主题角色定义
package staticproxy;/* * 真正主题角色 */public class RealSubject extends Subject {    /*     * 构造器     */    public RealSubject() { }    /*     * 实现请求方法     */    @Override    public void request() {        System.out.println("call real subject's request");    }}
3. 代理主题角色定义
package staticproxy;/* * 代理主题角色 *  * 静态代理,对具体真实对象直接引用代理角色,代理角色需要有对真实角色的引用,代理做真实角色要做的事。 */public class StaticProxySubject extends Subject {    private RealSubject realSubject;    /*     * 构造函数     */    public StaticProxySubject() {    }    /*     * 实现请求方法     */    @Override    public void request() {        preRequest();        //请求前的操作        //延迟加载RealSubject对象        if (realSubject == null) {            realSubject = new RealSubject();        }        realSubject.request();        postRequest();        //请求后的操作    }    /*     * 请求前的操作     */    public void preRequest() {        System.out.println("something to do before requesting");    }    /*     * 请求后的操作     */    public void postRequest() {        System.out.println("something to do after requesting");    }}
4. 客户端调用
package staticproxy;/* * 客户端调用 */public class Client {    public static void main(String[] args) {        Subject sub = new StaticProxySubject();        sub.request();    //代理者代替真实者做事情    }}
5. 运行结果
something to do before requestingcall real subject's requestsomething to do after requesting

三、动态代理

从静态代理的实现中,当被代理的对象比较多,方法也比较多的时候,代理类的实现就比较繁琐,需要对每一个真实角色创建相应的代理类。动态代理机制的出现可以动态生成代理类,从而使代理模式的实现更加优雅。

1. 动态代理的实现

JDK动态代理中包含一个类和一个接口,即Proxy类和InvocationHandler接口。
(1)Proxy类:Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成类。

public static Object newProxyInstance(ClassLoader                    loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException

参数说明:①ClassLoader loader:类加载器;②Class<?>[ ] interface:得到代理类的全部接口;③InvocationHandler h:得到InvocationHandler接口的子类实例。

(2)InvocationHandler接口:

public interface InvocationHandler {    public Object invoke(Object proxy, Method method, Object[] args)         throws Throwable; }

参数说明:①Object proxy:指被代理的对象;②Method method:要调用的方法;③Object[ ] args:调用的方法所需的参数。

2. 动态代理的创建

使用Proxy类创建动态代理的实例:

Objectsubject= Proxy.newProxyInstance(classloader, interfaces, handler);

等价于使用Proxy的方式:

//生成动态代理的对象Class<?> c = Proxy.getProxyClass(classloader, interfaces);//通过反射获取生成的动态代理类的构造方法Constructor<?> ct = c.getConstructor(new Class[] {InvocationHandler.class});//通过构造函数对象生成动态代理类的实例Subject subject = (Subject)ct.newInstance(new Object[]{handler});
3. JDK动态代理实例

(1)抽象主题角色

package dynamicproxy;/* * 抽象主题角色 * 抽象类应改为接口 */public interface Subject {    /*     * 声明一个抽象的请求方法     */    void request();}

(2)真实主题角色

package dynamicproxy;/* * 真正主题角色 */public class RealSubject implements Subject {    public RealSubject() { }    /*     * 实现请求方法     */    @Override    public void request() {        System.out.println("call real subject's request");    }}

(3)动态代理主题角色定义

package dynamicproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/* * 动态代理主题角色 */public class DynamicProxySubject implements InvocationHandler {    private Object obj;    public DynamicProxySubject(Object obj) {        this.obj = obj;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("before call: " + method);        method.invoke(obj, args);        System.out.println("after call: " + method);        return null;    }}

(4)客户端调用

package dynamicproxy;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;public class Client {    public static void main(String[] args) throws Exception {        RealSubject realSubject = new RealSubject();        InvocationHandler handler = new DynamicProxySubject(realSubject);        Class<?> cls = realSubject.getClass();       //创建动态代理实例的方式一        //生成动态代理的对象        Class<?> c = Proxy.getProxyClass(cls.getClassLoader(), cls.getInterfaces());        //通过反射获取生成的动态代理类的构造方法        Constructor<?> ct = c.getConstructor(new Class[] {InvocationHandler.class});        //通过构造函数对象生成动态代理类的实例        Subject subject = (Subject)ct.newInstance(new Object[]{handler});        //创建动态代理实例的方式二        Subject subject2 = (Subject)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler);         subject.request();    }}

(5)运行结果

before call: public abstract void dynamicproxy.Subject.request()call real subject's requestafter call: public abstract void dynamicproxy.Subject.request()

四、小结

  Java中的动态代理模式通过动态字节码生成、反射机制等技术,使得开发人员更为优雅地实现代理模式,动态代理在很多技术框架中都有使用,如Spring中的AOP、一些RPC框架等,出了Java自带的动态代理外,也可以使用CGLIB等类似的库。

【参考资料】

[1] hduhans, Java反射与动态代理. http://www.cnblogs.com/hanganglin/p/4485999.html
[2] JR’s Blog, Java 中的动态代理与反射. http://blog.jrwang.me/2015/java-dynamic-proxy-and%20java-reflection/
[3] 阎宏, 《java与模式》

原创粉丝点击