java设计模式--代理模式

Posted rhodesis

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java设计模式--代理模式相关的知识,希望对你有一定的参考价值。

代理模式

  代理模式是设计模式中的对象结构型模式,代理模式主要是为其他对象提供一种代理以控制对这个对象的访问。

  关于代理模式,我们听到的比较多的就是静态代理、动态代理,同时我们也听到的比较多的Spring Aop运用的也是代理模式,那么举个例子,在现实生活中我们会听到打官司的时候请律师,律师会帮我们发律师函,与对方当事人交涉,与警方交涉,这些本身是我们应该做的事,我们将所有处理这些事情的过程全权受理给律师来做,这种方式就是律师代理我,帮助我来做我应该做的事情

代理模式的结构图

技术图片

代理模式中一共包含了三类角色:

1、抽象对象角色(Subject):声明了目标对象和代理对象的共同接口,这样一来,在任何可以使用目标对象的地方都可以使用代理对象。

2、目标对象角色(RealSubject):定义了代理对象所代表的实体。

3、代理对象角色(ProxySubject):保存了一个目标对象的引用,使得可以访问实体;提供了一个与目标对象相同的接口,这样代理对象就可以用来替代实体。

代理模式示例

1、定义抽象对象角色,声明目标对象角色和代理对象角色共同的接口

public interface Subject 
    public void Request();

2、定义目标对象角色,在该角色中是客户端所在意的实际操作

public class RealSubject implements Subject 
    @Override
    public void Request() 
        System.out.println("完成具体的请求操作");
    

3、代理对象角色,通过代理的方式完成实际的操作

public class ProxySubject implements Subject 
    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) 
        this.realSubject = realSubject;
    

    @Override
    public void Request() 
        System.out.println("before request");
        realSubject.Request();
        System.out.println("after request");
    

4、客户端请求的操作

public class Client 
    public static void main(String[] args) 
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        proxySubject.Request();
    



运行结果:
before request
完成具体的请求操作
after request

代理模式的优缺点

  • 代理模式职责清晰,目标角色实现实际的业务逻辑。

  • 高扩展性,代理模式可以在目标角色修改之后不做任何修改来使用。

  • 有些类型的代理模式可能会造成请求的处理速度变慢。

  • 实现代理模式可能需要做额外的工作,甚至有些代理模式也比较复杂。

静态代理

  静态代理是指由程序员或特定的工具自动生成源代码,在程序运行之前代理类的.class文件就已经存在了。

  我们以律师代理我们处理官司为例,我们主要的业务逻辑是打官司,其他的我不关心的逻辑全都有律师来解决。首先我们定义一个打官司的类提供一个打官司的接口

public interface Court 
    public void goCourt();

  接下来定义一个授权人的类实现官司的类,完成具体业务逻辑

public class Donor implements Court 
    @Override
    public void goCourt() 
        System.out.println("正在法庭打官司");
    

  定义一个律师类实现官司的类,同时提供特定的服务

public class Lawyer implements Court 
    private Donor donor;
    public Lawyer(Donor donor)
        this.donor=donor;
    
    @Override
    public void goCourt() 
        System.out.println("打官司前准备材料");
        donor.goCourt();
        System.out.println("打完官司完成收尾动作");
    

  客户端调用

public class Client 
    public static void main(String[] args) 
        Donor donor = new Donor();
        Lawyer lawyer = new Lawyer(donor);
        lawyer.goCourt();
    



运行结果:

打官司前准备材料
正在法庭打官司
打完官司完成收尾工作

  静态代理就是在代理类中注入依赖,即引入需要代理的实体类,通过代理类来调用实体类的方式来实现代理。

静态代理的缺点

  首先,静态代理的特点是静态代理的代理类是由程序员创建的,这个类在程序运行之前.class文件就已经生成好了。那么通过上面的例子来看就可以看出静态代理有两种缺点:

1、代理类确实可以控制实际对象的引用来使用实际对象,但是上例可以看出Lawyer只能服务于Court接口,如果其他接口也想用此代理模式必须要新建一个代理类才能完成代理,这样在系统较为复杂的情况下回产生大量的代理类影响系统的可读性同时增大内存损耗。

2、如果抽象对象需要新增一个接口方法,实际对象实现了这个方法,那么在代理对象中也必须新增这个方法来完成代理,如果在系统中大量使用静态代理,那么改动一个接口方法则需要改动许多地方使得系统不易于管理。

动态代理

  动态代理和静态代理的区别就是动态代理类是在程序运行时通过反射机制来生成的,不需要和静态代理一样在程序运行之前就把代理类规定好,我们只需要编写一个动态处理器,真正的代理对象则在JDK运行时帮我们创建,所以我们也称之为JDK动态代理。

  动态代理示例:

  现在有一个IPerson接口,只有一个say方法。

public interface IPerson 
    void say();

  定义一个Man的类实现IPerson接口。

public class Man implements IPerson
    @Override
    public void say() 
        System.out.println("man say");
    

  下面代理类是通过实现InvocationHandler接口,通过将要代理的对象传入构造器,在实现的invoke方法中增加method.invoke(target,args);在调用的时候通过Proxy.newProxyInstance()方法获取代理对象实现调用。

public class NormalHandler implements InvocationHandler 
    private Object target;
    public  NormalHandler(Object target)
        this.target=target;
    

    public Object bind() 
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),this.target.getClass().getInterfaces(),this);
    

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        method.invoke(target,args);
        return null;
    

  客户端调用如下

public class TestMain 

    public static void main(String[] args) 
        Man man = new Man();
        NormalHandler normalHandler = new NormalHandler(man);
        IPerson proxyInstance=(IPerson) normalHandler.bind();
        System.out.println(proxyInstance instanceof Proxy);
        proxyInstance.say();
    




运行结果:
true
man say

  通过运行结果可以看出,bind()方法生成的对象实际上是Proxy对象而不是生成的IPerson对象的实例。

  想对比于静态代理,动态代理减少了对业务接口的依赖同时还降低了耦合度,但是JDK动态代理有一个致命的缺点,就是它只能针对接口生成代理,而不能针对具体的类生成代理。

Cglib代理

  Cglib是性能强大的代码代码生成包,包括Spring Aop的实现中也用到了Cglib代理,它的底层是通过小而快的字节码处理框架ASM转换并生成新的类,与JDK动态代理的方式不同,Cglib是通过继承被代理类来产生新的类的,所以有final修饰的类是不能通过cglib来代理的。

  还是以JDK动态代理的例子为例,首先拦截类实现MethodInterceptor类

public class ManMethodInterceptor implements MethodInterceptor 
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable 
        System.out.println("Before:"+method.getName());
        Object object=methodProxy.invokeSuper(o,objects);
        System.out.println("After:"+method.getName());
        return object;
    

  接下来是客户端调用,通过Cglib字节码增强器Enhancer来实现cglib动态代理

public class Client 
    public static void main(String[] args) 
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(Man.class);
        enhancer.setCallback(new ManMethodInterceptor());
        Man man=(Man) enhancer.create();
        System.out.println(man.getClass());
        System.out.println(man instanceof Man);
        man.say();

    

  其中setSuperclass传入要被代理的类,setCallback是拦截类,setcallback方法中可以传入多个拦截类来实现不同方式的拦截。同时enhancer还有setCallbackFilter方法来实现不同条件不同拦截方式,Cglib还有许多的用法,具体的使用方法在以后再详细介绍。

以上是关于java设计模式--代理模式的主要内容,如果未能解决你的问题,请参考以下文章

有关java的动态代理和代理模式

Java 之 设计模式——代理模式

java设计模式6——代理模式

Java设计模式----代理模式

java代码实现设计模式之代理模式

Java设计模式——代理模式