Java设计模式(1):代理模式

一、模式定义(What)

1.代理模式(Proxy):

为其对象提供一种代理以控制对这个对象的访问;是一种对象结构型模式;也称:"委托模式",是一种基本设计技巧。

(代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问)

2.模式解读:

(1).一个原对象对应一个代理;

(2).其他对象不会直接访问原对象,而是通过访问代理对象;

(3).代理对象需要访问原对象,但是原对象无法感知代理对象的存在。 

   

3.      组成结构:

(1).Subject 抽象主题类:该类是一个抽象类或者是一个接口,是一个普通的业务类型定义;(定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法)

(2).RealSubject  具体主题类:被委托、被代理的对象,是业务逻辑的具体执行者;(真正实现业务逻辑的类)

(3).Proxy 代理主题类:也称委托类、代理类。负责对具体类的应用,把抽象主题类中定义的所有方法委托给具体主题类实现,并在具体主题类处理完毕前、后做预处理和善后工作;(用来代理和封装真实主题)

(4).IOtherProxy 类:该类非必需,主要是对具体主题类的功能增强;

(5).Main:客户端,使用代理类和主题接口完成一些工作。

 

二、必要性(Why)

当一个客户端对象不愿意或者不能够直接引用一个目标对象时,此时使用一个代理对象实现间接的引用。代理对象可以在目标对象与客户端对象之间起到中介的作用。一方面,代理对象可以去掉不想让客户端看到的某些目标对象的功能,另一方面,代理对象可以对目标对象进行功能的增强。

总而言之,我们可以通过引入一个新的对象(代理对象)来间接访问一个对象的部分或者全部的功能,同时还可以对原有特定功能进行增强。

三、实现(How)

One:基本代理模式实现:

1.抽象主题类(Subject):

package com.designpattern.proxy;

/**   
 * @ClassName:  Subject   
 * @Description:代理模式的抽象主题类
 * @author: wanjinYoung 
 * @date:   2018年8月15日 下午2:34:40   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public interface Subject {
	//接口中的方法定义
	public void request();

}

2.具体主题类:

package com.designpattern.proxy;

/**   
 * @ClassName:  RealSubject   
 * @Description:真实主题类
 * @author: wanjinYoung 
 * @date:   2018年8月15日 下午2:20:03   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class RealSubject implements Subject{
	
	//实现接口的方法
	public void request() {
		// TODO Auto-generated method stub
		System.out.println("Do real business!");
	}
	
	
}

3.代理类:

package com.designpattern.proxy;

/**   
 * @ClassName:  Proxy   
 * @Description:代理类
 * @author: wanjinYoung 
 * @date:   2018年8月15日 下午2:19:36   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class Proxy implements Subject{
	private Subject realSubject;
	private long start;
	private long end;
	
	/**   
	 * @Title:  Proxy   
	 * @Description:构造函数
	 * @param:    
	 * @throws   
	 */  
	public Proxy() {
		super();
		// TODO Auto-generated constructor stub
		//经典的代理模式是在构造函数中实例化具体主题类
		//realSubject = new RealSubject();		
	}
	
	/**   
	 * Title: request  
	 * Description:重写接口Subject的方法,进行真实主题类方法的调用以及新功能的添加     
	 * @see com.designpattern.proxy.Subject#request()   
	 */  
	@Override
	public void request() {
		//进行功能增强1
		this.doBefore();
		
		//通过"虚拟代理"实现延迟加载
		//即:只有真正执行了操作时,才实例化对象
		if (realSubject == null) {
			realSubject = new RealSubject();
		}
		
		//执行真实对象的操作
		realSubject.request();
		
		//进行功能增强2
		this.doAfter();
	}

	/**   
	 * @Title: doBefore   
	 * @Description: 记录执行起始的时间 
	 * @param:       
	 * @return: void      
	 * @throws   
	 */  
	private void doBefore() {
		// TODO Auto-generated method stub
		start = System.nanoTime();
		System.out.println("Do someing before!");
		
	}
	
	/**   
	 * @Title: doAfter   
	 * @Description: 记录执行结束的时间
	 * @param:       
	 * @return: void      
	 * @throws   
	 */  
	private void doAfter() {
		// TODO Auto-generated method stub
		end = System.nanoTime();
		System.out.println("Do something after!");
	}
	
	/**   
	 * @Title: getCost   
	 * @Description:计算花费的时间
	 * @param: @return      
	 * @return: long      
	 * @throws   
	 */  
	public long getCost() {
		return end-start;
	}
	
	
	
	
	
	
	
}

4.客户端测试类:

package com.designpattern.proxy;

/**   
 * @ClassName:  Client   
 * @Description:测试类,用于测试结果
 * @author: wanjinYoung 
 * @date:   2018年8月15日 下午2:55:31   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class Client {
	public static void main(String[] args) {
		//客户端对象只需要知道代理类即可,无需知道具体主题类
		Subject proxy = new  Proxy();
		proxy.request();
		long costTime = ((Proxy)proxy).getCost();
		System.out.println("Cost time:"+costTime+"ns");
		
	}

}

5.运行结果:

6.结果分析:通过以上代码可以看出,我们可以在不修改具体主题类的前提下,对目标对象进行功能的扩展(在代理类中实现扩展功能,doBefore()和doAfter()),这种代理的对象时明确的称之为静态代理。这种模式相对简单但是功能有限以及缺陷很大,这种模式下我们要代理一个目标类时,就需要开发一个代理类,倘若我们需要代理多个目标类时则需要开发多个目标类。这种模式会造成类冗余。

Two:动态代理模式实现:

1.原理解释:顾名思义,动态代理就是指在运行的过程中动态的生成代理类,即:代理类的字节码将在运行时生成,并且载入到当前代理的ClassLoader中。

与静态代理类相比,动态代理类更加的实用。首先,不需要为真实主题类写一个形式上完全一样的封装类 ,假如主题接口中的方法很多,为每个接口写一个代理方法也很麻烦。如果主题接口有改变,则真实主题类和代理类都需要秀改,这样不利于系统的维护;其次,使用动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而提升了系统的灵活性。

2.抽象主题类:

package com.designpattern.dynamicproxy;

/**   
 * @ClassName:  Subject   
 * @Description:抽象主题类
 * @author: wanjinYoung 
 * @date:   2018年8月15日 下午5:12:20   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public interface Subject {
	public void request();
}

3.具体主题类

package com.designpattern.dynamicproxy;

/**   
 * @ClassName:  RealSubject   
 * @Description:真实主题类
 * @author: wanjinYoung 
 * @date:   2018年8月15日 下午5:13:59   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class RealSubject implements  Subject{

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

4.通知接口

package com.designpattern.dynamicproxy;

/**   
 * @ClassName:  IAdvice   
 * @Description:通知接口
 * @author: wanjinYoung 
 * @date:   2018年8月15日 下午5:16:44   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public interface IAdvice {
	public void exec();
}

5.具体通知类

package com.designpattern.dynamicproxy;

/**   
 * @ClassName:  BeforeAdvice   
 * @Description:具体通知类
 * @author: wanjinYoung 
 * @date:   2018年8月15日 下午5:17:40   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class BeforeAdvice implements IAdvice {

	@Override
	public void exec() {
		// TODO Auto-generated method stub
		System.out.println("Before Advice is Exectuded!");
	}
	
}

6.动态代理的Handler类

package com.designpattern.dynamicproxy;

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

/**   
 * @ClassName:  MyInvocationHandler   
 * @Description:动态代理的Handler类
 * @author: wanjinYoung 
 * @date:   2018年8月15日 下午5:23:03   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class MyInvocationHandler  implements InvocationHandler{
	
	//被代理的对象
	private Object target = null;
	//构造方法
	public MyInvocationHandler(Object object) {
		super();
		// TODO Auto-generated constructor stub
		this.target = object;
	}
	//重写方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		return method.invoke(this.target, args);
	}
	
	

}

7.动态代理类

package com.designpattern.dynamicproxy;

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

/**   
 * @ClassName:  DynamicProxy   
 * @Description:动态代理类
 * @author: wanjinYoung 
 * @date:   2018年8月16日 上午11:06:23   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class DynamicProxy {
	
	public static <T> T newProxyInstance(Subject subject) {
		
		//寻找AOP框架中的定义的JoinPoint连接点
		boolean isBeforeAdviceDefined = true;
		if (isBeforeAdviceDefined) {
			(new BeforeAdvice()).exec();
		}
		
		//定义一个Handler
		InvocationHandler handler = new MyInvocationHandler(subject);
		
		//定义主题的代理
		return (T)Proxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),handler);
	}

}

8.客户端测试类

package com.designpattern.dynamicproxy;

/**   
 * @ClassName:  Client   
 * @Description:客户端测试类
 * @author: wanjinYoung 
 * @date:   2018年8月16日 上午11:04:59   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class Client {
	public static void main(String[] args) {
		//定义一个主题
		Subject subject = new RealSubject();
		
		//定义一个代理
		Subject proxy = DynamicProxy.newProxyInstance(subject);
		
		//执行代理的行为
		proxy.request();
	}

}

9.测试结果

10.结果分析:我们利用JDK动态代理是一个通用的代理框架。如果要设计自己AOP框架,也可以使用上述的框架为设计的基础进行扩展,但是需要满足一定的条件:被代理类必须实现一个接口。

Because:JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理。并且,如果业务实现类中新增加了接口中没有的方法,这些方法就是无法被代理的(因为无法被调用)。

Three:CGlib动态代理

1.具体主题类:

package com.designpattern.cglibproxy;

/**   
 * @ClassName:  RealSubject   
 * @Description:真实主题类
 * @author: wanjinYoung 
 * @date:   2018年8月16日 上午11:42:46   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class RealSubject {
	public void request() {
		System.out.println("Do real business!");
	}

}

2.代理类

package com.designpattern.cglibproxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**   
 * @ClassName:  SubjectCglib   
 * @Description:代理类
 * @author: wanjinYoung 
 * @date:   2018年8月16日 下午12:24:33   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class SubjectCglib  implements MethodInterceptor{
	
	 //业务类对象,供代理方法中进行真正的业务方法调用
    private Object target;


    //相当于JDK动态代理中的绑定
    public Object getInstance(Object target) {
        //给业务对象赋值
        this.target = target;

        //创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();

        //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        enhancer.setSuperclass(this.target.getClass());

        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this);

        // 创建动态代理类对象并返回
        return enhancer.create();
    }

    // 实现回调方法
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        doBefore();
        //调用业务类(父类中)的方法
        proxy.invokeSuper(obj, args);
        doAfter();
        return null;
    }

    private void doBefore(){
        System.out.println("Do something before!");

    }

    private void doAfter(){
        System.out.println("Do something after!");
    }
	
	

}

3.客户端测试类

 

package com.designpattern.cglibproxy;

/**   
 * @ClassName:  Client   
 * @Description:客户端测试类
 * @author: wanjinYoung 
 * @date:   2018年8月16日 下午1:30:24   
 *     
 * @Copyright: 2018 www.wanjinYoung.com Inc. All rights reserved. 
 */  
public class Client {
	public static void main(String[] args) {
		//实例化代理类对象
		SubjectCglib cglib = new SubjectCglib();
		//通过代理动态生成真实主题类对象
		RealSubject subject = (RealSubject) cglib.getInstance(new RealSubject());
		//使用真实主题类的对象调用其中的方法
		subject.request();
	}

}

4.测试结果

四、不同(Compare)

(1).静态代理:是通过在代码中显示的定义一个业务实现类和一个代理,在代理类中对同名的业务方法进行包装,用户(Client)通过代理类调用被包装过的业务方法。

(2).JDK动态代理:是通过接口中的方法名 ,在动态生成的代理类中调用业务实现类的同名方法。

(3).CGlib动态代理:是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。

五、应用场景(When)

  • 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又叫做大使(Ambassador),如远程过程调用(RPC)。

目的:隐藏一个对象存在于不同地址空间的事实;远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。

  • 虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。

目的:减少系统的开销。

  • Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。

  • 保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。

  • 缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。

  • 防火墙(Firewall)代理:保护目标不让恶意用户接近。

  • 同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。

  • 智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。

六、优缺点

优点:

(1).可以协调调用者和被调用者,降低了系统耦合度。

(2).远程代理使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的性能与处理速度,可以响应并处理客户端请求。

(3).虚拟代理可以使用小对象来代表大对象,可以减少系统资源的消耗,对系统的速度进行优化。

(4).保护代理可以控制对真实对象的使用权。 

缺点:

(1).因为通过代理在真实对象和客户端之间进行交互,所以可能造成请求速度的变慢。

(2).实现代理需要实现额外的工作,有些代理模式十分的复杂。

注:个人学习随记!

代码:https://github.com/youngwanjin/DesignPattern/tree/master/JavaTrying/src/com/designpattern

猜你喜欢

转载自blog.csdn.net/wanjinYoung/article/details/81701900