一、模式定义(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