首页 > 编程知识 正文

代理模式的典型例子,品牌代理模式的优缺点

时间:2023-05-03 12:26:03 阅读:152572 作者:3693

比较代理模式的实现方式和优缺点代理模式最典型的应用是AOP,本文主要描述了代理模式的几种实现方式,即静态代理和动态代理。 这里动态代理可以分为jdk代理和Cglib代理,本文还比较了这些代理模式的优缺点。

代理,ttdyl,即代表委托人处理相关事务。 代理通常完全代理被委托人的所有权。 客户访问代理与访问被申请人相同。 代理最终可能会访问被请求者,但可以在请求之前或之后执行其他任务。 或者,如果客户的请求不合法,也可以直接拒绝客户的请求。 下图是代理模式的示意图。

代理模式角色:

ISubject :代理和被代理共同实现的界面,可以理解为代理所需要的行为;

SubjectImpl :作为具有某一特定行为的实现者的被代理人;

subject proxy :全面代行subject impl所具有的功能,在实现该功能方面做额外工作的代理;

客户端、客户端访问代理具有与访问被代理相似的效果,无法区分访问的是代理还是被代理。

静态代理

静态代理模式与上图类似,从图中可以看到,SubjectProxy保存ISubject实例,当客户端调用SubjectProxy的request ()方法时,除了执行额外的任务外以下三个类的简单实现。 publicinterfaceisubject { void request (; } publicclasssubjectimplimplementsisubject { @ overridepublicvoidrequest () } { system.out.println (requestsubjectimpl.plovictimpletinteqution } publicclasssubjectproxyimplementsisubject { privateisubjecttarget; publicsubjectproxy (isubjecttarget ) { this.target=target; } @Override public void request () system.out.println (' beforesafetycheck.' ); target.request (; system.out.println (aftersafetycheck.' ); }您可以看到代理对象在调用被代理对象的方法之前和之后打印相关语句。 以下是客户端请求的示例。

public class client { @ testpublicvoidteststaticproxy () { I subject subject=new subject impl ); isubjectproxy=newsubjectproxy (subject; proxy.request (; }运行以上用例将产生以下结果:

从beforesafetycheck.requestsubjectimpl.aftersafetycheck .客户端访问方法可以看出,客户端获取实现ISubject接口的实例,实际上是preplise 调用方法。这种代理方式称为静态代理,因为所有的类都已经创建好了,客户端只需要获取代理对象并运行,所以这种代理方式也是最有效的方式。

静态代理虽然高效,但也有不可避免的缺点。 您可以看到,客户端在调用代理对象时,使用的是代理对象和被代理对象都实现的接口。 此接口可以理解为定义业务需求的实现规范。 如果存在其他业务需求(如数据更改),则它与当前需求并行且不相交,但在普通业务之外进行的安全验证工作与当前需求一致。 执行该数据修改业务的安装代码如下。

publicinterfaceiupdatable { void update (; } publicclassupdatableimplimplementsiupdatable { @ overridepublicvoidupdate () { system.out.println (updateupdatableimplemplint ) publicupdatableproxy (iupdatableupdatable ) { this.updatable=updatable; } @Override public void update () system.out.println (' presafetycheck.' ); updatable.update (; system.out.println (aftersafetycheck.' ); }客户端代码如下所示。

公共的

class Client { @Test public void testStaticProxy() { ISubject subject = new SubjectImpl(); ISubject proxy = new SubjectProxy(subject); proxy.request(); IUpdatable updatable = new UpdatableImpl(); IUpdatable proxy = new UpdatableProxy(updatable); proxy.update(); }}

可以看到,要实现相同的对象代理功能(安全验证),静态代理方式需要为每个接口实现一个代理类,而这些代理类中的代码几乎是一致的。这在大型系统中将会产生很大的维护问题。
2. 动态代理
① jdk代理
所谓的jdk代理指的是借助jdk所提供的相关类来实现代理模式,其主要有两个类:InvocationHandler和Proxy。在实现代理模式时,只需要实现InvocationHandler接口即可,如下是实现该接口的一个示例:

public class SafetyInvocationHandler implements InvocationHandler { private Object target; public SafetyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before safety check."); Object result = method.invoke(target, args); System.out.println("after safety check."); return result; }}

如下是客户端调用方式:

public class Client { @Test public void testDynamicProxy() { ISubject subject = new SubjectImpl(); ISubject proxySubject = (ISubject) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{ISubject.class}, new SafetyInvocationHandler(subject)); proxySubject.request(); IUpdatable updatable = new UpdatableImpl(); IUpdatable proxyUpdatable = (IUpdatable) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{IUpdatable.class}, new SafetyInvocationHandler(updatable)); proxyUpdatable.update(); }} 可以看到,客户端在调用代理对象时使用的都是同一个SafetyInvocationHandler。这里jdk代理其实在底层利用反射为每个需要代理的对象都创建了一个InvocationHandler实例,在调用目标对象时,其首先会调用代理对象,然后在代理对象的逻辑中请求目标对象。这也就是为什么在代理类中可以保存目标对象实例的原因,比如上述的SafetyInvocationHandler,其声明了一个Object类型的属性用来保存目标对象的实例。 jdk代理解决了静态代理需要为每个业务接口创建一个代理类的问题,虽然使用反射创建代理对象效率比静态代理稍低,但其在现代高速jvm中也是可以接受的,在Spring的AOP代理中默认就是使用的jdk代理实现的。这里jdk代理的限制也是比较明显的,即其需要被代理的对象必须实现一个接口。这里如果被代理对象没有实现任何接口,或者被代理的业务方法没有相应的接口,我们则可以使用另一种方式来实现,即Cglib代理。

② Cglib代理
Cglib代理是功能最为强大的一种代理方式,因为其不仅解决了静态代理需要创建多个代理类的问题,还解决了jdk代理需要被代理对象实现某个接口的问题。对于需要代理的类,如果能为其创建一个子类,并且在子类中编写相关的代理逻辑,因为“子类 instanceof 父类”,因而在进行调用时直接调用子类对象的实例,也可以达到代理的效果。Cglib代理的原理实际上是动态生成被代理类的子类字节码,由于其字节码都是按照jvm编译后的class文件的规范编写的,因而其可以被jvm正常加载并运行。这也就是Cglib代理为什么不需要为每个被代理类编写代理逻辑的原因。这里需要注意的是,根据Cglib实现原理,由于其是通过创建子类字节码的形式来实现代理的,如果被代理类的方法被声明final类型,那么Cglib代理是无法正常工作的,因为final类型方法不能被重写。如下是使用Cglib代理的一个示例:

/** * 被代理类 */public class Suject { public void request() { System.out.println("update without implement any interface."); }}/** * 代理类 */public class SafetyCheckCallback implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before safety check."); Object result = methodProxy.invokeSuper(o, objects); System.out.println("after safety check."); return result; }}

如下是客户端访问方式:

public class Client { @Test public void testCglibProxy() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Suject.class); enhancer.setCallback(new SafetyCheckCallback()); Suject proxy = (Suject) enhancer.create(); proxy.request(); }} 可以看到,客户端代码中首先创建了一个Enhancer对象,并且设置了父类及代理回调类对象。该Enhancer对象会为目标类创建相关的子类字节码,并且将代理代码植入该子类字节码中。 总结
本文主要对代理模式的三种实现方式进行了详细讲解,并且比较了各个代理方式的优缺点,Spring主要使用的是动态代理方式实现切面编程的。这里读者可能会有一个疑问,即上述代理代码中,根据实现方式的不同,对客户端代码都有一定的侵入性,比如静态代理客户端需要侵入代理类的实例,jdk代理需要侵入Proxy类,而Cglib代理则需要侵入子类子类对象创建等代码。理论上,客户端只需要获取目标对象,无论是否为代理过的,然后调用其相关方法实现特定功能即可。这其实也是工厂方法的强大之处,因为工厂方法会将对象的创建封装起来,对象的具体创建过程可以根据具体的业务处理即可,客户端只需要依赖工厂类调用相关的方法即可。同样的这也就说明了Spring IoC容器是天然支持AOP代理的原因,因为其将对象的创建过程交由容器进行了。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。