zoukankan      html  css  js  c++  java
  • 代理模式

    Java中,代理模式的核心本质在于:两个子类共同实现一个接口,其中一个子类负责真实业务实现,另外一个子类完成辅助真实业务主题的操作。

    先给大家看一张基础设计代理的原理图

    第一种方法是最基本的代理模式实现,简单的举一个买电脑的例子,代码如下:

    public class Test {
        public static void main(String[] args) {
            ISubject subject = Factory.getInstance();
            subject.buyComputer();
        }
    }
    //定义一个买电脑接口
    public interface ISubject {
        void buyComputer();
    }
    //定义真实类实现接口
    public class RealSubject implements ISubject{
    
        @Override
        public void buyComputer() {
            System.out.println("买电脑");
        }
    }
    //定义代理类实现接口
    public class ProxySubject implements ISubject{
        private ISubject subject;
        public ProxySubject(ISubject subject){
            this.subject = subject;
        }
        public void produceComputer(){
            System.out.println("1.生产");
        }
    
        public void afterSale(){
            System.out.println("3.售后服务");
        }
    
        @Override
        public void buyComputer() {
            this.produceComputer();
            this.subject.buyComputer();
            this.afterSale();
        }
    }
    //定义一个工厂,用来返回代理类对象
    public class Factory {
        public static ISubject getInstance(){
            return new ProxySubject(new RealSubject());
        }
    }

    第二种方法通过反射实现,举一个吃卤菜的例子

    public interface ISubject {
        void eat();
    }
    
    public class RealSubject implements ISubject {
        @Override
        public void eat() {
            System.out.println("吃卤菜");
        }
    }
    
    public class ProxySubject implements ISubject{
        private ISubject subject;
        public ProxySubject(ISubject subject){
            this.subject = subject;
        }
    
        public void eatbefore(){
            System.out.println("想吃卤菜,真香定理再次被证明,去小调买嘛");
        }
    
        public void eatafter(){
            System.out.println("吃完太辣喝口水,压压惊,并且心里发誓,以后再也不买,");
        }
    
        @Override
        public void eat() {
            this.eatbefore();
            this.subject.eat();
            this.eatafter();
        }
    }
    
    public class Factory {
        private Factory(){}
        //获取类对象,classname是包名+类名
        public static <T> T getInstance(String classname){
            T t = null;
            try {
                t = (T) Class.forName(classname).newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return t;
        }
        //获取类对象,classname是包名+类名,obj为真实类对象
        public static <T> T getInstance(String classname,Object obj){
            T t = null;
            try {
               //通过反射获取构造器 
                Constructor<?> cons = Class.forName
              (classname).getConstructor(obj.getClass().getInterfaces()[0]); t = (T)cons.newInstance(obj); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return t; } }

    public class Test {
        public static void main(String[] args) {
            ISubject subject = Factory.getInstance("BIT.Reflex.reflect.reflexproxyI.ProxySubject",
                    Factory.getInstance("BIT.Reflex.reflect.reflexproxyI.RealSubject"));
            subject.eat();
        }
    }
     

    以上程序如果结合反射之后,整体的处理会非常繁琐。不光开发端,使用者使用起来也很麻烦。对于以上操作,客 户端最多只需要关系代理是谁,实际业务是谁即可。

    因此我们可以对上述Factory类中的getInstance(String classname,Object obj)做优化,

     public static <T> T getInstance1(String proxyName,String realName){
            T t = null;
            //真实类的对象
            T obj = getInstance(realName);
            try {
                Constructor<?> cons = Class.forName
              (proxyName).getConstructor(obj.getClass().getInterfaces()[0]); t = (T)cons.newInstance(obj); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return t; }

    此时主函数中我们可以这样写

    public static void main(String[] args) {
    
            ISubject subject1 = Factory.getInstance1("BIT.Reflex.reflect.reflexproxyI.ProxySubject",
                    "BIT.Reflex.reflect.reflexproxyI.RealSubject");
            subject1.eat();
    }

    上面我们写的都是最基础的代理,一个类只实现一个接口。我们引出一个这样的问题,如果一个类实现两个接口,甚至更多的接口呢?那么我们该如何解决?要想解决此类问题,我们需要引入动态代理设计模式。

    动态代理模式:

    先看一张动态代理的原理图:

    动态代理模式的核心特点:一个代理类可以代理所有需要被代理的接口的子类对象

    /** 动态代理实现的标识接口,只有实现此接口才具备有动态代理的功能 */
    
    public interface InvocationHandler {  
    
     /**     
    * invoke表示的是调用执行的方法,但是所有的代理类返回给用户的接口对象都属于代理对象 * 当用户执行接口方法的时候所调用的实例化对象就是该代理主题动态创建的一个接口对象 *
    @param proxy 表示被代理的对象信息 * @param method 返回的是被调用的方法对象,取得了Method对象则意味着可以使用invoke()反射调用方 法 * @param args 方法中接收的参数 * @return 方法的返回值 * @throws Throwable 可能产生的异常 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }

    如果要想进行对象的绑定,那么就需要使用一个Proxy程序类,这个程序类的功能是可以绑定所有需要绑定的接口 子类对象,而且这些对象都是根据接口自动创建的,该类有一个动态创建绑定对象的方法:

    public static Object newProxyInstance(ClassLoader loader,Class<?> []interfaces,InvocationHandler h) throws IllegalArgumentException

    下面举一个简单例子

    //定义两个接口
    public
    interface ISubject { // 核心操作接口     void eat(String msg, int num) ; // 吃饭是核心业务 } public interface ITest { void play(); }
    //定义真实类
    public class RealSubject implements ISubject,ITest{ @Override public void eat(String msg, int num) { System.out.println("我要吃 "+num + "碗 "+msg) ; } @Override public void play() { System.out.println("玩吉尔"); } }
    //定义代理类
    public class ProxySubject implements InvocationHandler { // 绑定任意接口的对象,使用Object描述     private Object target ; /**     实现真实对象的绑定处理,同时返回代理对象     @param target     @return 返回一个代理对象(这个对象是根据接口定义动态创建生成的代理对象)     */ public Object bind(Object target) { // 保存真实主题对象 this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(),
              target.getClass().getInterfaces(),this);//this就是InvocationHandler对象 } public void preHandle() { System.out.println("[ProxySubject] 方法处理前"); } public void afterHandle(){ System.out.println("[ProxySubject] 方法处理后"); }

          
      //在代理对象上引用了InvocationHandler的对象, 所以才会在执行代理对象的方法时,
      //执行InvocationHandler中的invoke方法
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            this.preHandle();
            Object obj = method.invoke(target,args);
            this.afterHandle();
            return obj;
        }
    }
    //定义测试类
    public class Test { public static void main(String[] args) { ISubject subject =(ISubject) new ProxySubject().bind(new RealSubject()); subject.eat("酸汤面",20); ITest iTest = (ITest)new ProxySubject().bind(new RealSubject()); iTest.play(); } }

    以上就是动态代理的具体实现。

    我们都知道所有代理的模式都有一个问题:离不开接口,那么如果让我们设计一个没有接口的动态代理,我们该如何实现,此时如果要想实现这样的要求,就必须依靠另外的第三方组件包: CGLIB 。这个开发包才能帮用户实现这类的要 求。

    这种方式在这里不在做过多的阐述,大家只需要知道有这么一种方法即可,大家熟练掌握上面的几种方法即可。

  • 相关阅读:
    js去除空格
    Quartz定时任务学习(九)Quartz监听器
    Quartz定时任务学习(七)Cron 触发器
    数据挖掘之聚类算法K-Means总结
    SQL SERVER分区具体例子详解
    基于basys2驱动LCDQC12864B的verilog设计图片显示
    图像处理的多线程计算
    三维空间中的几种坐标系
    2017年要学习的三个CSS新特性
    Mesos 资源分配
  • 原文地址:https://www.cnblogs.com/du001011/p/10779847.html
Copyright © 2011-2022 走看看