设计模式:学习笔记(12)——代理模式

设计模式:学习笔记(12)——代理模式

代理模式

  举个简单的例子,打官司的时候,我们需要找律师,此时我们把案子委托给律师,此时律师和我们的关系就属于代理关系,即律师代理我们。这时我们再想,律师需要我们去阐述事实,同时可以帮我们事前做案情分析,事后进行案件记录等等,来使得我们的行为朝向更有利于我们的方向!这就是一个代理的小例子,像下图这样:

  

  代理模式用更专业的表述如下:

  代理者使用代理对象完成用户请求,屏蔽用户对真实对象的访问。在软件设计中,使用代理模式的意图有很多,比如因为安全原因需要屏蔽客户端直接访问真实对象,或者在远程调用中需要使用代理类处理远程方法调用的技术细节(如RMI),也可能为了提升系统性能,对真实对象进行封装,从而达到延迟加载的目的。

  那我们站在Java开发角度考虑,代理类代替本体执行功能的同时扩充了其能力,使其更加灵活、功能丰富

代理模式的设计图  

  

  看上图,代理类和本体都继承了同一个个接口,同时代理对象内部拥有本体实例,以便实现在原有功能上的扩展。

静态代理

  其实呢这个类图就是静态代理了,我们将其转换为代码,如下:

//公共接口
interface Hello{
    void sayHi();
}

//本体
class SayHello implements Hello{
    @Override
    public void sayHi() {
        System.out.println("Hello World");
    }
}

//代理类
class SayHelloProxy implements Hello{
    private SayHello sayHello = new SayHello();

    @Override
    public void sayHi() {
        System.out.println("Before ....");
        sayHello.sayHi();
        System.out.println("After ....");
    }
}

class Demo{
    public static void main(String[] args) {
        Hello hello = new SayHelloProxy();
        hello.sayHi();
    }
}

  是不是很简单吖,但是我们再考虑一下!如果有同一个接口的一批实现类,都需要通过代理扩充日志记录的功能,那么我们需要一一创建多个代理类,显然非常繁琐,那如果之后再扩展权限控制、缓存、异常处理等等,同时需要修改所有的代理类,这将使得我们的代码非常冗余和晦涩!

  为此,我们可以使用Java提供的动态代理的方式!什么是动态代理呢?

 

动态代理

  因为传统的代理模式,我们需要依赖具体的代理类,也就是说在编译时要确认本体及其代理类。那所谓动态代理呢,其实是就把从前需要写的众多代理类抽象成一个,并且呢可以在运行时动态创建这个代理类的实例!

  我们首先定义一个处理类类封装抽取出来的逻辑,它继承自InvocationHandler内部用一个Object实例来表示被代理的本体,然后整体实现和我们的静态代理时很相似的,只不过这里利用了反射的思想。

class HelloDynamicProxy implements InvocationHandler {

    private Object target;

    public HelloDynamicProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Dynamic Before.....");
        Object result = method.invoke(target,args);
        System.out.println("Dynamic Before.....");
        return null;
    }
}

  接着,我们使用JDK提供的Proxy类来创建某个接口的代理类!(这里就可以看到和静态代理的区别,可以承接整个接口

    @Test
    public void dynamicEntry(){
        HelloDynamicProxy handle = new HelloDynamicProxy(new SayHello());
        Hello hello = (Hello) Proxy.newProxyInstance(this.getClass().getClassLoader(),new Class[]{Hello.class},handle);
        hello.sayHi();
    }

  可以看到,Proxy.newProxyInstance(类加载器,接口,抽象处理类)。需要我们传递接口!那如果我们想要包装的类就没有实现接口,该怎么半呢?此时我们可以使用GCLib,它是一个代码生成包,它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择

 

CGLib动态代理

  上述我们已经知道了JDK的动态代理的弊端,我们现在来看一下CGLib实现的动态代理怎么写!

class CGLibProxy implements InvocationHandler{

    private Object obj;

    public CGLibProxy(Object obj){
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("speak")){
            System.out.println("Before....");
            method.invoke(obj,args);//反射
            System.out.println("After....");
        }
        return null;
    }
}

  这个和刚刚几乎是一模一样的是吧,创建动态代理对象的时候呢,我们使用Enhancer而不是Proxy。

        CGLibProxy lawyerInteceptor = new CGLibProxy(new SayHello());
        SayHello lisi = (SayHello) Enhancer.create(SayHello.class,CGLibProxy);
        lisi.say();

  当这里呢,我们就介绍完毕两种代理的使用方法了!

 

参考链接

 

posted @ 2018-12-26 11:08  子烁爱学习  阅读(294)  评论(0编辑  收藏  举报