Java设计模式 – 代理模式

Java设计模式 – 代理模式

代理模式

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

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

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

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

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

}

1 Comment

  1. Have you ever thought about creating an e-book or guest authoring on other sites?

    I have a blog based on the same topics you discuss and
    would really like to have you share some stories/information. I know my visitors would value your
    work. If you’re even remotely interested, feel free to send me an e-mail.

发表回复

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