A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

1、定义

Provide a surrogate or placeholder for another object to control access to it.

为其他对象提供一种代理以控制对这个对象的访问。

2、类图

3、角色

代理模式也叫委托模式,它是一项基本的设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。代理模式可以提供非常好的访问控制。

Subject :抽象主题角色

抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。

RealSubject:具体主题角色

也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。

Proxy:代理主题角色

也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

4、例子



  • public interface ISubject {



  •     public void method1();



  •     public void method2();



  •     public void method3();



  • }




  • public class RealSubjectA implements ISubject {







  •     private String name;







  •     public RealSubjectA(String name) {



  •         this.name = name;



  •     }







  •     @Override



  •     public void method1() {



  •         System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method1");



  •     }







  •     @Override



  •     public void method2() {



  •         System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method2");



  •     }







  •     @Override



  •     public void method3() {



  •         System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method3");



  •     }



  • }





  • public class RealSubjectProxy implements ISubject {







  •     private ISubject mISubject;







  •     public RealSubjectProxy(ISubject iSubject) {



  •         this.mISubject = iSubject;



  •     }







  •     @Override



  •     public void method1() {



  •         this.mISubject.method1();



  •     }







  •     @Override



  •     public void method2() {



  •         this.mISubject.method2();



  •     }







  •     @Override



  •     public void method3() {



  •         this.mISubject.method3();



  •     }



  • }


使用:



  • ISubject iSubject = new RealSubjectA("zlm");



  • RealSubjectProxy proxy = new RealSubjectProxy(iSubject);



  • proxy.method1();



  • proxy.method2();



  • proxy.method3();






输出:



  • I/System.out: zlm        RealSubjectA        method1



  • I/System.out: zlm        RealSubjectA        method2



  • I/System.out: zlm        RealSubjectA        method3


5、优缺点

优点:

a、职责清晰。真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰

b、高扩展性。具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。

c、智能化。动态代理可以体现。

6、扩展

设计模式中的代理分为“普通代理”和“强制代理”两种。

普通代理

他的要求就是客户端只能访问代理角色,而不能访问真实角色,也就是场景类不能再直接new一个对象了,他必须由代理来模拟场景。

我们需要修改两个实现类的构造函数即可。

RealSubject:



  • public class RealSubjectA implements ISubject {







  •     private String name;











  •     //构造函数限制谁能创建对象,并同时传递name..当然还可以 有其他的限制,比如类名必须为Proxy类等,



  •     public RealSubjectA(ISubject iSubject,String name) throws Exception {



  •         if (iSubject == null) {



  •             throw new Exception("can not create RealSubject");



  •         } else {



  •             this.name = name;



  •         }



  •     }







  •     @Override



  •     public void method1() {



  •         System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method1");



  •     }







  •     @Override



  •     public void method2() {



  •         System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method2");



  •     }







  •     @Override



  •     public void method3() {



  •         System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method3");



  •     }



  • }


Proxy:



  • public class RealSubjectProxy implements ISubject {







  •     private ISubject mISubject;







  •     //仅仅修改了构造函数,传递进来一个代理者名称,即可进行代理,



  •     public RealSubjectProxy(String name) {



  •         try {



  •             this.mISubject = new RealSubjectA(this,name);



  •         } catch (Exception e) {



  •             // TODO: 2018/6/22 异常处理



  •         }



  •     }







  •     @Override



  •     public void method1() {



  •         this.mISubject.method1();



  •     }







  •     @Override



  •     public void method2() {



  •         this.mISubject.method2();



  •     }







  •     @Override



  •     public void method3() {



  •         this.mISubject.method3();



  •     }



  • }


使用:



  • ISubject proxy = new RealSubjectProxy("zlm");



  • proxy.method1();



  • proxy.method2();



  • proxy.method3();


输出:



  • I/System.out: zlm        RealSubjectA        method1



  • I/System.out: zlm        RealSubjectA        method2



  • I/System.out: zlm        RealSubjectA        method3


        运行结果完全相同。在该模式下,调用者只知代理而不用知道真实的角色RealSubject是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色,这也是一个非常好的方案。

强制代理

强制代理在设计模式汇总比较另类,一般的思维是通过代理找到真实的角色,但是强制代理确实要“强制”,比必须通过真实角色查找到代理,否则你不能访问,也就是通过真实角色管理代理角色。

改造如下:

ISubject:



  • public interface ISubject {



  •     public void method1();







  •     public void method2();







  •     public void method3();



  •     //每个角色都可以拿到自己的代理类



  •     public ISubject getProxy();







  • }


RealSubjectProxy:



  • public class RealSubjectProxy implements ISubject {







  •     private ISubject mISubject = null;







  •     //仅仅修改了构造函数,传递进来一个用户



  •     public RealSubjectProxy(ISubject iSubject) {



  •         this.mISubject = iSubject;



  •     }







  •     @Override



  •     public void method1() {



  •         this.mISubject.method1();



  •     }







  •     @Override



  •     public void method2() {



  •         this.mISubject.method2();



  •     }







  •     @Override



  •     public void method3() {



  •         this.mISubject.method3();



  •     }







  •     @Override



  •     public ISubject getProxy() {



  •         return this;



  •     }



  • }


RealSubjectA:



  • public class RealSubjectA implements ISubject {







  •     private String name;



  •     private ISubject iSubject = null;







  •     public RealSubjectA(String name) {



  •         this.name = name;



  •     }







  •     @Override



  •     public void method1() {



  •         if (this.isProxy()) {



  •             System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method1");



  •         } else {



  •             System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method1" + "\t" + "请使用指定代理访问!");



  •         }



  •     }







  •     @Override



  •     public void method2() {



  •         if (this.isProxy()) {



  •             System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method2");



  •         } else {



  •             System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method2" + "\t" + "请使用指定代理访问!");



  •         }



  •     }







  •     @Override



  •     public void method3() {



  •         if (this.isProxy()) {



  •             System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method3");



  •         } else {



  •             System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method3" + "\t" + "请使用指定代理访问!");



  •         }



  •     }







  •     @Override



  •     public ISubject getProxy() {



  •         this.iSubject = new RealSubjectProxy(this);



  •         return this.iSubject;



  •     }







  •     //校验是否是代理访问



  •     private boolean isProxy() {



  •         if (this.iSubject == null) {



  •             return false;



  •         } else {



  •             return true;



  •         }



  •     }



  • }


运行:



  • RealSubjectA realSubjectA = new RealSubjectA("zlm");



  • ISubject proxy = realSubjectA.getProxy();



  • proxy.method1();



  • proxy.method2();



  • proxy.method3();


输出:



  • I/System.out: zlm        RealSubjectA        method1



  • I/System.out: zlm        RealSubjectA        method2



  • I/System.out: zlm        RealSubjectA        method3


7、代理个性化

一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。

增加一个接口,IProxy:



  • public interface IProxy {



  •     public void methodIProxy();



  • }


RealSubjectProxy:



  • public class RealSubjectProxy implements ISubject,IProxy {







  •     private ISubject mISubject = null;







  •     //仅仅修改了构造函数,传递进来一个用户



  •     public RealSubjectProxy(ISubject iSubject) {



  •         this.mISubject = iSubject;



  •     }







  •     @Override



  •     public void method1() {



  •         this.mISubject.method1();



  •     }







  •     @Override



  •     public void method2() {



  •         this.mISubject.method2();



  •     }







  •     @Override



  •     public void method3() {



  •         this.mISubject.method3();



  •     }







  •     @Override



  •     public ISubject getProxy() {



  •         return this;



  •     }







  •     @Override



  •     public void methodIProxy() {



  •         System.out.println("新加了一个接口,实现了其中的方法!");



  •     }



  • }


就只用修改这一个类,实现接口,其他了类不用变!

使用:



  • RealSubjectA realSubjectA = new RealSubjectA("zlm");



  • RealSubjectProxy proxy = (RealSubjectProxy) realSubjectA.getProxy();



  • proxy.method1();



  • proxy.method2();



  • proxy.method3();



  • proxy.methodIProxy();


输出:



  • I/System.out: zlm        RealSubjectA        method1



  • I/System.out: zlm        RealSubjectA        method2



  • I/System.out: zlm        RealSubjectA        method3



  • I/System.out: 新加了一个接口,实现了其中的方法!


代理类不仅仅是可以有自己的运算方法,通常的情况下代理的职责并不一定单一,它可以组合其他的真实角色,也可以实现自己的职责。

代理类可以为真实角色预处理消息、过滤消息、消息转发、事后处理消息等功能。

当然一个代理类,可以代理多个真实角色,并且真实角色之间可以有耦合关系。


8、动态代理

什么是动态代理?

动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理那个对象。面向横切面编程(AOP)的核心就是采用了动态代理机制。

接口和实现类很简单,定义需要实现的方法即可。

ISubject:



  • public interface ISubject {



  •     public void method1();







  •     public void method2();







  •     public void method3();







  • }


RealSubjectA:



  • public class RealSubjectA implements ISubject {







  •     private String name;







  •     public RealSubjectA(String name) {



  •         this.name = name;



  •     }







  •     @Override



  •     public void method1() {



  •         System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method1");



  •     }







  •     @Override



  •     public void method2() {



  •         System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method2");



  •     }







  •     @Override



  •     public void method3() {



  •         System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method3");



  •     }











  • }


定义一个动态代理类RealSubjectHI,实现InvocationHandler接口,构造方法传入需要代理的对象。



  • public class RealSubjectHI implements InvocationHandler {



  •     //被代理的实例



  •     Object obj = null;







  •     //我要代理谁



  •     public RealSubjectHI(Object obj) {



  •         this.obj = obj;



  •     }







  •     //invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调



  •     @Override



  •     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {



  •         Object invoke = method.invoke(this.obj, args);



  •         return invoke;



  •     }



  • }


其中invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调用。我 们来详细讲解一下InvocationHandler接口,动态代理是根据被代理的接口生成所有的方法, 也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”,那动态代理怎么才能实现被代理接口中的方法呢?默认情况下所有的方法返回值都是空的,是的,代理已经实现它了,但是没有任何的逻辑含义,那怎么办?好办,通过 InvocationHandler接口,所有方法都由该Handler来进行处理,即所有被代理的方法都由 InvocationHandler接管实际的处理任务。

调用:



  • //定义具体实现类



  • ISubject iSubject = new RealSubjectA("zlm");



  • //获得类的classload



  • ClassLoader classLoader = iSubject.getClass().getClassLoader();



  • //定义一个handler



  • InvocationHandler invocationHandler = new RealSubjectHI(iSubject);



  • //动态产生一个代理者



  • ISubject proxy = (ISubject) Proxy.newProxyInstance(classLoader, new Class[]{ISubject.class}, invocationHandler);



  • proxy.method1();



  • proxy.method2();



  • proxy.method3();


输出:



  • I/System.out: zlm        RealSubjectA        method1



  • I/System.out: zlm        RealSubjectA        method2



  • I/System.out: zlm        RealSubjectA        method3


注意:

我们既没有创建代理类,也没有实现ISubject接口,这就是动态代理。

如果我们想在一个方法调用后随即触发一些逻辑操作怎么办呢?简单,只需要改变动态代理Handler类,在invoke方法中添加一段的代码即可,请看:



  • public class RealSubjectHI implements InvocationHandler {



  •     //被代理的实例



  •     Object obj = null;







  •     //我要代理谁



  •     public RealSubjectHI(Object obj) {



  •         this.obj = obj;



  •     }







  •     //invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调



  •     @Override



  •     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {



  •         Object invoke = method.invoke(this.obj, args);



  •         if (method.getName().equalsIgnoreCase("method1")) {



  •             System.out.println("调用了method1方法,可以在此处添加逻辑!");



  •         }



  •         return invoke;



  •     }



  • }


调用方法并无变化:




  • //定义具体实现类



  • ISubject iSubject = new RealSubjectA("zlm");



  • //获得类的classload



  • ClassLoader classLoader = iSubject.getClass().getClassLoader();



  • //定义一个handler



  • InvocationHandler invocationHandler = new RealSubjectHI(iSubject);



  • //动态产生一个代理者



  • ISubject proxy = (ISubject) Proxy.newProxyInstance(classLoader, new Class[]{ISubject.class}, invocationHandler);



  • proxy.method1();



  • proxy.method2();



  • proxy.method3();


输出:



  • I/System.out: zlm        RealSubjectA        method1



  • I/System.out: 调用了method1方法,可以在此处添加逻辑!



  • I/System.out: zlm        RealSubjectA        method2



  • I/System.out: zlm        RealSubjectA        method3


很棒啊,有木有!这就是AOP编程。AOP编程没有使用什么新的技术,但是它对我们的设计、编码有非常大的影响,对于日志、事务、权限等都可以在系统设计阶段不用考虑,而在设计后通 过AOP的方式切过去。

很简单,两条独立发展的线路。动态代理实现代理的职责,业务逻辑Subject实现相关的 逻辑功能,两者之间没有必然的相互耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。

AOP及动态代理抽取

在这里插入了较多的AOP术语,如在什么地方(连接点)执行什么行为(通知)。我们 在这里实现了一个简单的横切面编程。

我们来看通知Advice,也就是我们要切入的类,接口和实现如代码清单如下:



  • /**



  • * Created by ZLM on 2018/7/2.



  • * Describe 前置通知接口



  • */







  • public interface IAdvice {



  •     public void exec();



  • }




  • /**



  • * Created by ZLM on 2018/7/2.



  • * Describe 前置通知实现



  • */







  • public class BeforeIAdvice implements IAdvice {



  •     @Override



  •     public void exec() {



  •         System.out.println("我是前置通知!我被执行了!");



  •     }



  • }


因此我们抽象动态代理类:



  • /**



  • * Created by ZLM on 2018/7/2.



  • * Describe 动态代理类



  • */







  • public class DynamicProxy<T> {



  •     public static <T> T getNewProxyInstance(ClassLoader classLoader, Class<?>[] tClass, InvocationHandler invocationHandler) {



  •         //寻找JoinPoint连接点,AOP框架使用元数据定义



  •         if (true) {



  •             //执行一个前置通知



  •             new BeforeIAdvice().exec();



  •         }



  •         //执行目标,返回结果



  •         return (T) Proxy.newProxyInstance(classLoader, tClass, invocationHandler);



  •     }



  • }


在返回动态代理对象之前,执行我们要切入的类的方法。

执行变成了这样:







  • //定义具体实现类



  • ISubject iSubject = new RealSubjectA("zlm");



  • //获得类的classload



  • ClassLoader classLoader = iSubject.getClass().getClassLoader();



  • //定义一个handler



  • InvocationHandler invocationHandler = new RealSubjectHI(iSubject);



  • //产生一个动态代理



  • ISubject proxy = DynamicProxy.getNewProxyInstance(classLoader, iSubject.getClass().getInterfaces(), invocationHandler);



  • proxy.method1();



  • proxy.method2();



  • proxy.method3();


输出:



  • I/System.out: 我是前置通知!我被执行了!



  • I/System.out: zlm        RealSubjectA        method1



  • I/System.out: 调用了method1方法,可以在此处添加逻辑!



  • I/System.out: zlm        RealSubjectA        method2



  • I/System.out: zlm        RealSubjectA        method3


Proxy.newProxyInstance(classLoader, tClass, invocationHandler);该方法是重新生成了一个对象,为什么要重新生成?你要使用代理呀,注意 c.getInterfaces()这句话, 这是非常有意思的一句话,是说查找到该类的所有接口,然后实现 接口的所有方法。当然了,方法都是空的,由谁具体负责接管呢?是new MyInvocationHandler(_Obj)这个对象。 于是我们知道一个类的动态代理类是这样的一个类,由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有方法的实现。

我们注意到上面的动态代理类DynamicProxy是一个通用类,不具有业务意义,我们可以在再产生一个实现类简化代码。



  • /**



  • * Created by ZLM on 2018/7/2.



  • * Describe 具有业务逻辑的动态代理类



  • */







  • public class SubjectDynamicProxy extends DynamicProxy {



  •     public static <T> T getNewProxyInstance(T t) {



  •         ClassLoader classLoader = t.getClass().getClassLoader();



  •         Class<?>[] interfaces = t.getClass().getInterfaces();



  •         InvocationHandler invocationHandler = new RealSubjectHI(t);



  •         //执行目标,返回结果



  •         return getNewProxyInstance(classLoader, interfaces, invocationHandler);



  •     }



  • }


调用 so easy:



  • ISubject iSubject = new RealSubjectA("zlm");



  • ISubject proxy = SubjectDynamicProxy.getNewProxyInstance(iSubject);



  • proxy.method1();



  • proxy.method2();



  • proxy.method3();


输出:



  • I/System.out: 我是前置通知!我被执行了!



  • I/System.out: zlm        RealSubjectA        method1



  • I/System.out: 调用了method1方法,可以在此处添加逻辑!



  • I/System.out: zlm        RealSubjectA        method2



  • I/System.out: zlm        RealSubjectA        method3



【转载】原文地址:https://blog.csdn.net/Aminy123/article/details/80769246


1 个回复

倒序浏览
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马