Java设计模式 – 代理模式

Java设计模式 – 代理模式

代理模式

个人理解的代理模式,动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。即不知道真实对象是谁,此时代理对象可以在客户端和真实对象之间起到中介的作用.

  • 代理对象完全包含真实对象, 客户端使用的都是代理对象的方法,
  • 代理模式的职责: 把不属于真实对象的操作从其身上分开 — 责任分离

代理 ( Proxy ) 是一种设计模式,提供了对目标对象另外的访问方式。即通过代理对象访问目标对象。

这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

JDK 的动态代理 要求真实对象必须有接口

1
2
3
4
5
6
7
8
9
10
static InvocationHandler getInvocationHandler(Object proxy)
//返回指定代理实例的调用处理程序。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
//返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
static boolean isProxyClass(Class<?> cl)
//当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。
static Object newProxyInstance( ClassLoader loader ,  Class<?>[] interfaces ,  InvocationHandler h )
//参数1: 类加载器,一般跟上真实对象的类加载器
//参数2: 真实对象所实现的接口;  .getClass().getInterfaces();
//参数3: 如何做增强的对象, 即动态代理类的处理器; 此类需要实现InvocationHandler接口;

一个对象数组包含的值情况代理实例上在方法调用中传递,或null如果接口方法不包含任何参数。原始类型的参数被包装在适当的原始包装类的实例中,例如java.lang。整数或java.lang.Boolean。

直接上代码:

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
45
46
47
/**
 * 创建一个代理对象
 * @author LzM
 */

public class SomeClassProxy implements InvocationHandler {

    private Object target; //要被代理的真实目标类
    public SomeClassProxy(  Object target ){
        this.target = target;
    }

    /**
     * 创建一个代理对象
    * 注意返回的时候要用接口变量. 否则可能会转换异常
     * @param <T>
     * @return ProxyObject
     */

    public <T> T getProxyObject(){
        return (T) Proxy.newProxyInstance( target.getClass().getClassLoader(), //真实对象的类加载器
                        target.getClass().getInterfaces(),  //真实对象所实现的接口
                        this );    //动态代理类的处理器,需实现InvocationHandler接口
    }
   
    /**
     * 为真实对象做增强的具体操作.这里自定义,可以写多个类的处理器也可以写一个,这里为了演示只写一个
     */

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy类 "+proxy.getClass());
        System.out.println("方法名 "+method.getName());
        System.out.println("参数列表 "+Arrays.toString(args));
        System.out.println("-------------------------------");
       
        System.out.println("开启增强: balabalaba");
        try {
            //调用 真实对象的方法
            method.invoke(target, args);           
        } catch (Exception e) {
            System.err.println("[增强] 出错了!回滚..");
            return 0 ;
        }finally{
            System.out.println("结束..");
        }
        return null;
    }

}

基于 CGLIB动态代理 (可以没有接口)

CGLIB的原理,简单说就是直接继承了需要被增强 (代理) 的类,并重写其中的关键方法。

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
 * CGLIB - 创建一个代理对象
 * @author LzM
 */

public class SomeClassProxy_cg implements org.springframework.cglib.proxy.InvocationHandler {

    private Object target; //要被代理的真实目标类
    public SomeClassProxy_cg(  Object target ){
        this.target = target;
    }

    /**
     * 创建一个代理对象
     * @param <T>
     * @return ProxyObject
     */

    public Object getProxyObject(){
        Enhancer enhancer = new Enhancer();
        //继承那个类去做增强(代理)
        enhancer.setSuperclass(target.getClass());
        //设置增强处理对象(这里是本身,其他的话就用别的类)
        enhancer.setCallback(this);
        //返回创建的代理对象
        return enhancer.create();  
    }
   
    /**
     * 为真实对象做增强的具体操作
     */

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy类 "+proxy.getClass());
        System.out.println("方法名 "+method.getName());
        System.out.println("参数列表 "+Arrays.toString(args));
        System.out.println("-------------------------------");
       
        System.out.println("开启增强: 拿纸笔");
        try {
            //调用 真实对象的方法
            method.invoke(target, args);           
        } catch (Exception e) {
            System.err.println("[增强] 出错了!回滚..");
            return 0 ;
        }finally{
            System.out.println("结束..");
        }
        return null;
     }

}
---------- 测试 -------------
public static void main(String[] args) {
    ZooIMPL_cg zooIMPL = new ZooIMPL_cg();
    SomeClassProxy_cg proxy = new SomeClassProxy_cg(zooIMPL);
    Zoo_cg proxyObject = (Zoo_cg) proxy.getProxyObject();
   
    proxyObject.getAllannimo(11);
   
    }

举个栗子:这是一个可以唱歌和跳舞的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * 定义了唱歌和跳舞的接口
 * @author LzM
 */

public interface Iperson {
   
    /**
     * 唱歌
     * @param name 歌名
     * @return 唱歌的人
     */

    String sing(String name);
   
    /**
     * 跳舞
     * @param name 舞名
     * @return 跳舞者
     */

    String dance( String name );       
}

这是一个具体人,实现了上面的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * 实现person接口的具体类
 * @author LzM
 */

public class ZhangSan implements Iperson{

    @Override
    public String sing(String name) {
        System.out.println("张三,开始唱" + name);
       
        return "唱完了。谢谢大家";
    }

    @Override
    public String dance(String name) {
        System.out.println("张三跳舞了。舞蹈名:" + name);
        return "跳累了。。歇会";
    }
}

下面开始给张三一个代理人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * 这个类用来生成张三的代理人。 即代理类
 * @author LzM
 */

public class ZhangSanProxy {
   
    //要代理的目标对象 :张三
    private ZhangSan zs = new ZhangSan();
   
    public Iperson getProxy(){
        Iperson p = (Iperson) Proxy.newProxyInstance (
        ZhangSanProxy.class.getClassLoader(),
        zs.getClass().getInterfaces(),
        new personHandle( zs ));
        return p ;
    }
}

然后给代理人一个处理方法,即不能直接找张三,得先给代理人付钱。才能找张三

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
45
46
47
48
49
50
51
/**
 * InvocationHandler接口的实现类
 * 用来指明代理对象(即newProxyInstance的第一个参数)要做什么事情。
 * 细分的话,在调用前做什么,调用后做什么。
 * @author LzM
 */

public class personHandle implements InvocationHandler{
   
    private Object obj = null;
   
    public personHandle( Object o ){
        this.obj = o;
       
    }

    /**
     * 在代理实例上处理方法调用并返回结果。
     * 在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
     */

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //下面是测试打印:
        System.out.println("[代理类]"+proxy.getClass().getName());
        System.out.println("[方法名]"+method.getName());
        System.out.println("[参数]"+Arrays.toString(args)+"\n\n");
        //-------------------------------------------------------
        Object result = null;//被代理的类型为Object基类   
       
        /**
         * 这里就可以进行所谓的AOP编程了
         * 在调用被代理类的方法之前前,先执行自定义功能处理下
         */

       
        //下面是自定义功能,经纪人要加钱。。
        if( method.getName().equals("sing") ){
            System.out.println("我是张三经纪人,你要他唱歌先给我10万... ");
            System.out.println("给钱中...");
            //下面才调用张三的唱歌
            return method.invoke( obj, args );
        }
        if( method.getName().equals("dance") ){
            System.out.println("我是张三经纪人,你要他跳舞啊,先给我50万... ");
            System.out.println("给钱中...");
            //下面才调用张三跳舞
            return method.invoke( obj , args );
        }
       
        return result;
    }

}

测试一下:

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
/**
 * 入口函数
 * @author LzM
 */

public class TsetMain {
   
    public static void main(String[] args) {
       
        ZhangSanProxy proxy = new ZhangSanProxy();
        Iperson p = proxy.getProxy();
        p.dance("小天鹅");
        p.sing("不将就");
 
/*
输出:
[代理类]com.sun.proxy.$Proxy0
[方法名]dance
[参数][小天鹅]
我是张三经纪人,你要他跳舞啊,先给我50万...
给钱中...
张三跳舞了。舞蹈名:小天鹅


[代理类]com.sun.proxy.$Proxy0
[方法名]sing
[参数][不将就]
我是张三经纪人,你要他唱歌先给我10万...
给钱中...
张三,开始唱不将就
*/

    }

}

评论

目前还没有评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注