zoukankan      html  css  js  c++  java
  • 设计模式:学习笔记(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();
    

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

    参考链接

  • 相关阅读:
    AngularJS ——ngResource、RESTful APIs 使用
    Angularjs promise-$q服务详解
    ui-router参数传递
    overflow知多少
    css3 @font-face设置嵌入字体
    CSS3文本超出容器显示省略号之text-overflow属性
    CSS3线性渐变和径向渐变
    CSS3边框图片属性---border-image
    在移动端如何选择字体大小和布局的单位,px或dp?
    angular $http配置属性
  • 原文地址:https://www.cnblogs.com/MrSaver/p/10178069.html
Copyright © 2011-2022 走看看