Java静态代理和动态代理

Java代理模式主要有两种:静态代理和动态代理

我们先来说说代理模式一般涉及到的角色有:

— 抽象角色:声明真实对象和代理对象的共同接口

— 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装

— 真实角色:代理角色所代表的的真实对象,是我们最终要引用的对象

静态代理:

proxy1

 

先定义一个抽象类(或者接口):

1
2
3
public abstract class Subject {
    public abstract void request();
}

定义一个继承该类的实现类:

1
2
3
4
5
6
7
8
9
public class RealSubject extends Subject{

    @Override
    public void request() {
        // TODO Auto-generated method stub
        System.out.println("From real subject!");
    }
   
}

定义一个静态代理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ProxySubject extends Subject{

    private RealSubject realSubject;
   
    @Override
    public void request() {
        this.preRequest();
        // TODO Auto-generated method stub
        if(null==realSubject){
            realSubject = new RealSubject();
        }
       
        realSubject.request();
        this.postRequest();
    }
   
    private void preRequest(){
        System.out.println("pre request");
    }
   
    private void postRequest(){
        System.out.println("post request");
    }
   
}

一个测试类:

1
2
3
4
5
6
7
public class Client {
    public static void main(String[] args) {
        Subject subject = new ProxySubject();
       
        subject.request();
    }
}

运行结果:

1
2
3
pre request
From real subject!
post request

可以看出静态代理类有一个很不爽的缺点:当如果接口加一个方法,所有的实现类和代理类里都需要做个实现。这就增加了代码的复杂度。动态代理就可以避免这个缺点。

动态代理:

随着Proxy的流行,Sun把它纳入到JDK1.3实现了Java的动态代理。动态代理和普通的代理模式的区别,就是动态代理中的代理类是由java.lang.reflect.Proxy类在运行期时根据接口定义,采用Java反射功能动态生成的。和java.lang.reflect.InvocationHandler结合,可以加强现有类的方法实现。如图2,图中的自定义Handler实现InvocationHandler接口,自定义Handler实例化时,将实现类传入自定义Handler对象。自定义Handler需要实现invoke方法,该方法可以使用Java反射调用实现类的实现的方法,同时当然可以实现其他功能,例如在调用实现类方法前后加入Log。而Proxy类根据Handler和需要代理的接口动态生成一个接口实现类的对象。当用户调用这个动态生成的实现类时,实际上是调用了自定义Handler的invoke方法。

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
(1):Interface InvocationHandler:该接口中仅定义了一个方法
public object invoke(Object obj, Mehtod method, Object[] args)
在实际使用时,第一个参数obj一般是指代理类,mehtod是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
(2):Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容
protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。

static Class getProxyClass(ClassLoader loader,Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部借口的数组。

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当做被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。

所谓Dynamic Proxy是这样的一种class:她是运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣传它实现了这些interface。你当然可以把该class的实例当做这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

proxy2

 

定义一个接口:

1
2
3
public interface Subject {
    public void request();
}

定义一个实现该接口的实现类:

1
2
3
4
5
6
7
8
9
public class RealSubject implements Subject{

    @Override
    public void request() {
        // TODO Auto-generated method stub
        System.out.println("real Subject");
    }
   
}

定义一个动态代理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ReadyInvocationHandler implements InvocationHandler {

    private Object sub;
   
    public ReadyInvocationHandler(Object obj){
        this.sub = obj;
    }
   
    //拦截关联的这个实现类的方法被调用时将被执行  
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("before calling "+method);
       
        method.invoke(sub, args);
       
        System.out.println("after calling "+method);
        return null;
    }

}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Client {
    public static void main(String[] args) {
        //先将Subject实现类实例化,也就是得到Subject接口的一个实例对象  
        Subject realSubject = new RealSubject();
       
        /**
         * 得到RealSubject这个类的一个代理类,同时为代理类绑定了一个处理类ReadyInvocationHandler。
         * 听着很绕口,其实就是每次调用RealSubject这个子类的request方法时,
         * 不是realSubject这个RealSubject类的实例去调用,
         * 而是这个RealSubject的代理类ReadyInvocationHandler去调用它自己的invoke方法,
         * 这个invoke方法里呢可以调用realSubject这个实例的request方法
         */

        /**
         * 在java种怎样实现动态代理呢
         * 第一步,我们要有一个接口,还要有一个接口的实现类,而这个实现类呢就是我们要代理的对象,
         * 所谓代理呢也就是在调用实现类的方法时,可以在方法执行前后做额外的工作,这个就是代理。
         * 第二步,我们要自己写一个在要代理类的方法执行时,能够做额外工作的类,而这个类必须继承InvocationHandler接口,
         * 为什么要继承它呢?因为代理类的实例在调用实现类的方法的时候,不会调真正的实现类的这个方法,
         * 而是转而调用这个类的invoke方法(继承时必须实现的方法),在这个方法中你可以调用真正的实现类的这个方法。
         * 第三步,在要用代理类的实例去调用实现类的方法的时候,写出下面代码。
         */
 
        InvocationHandler headler = new ReadyInvocationHandler(realSubject);
       
        Subject subject = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), headler);
       
        subject.request();
         /**
         * 这里要解释下中部那段长长的代码的意思,以及具体做了哪些工作?
         * 第一,根据realSubject.getClass().getClassLoader()这个要代理类的类加载器和
         * realSubject.getClass().getInterfaces()要代理类所实现的所有的接口
         * 作为参数调用Proxy.getProxyClass(ClassLoader loader, Class<?>... interfaces)
         * 的方法返回代理类的java.lang.Class对象,也就是得到了java动态生成的代理类$Proxy0的Class对象。
         * 同时,java还让这个动态生成的$Proxy0类实现了要代理类的实现的所有接口,并继承了Proxy接口。
         * 第二,实例化这个动态生成的$Proxy0类的一个实例,实例化代理类的构造函数为Proxy(InvocationHandler h),
         * 也就是说要实例化这个动态生成的$Proxy0类,必须给它一个InvocationHandler参数,也就是我们自己实现的用来在代理类
         * 方法执行前后做额外工作的类ReadyInvocationHandler。
         * 这段代码Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),headler)
         * 得到的其实是一个类名叫$Proxy0 extends Proxy implements Subject的类。
         * 第三,将这个$Proxy0类强制转型成Subject类型,调用request方法。
         */
 
       
        System.out.println(subject.getClass());
    }
}

除非注明,饮水思源博客文章均为原创,转载请以链接形式标明本文地址

本文地址:http://www.alonemonkey.com/java-proxy.html

本文链接:http://www.alonemonkey.com/java-proxy.html