浅谈java设计模式 – 代理模式

浅谈java设计模式 – 代理模式
个人理解的代理模式,动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。即不知道真实对象是谁,此时代理对象可以在客户端和真实对象之间起到中介的作用.
  • 代理对象完全包含真实对象, 客户端使用的都是代理对象的方法,
  • 代理模式的职责: 把不属于真实对象的操作从其身上分开 — 责任分离
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
1.1 JDK 的动态代理 要求真实对象必须有接口.
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。

直接上代码:

/**
 * 创建一个代理对象
 * @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的原理,简单说就是直接继承了需要被增强(代理)的类,并重写其中的关键方法。

/**
 * 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);
    
    }

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

/**
 * 定义了唱歌和跳舞的接口
 * @author LzM
 */
public interface Iperson {
    
    /**
     * 唱歌 
     * @param name 歌名
     * @return 唱歌的人
     */
    String sing(String name);
    
    /**
     * 跳舞
     * @param name 舞名
     * @return 跳舞者
     */
    String dance( String name );        
    //...   
}

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

/**
 * 实现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 "跳累了。。歇会";
    }
}

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

/**
 * 这个类用来生成张三的代理人。 即代理类
 * @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 ;
    }
}

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

/**
 * 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;
    }

}

测试一下:

/**
 * 入口函数
 * @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万... 
给钱中...
张三,开始唱不将就
*/
    }

}

 

Comments

No comments yet. Why don’t you start the discussion?

发表回复

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