Java中的代理机制

13 篇文章 0 订阅

说起代理,类比于生活中的例子,就是中介。对于目标类,有一个代理对象去代替目标类执行方法,但实际上还是调用了目标类的方法。
代理模式涉及到的角色:

1.抽象角色:声明真实对象和代理对象的共同接口
2.代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象;代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。除此之外,代理对象可以在执行真实对象操作时,附加其他操作,相当于对真实对象进行封装
3.真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

至于Java中的静态代理实现,代理类和目标类都需要实现同一个接口。写个小Demo

按照上述的方法是用代理模式,那么真实角色必须是事先存在的,并将其作为代理对象的内部属性。在实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先不知道真实角色,该如何使用代理?所以引入动态代理来解决此类问题。
动态代理原理见Java帝国之动态原理,讲解的通俗易懂
写个Demo
接口:

public interface Subject1 {
    public void rent();
    public void sayHello(String name);
}

目标类:

public class RealSubject1 implements Subject1{
    public void rent(){
        System.out.println("I want to rent my house.");
    }
    public void sayHello(String name){
        System.out.println("Hello,"+name);
    }
}

代理类:

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

/**
 * 代理处理器类
 * 1.必需实现InvocationHandler接口
 * 2.内部维护真实对象类的引用,通过构造函数传递
 */
public class DynamicProxy implements InvocationHandler {
    //代理的真实对象,可以传入任何类型的引用
    private Object tar;
    //通过构造函数传入真实对象的引用
    public DynamicProxy(Object tar){
        this.tar = tar;
    }
    /**
     *
     * @param proxy  指向动态生成的代理对象(class $Proxy0),一般不用
     * @param method 被代理的方法
     * @param args   方法的参数
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object o = null;
        //在代理真实对象前,我们可以添加自己的操作
        System.out.println("代理真实对象之前...");
        System.out.println("Method:"+method);

        //当生成的代理对象去调用真实对象的方法时,其会自动跳转到代理对象关联的handler对象的invoke方法来进行调用
        o = method.invoke(tar,args);

        //在代理真实对象后,我们也可以添加自己的操作
        System.out.println("代理真实对象结束...");
        return o;
    }
}

动态代理类的使用:

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

/**
 *  1.首先生成真实的对象,通过构造函数传入动态代理类
 *  2.通过代理类Proxy的静态方法newProxyInstance动态地生成代理对象
 *  3.通过动态生成的对象执行真实对象的方法
 */
public class Client {
    public static void main(String[] args){
        //代理的真实对象
        Subject1 real = new RealSubject1();
        // 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new DynamicProxy(real);
        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        Subject1 sub = (Subject1) Proxy.newProxyInstance(handler.getClass().getClassLoader(),real.getClass().getInterfaces(),handler);
        System.out.println(sub.getClass().getName());
        sub.rent();
        sub.sayHello("Dream");
    }
}

运行结果:

com.sun.proxy.$Proxy0
代理真实对象之前...
Method:public abstract void com.itdream.Dynamic.Subject1.rent()
I want to rent my house.
代理真实对象结束...
代理真实对象之前...
Method:public abstract void com.itdream.Dynamic.Subject1.sayHello(java.lang.String)
Hello,Dream
代理真实对象结束...

通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
起初认为返回的这个代理对象会是Subject类型的对象,或者是InvocationHandler的对象,结果却不是,首先我们解释一下为什么我们这里可以将其转化为Subject类型的对象?原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了。

所谓动态代理类是这样一种类,它是在运行时生成的类,在生成它时,你必须提供一组接口给它。你当然可以把该类的实例当作这些interface中的任何一个来用。当然,这个动态代理类其实就是一个代理,它不会替你作实质性的工作,在生成它的实例时你必须提供一个hander,由它接管实际的工作。

被代理的对象(RealSubject)可以在运行时动态地改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态地改变,从而实现了非常灵活的动态代理关系。
动态代理的创建步骤:
1.创建一个实现接口InvocationHandler的类,必须实现invoke方法
2.创建被代理的类及接口
3.通过Proxy类的静态方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)创建一个代理
4.通过代理调用方法

/**
 * 定义静态工厂方法,可以通过类的调用直接生成代理对象
 * 可以实现JDK中已有类的代理类,如下是Vector类的代理类
 */
public class VectorProxy implements InvocationHandler {
    private Object proxyObj;
    public VectorProxy(Object obj){
        this.proxyObj = obj;
    }
    /**
     * @param obj  被代理的对象
     * @return  返回动态生成的代理对象
     */
    public static Object factory(Object obj){
        Class<?> clazz = obj.getClass();
        //classLoader可以是任何类的,只要是当前应用中存在的或者自定义的均可以,因为某个类的classLoader可以加载很多类,不止包括自身
        //如下换成String.class.getClassLoader()也是可以的
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),new VectorProxy(obj));
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before calling:"+method);
        if(null != args){
            for(int i=0;i<args.length;i++){
                System.out.println(args[i]);
            }
        }
        System.out.println("After calling:"+method);
        return method.invoke(proxyObj,args);
    }
    public static void main(String[] args){
        //传递的是被代理的对象
        List list = (List) VectorProxy.factory(new Vector());
        //add方法本身没有返回值
        list.add("Dream");
        //list.toString方法有返回值,最终输出list集合
        System.out.println(list);
    }
}

传入的真实对象可以是多种,在代理处理器中定义set方法,可以更换指向真实对象的引用,那么该处理器就可以代理去处理任何真实对象的方法了。
更详细的Java动态代理讲解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值