设计模式-代理模式

用java的动态代理类实现代理模式。我们知道代理对象实现对被代理对象的访问,客户端通过对代理实例达到对代理实例(也叫真实对象)的访问。那么在对真实对象访问前我们可以做更多的控制,参数检查,鉴权方面的操作。所以非常方便,但是有一个问题就是,如果每一个真实实例所代表的接口都必须有一个代理类的话当然可以,这样势必造成了问题,有很多的代理类和被代理类。

而java动态代理框架呢,正好可以动态的在运行时候生成代理类,听起来好像很不可思议。我都没有写一个java源文件,怎么动态生成了一个新的类出来了呢。
动态代理就是这篇博文所要说的技术点。

先上我写的一个例程

真实对象实现的接口

package proxy;
/**
 * 代理类和真实类都要遵守的规则
 * @author cindy
 */
public interface Subject {
    public void request(String code);
    public void response();
}

真实实例,也就是被代理的类

package proxy;

/**
 * 真实对象
 * 
 * @author cindy
 *
 */
public class RealSubject implements Subject {

    public RealSubject() {
        // TODO Auto-generated constructor stub
    }

    @Override
    public void request(String code) {

        System.out.println("传进来的参数code是:" + code);
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "i'm is realsubject object ";
    }

    @Override
    public void response() {
        // TODO Auto-generated method stub
        System.out.println("bye ,i 'm is realsubject");
    }

}

在实现动态代理类动态生成代理类时候,必须创建一个实现了接口InvocationHandler的调用处理器类,这个接口的作用是当我生成的代理类实例调用被代理对策的接口方法时有个invoke方法通过反射技术调用了真实实例的方法。我们所要新增的控制逻辑都要新增到invoke方法里面。比如我的实现

package proxy;

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

//InvocationHandler这是调用处理器接口,它自定义了一个 invoke 方法,
//代理类实例调用接口Realsub的方法时候会自动转化成调用invoke方法,然后invoke方法
//通过method参数发射调用接口的方法,
//realsub代表接口,method代表方法,args代表method方法接受的参数列表。
//用于集中处理在动态代理类实例上的方法调用,通常在该方法中实现对委托类(真实对象)的代理访问。
public class DynamicProxy implements InvocationHandler {

    private Object Realsub;

    public DynamicProxy(Object sub) {
        this.Realsub = sub;
        // TODO Auto-generated constructor stub
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        Object result = null;
        // System.out.println("proxy: " + proxy);
        System.out.println("call before :  " + method);
        //通过这个反射方法调用了真实对象的subject接口中的方法
        result = method.invoke(Realsub, args);
        System.out.println("call after :   " + method);

        return result;
    }

}

上面中我们把真实实例的引用通过构造方法注入。然后通过invoke方法里面的method.invoke(Realsub, args);方法实现反射调用。

最后是写一个测试程序

package proxy.run;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;

import proxy.DynamicProxy;
import proxy.RealSubject;
import proxy.Subject;

public class RunTest {

    public RunTest() {
        // TODO Auto-generated constructor stub
    }

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
            IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        // 真实对象
        RealSubject realSubject = new RealSubject();
        // 创建代理对象调用处理器 1
        InvocationHandler dProxyHandler = new DynamicProxy(realSubject);
        Class realcls = realSubject.getClass();// 获取真实类的class实例

        // 返回一个实现一组接口和指定类装载器的动态代理类的class实例,这里就是说要实现一个
        // 实现了真实对象接口的代理类,并返回其Class实例
        // 这是一种新的类定义方式,没有写类代码,这个类都是运行是临时组建的。
        // 参数指明了这个类的装载器,类需要实现的接口,有了Class类实例,我们就有了创建实例的模板。
        // 2
        Class proxyClass = Proxy.getProxyClass(realcls.getClassLoader(), realcls.getInterfaces());

        // 根据proxyClass创建一个代理类构造器,参数申明的是代理类构造器的参数是这种类型。用于等下将我们上面创建的代理类处理器传递进去
        // 3
        Constructor constructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class });
        // 根据构造器创建一个代理类实例subject,newInstance方法的参数数组,当constructor方法调用时候以
        // 参数传递过去。
        // 代理类实例通过dProxyHandler完成反射调用真正对象的接口方法
        // 4
        Subject subject = (Subject) constructor.newInstance(new Object[] { dProxyHandler });
        System.out.print("proxy: ");
        System.out.println(subject instanceof Proxy);
        System.out.println(Proxy.isProxyClass(subject.getClass()));
        System.out.println(Proxy.getInvocationHandler(subject).toString());
        subject.request("vincent");
        subject.response();
        subject.toString();
        // Proxy.newProxyInstance(loader, interfaces, h)

    }
}

输出

proxy: true
true
proxy.DynamicProxy@4aa298b7
call before :  public abstract void proxy.Subject.request(java.lang.String)
传进来的参数code是:vincent
call after :   public abstract void proxy.Subject.request(java.lang.String)
call before :  public abstract void proxy.Subject.response()
bye ,i 'm is realsubject
call after :   public abstract void proxy.Subject.response()

从输出来的结果来判断,我们生成的subject实例是一个代理类,而且是Proxy的子类。调用subject接口的方法时候也能可以正确的调用到真实对象的接口方法。

解读

ava.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
清单 1. Proxy 的静态方法

// 方法 1: 该方法用于获取指定代理实例所关联的调用处理器
//我们可以Proxy.getInvocationHandler(subject);调用获取调用处理器实例
static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象,这是创建动态代理类的关键
static Class getProxyClass(ClassLoader loader, Class[] interfaces)

// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
//Proxy.isProxyClass(subject.getClass())
static boolean isProxyClass(Class cl)

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例的简便方法
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
InvocationHandler h)
比如这个一条语句就创建了一个代理类实例

Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(), dProxyHandler);

java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。

清单 2. InvocationHandler 的核心方法

// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
Object invoke(Object proxy, Method method, Object[] args)
每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象(参见 Proxy 静态方法 4 的第三个参数)。

java.lang.ClassLoader:这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。
每次生成动态代理类对象时都需要指定一个类装载器对象(参见 Proxy 静态方法 4 的第一个参数)

创建代理类主要有四个步骤

1、通过实现 InvocationHandler 接口创建自己的调用处理器;
2、通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类的Class类实例;
3、通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
4、 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值