• Java 反射机制:(十四) 反射的应用:动态代理


    一、代理设计模式

      1、原理

        使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

      2、静态代理

        静态代理特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。 好可以通过一个代理类完成全部的代理功能

      3、动态代理

        动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

        动态代理使用场合:
          ① 调试
          ② 远程方法调用
        动态代理相比于静态代理的优点:

        抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

    二、静态代理

      1、静态代理特点

        代理类和被代理类在编译期间,就确定下来了。

      2、案例

        (1)创建接口

    1 interface ClothFactory{
    2 
    3     void produceCloth();
    4 
    5 }

        (2)创建被代理类

    1 //被代理类
    2 class NikeClothFactory implements ClothFactory{
    3 
    4     @Override
    5     public void produceCloth() {
    6         System.out.println("Nike工厂生产一批运动服");
    7     }
    8 }

        (3)创建代理类

     1 //代理类
     2 class ProxyClothFactory implements ClothFactory{
     3 
     4     private ClothFactory factory;//用被代理类对象进行实例化
     5 
     6     public ProxyClothFactory(ClothFactory factory){
     7         this.factory = factory;
     8     }
     9 
    10     @Override
    11     public void produceCloth() {
    12         System.out.println("代理工厂做一些准备工作");
    13 
    14         factory.produceCloth();
    15 
    16         System.out.println("代理工厂做一些后续的收尾工作");
    17 
    18     }
    19 }

        (4)测试类

     1 public class StaticProxyTest {
     2     public static void main(String[] args) {
     3         //创建被代理类的对象
     4         ClothFactory nike = new NikeClothFactory();
     5         //创建代理类的对象
     6         ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
     7 
     8         proxyClothFactory.produceCloth();
     9 
    10     }
    11 }

      

    三、动态代理

      1、Java 动态代理相关 API

        Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。

        提供用于创建动态代理类和动态代理对象的静态方法

    static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 创建一个动态代理类所对应的Class对象
    
    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 直接创建一个动态代理对象
    

        newProxyInstance 方法需要三个参数:

        ① 被代理类的类加载器;

        ② 得到被代理类实现的全部接口;

        ③ 得到 InvocationHandler 接口的实现类实例;

      2、动态代理步骤

        (1)创建一个实现接口 InvocationHandler 的类,它必须实现 invoke 方法,以完成代理的具体操作。

          

        (2)创建被代理的类以及接口

          

        (3)通过 Proxy 的静态方法创建代理对象

    newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理

        (4)通过代理调用实现被代理的方法

      3、动态代理案例

        (1)创建接口

    1 interface Human{
    2 
    3     String getBelief();
    4 
    5     void eat(String food);
    6 
    7 }

        (2)创建被代理类

     1 //被代理类
     2 class SuperMan implements Human{
     3 
     4 
     5     @Override
     6     public String getBelief() {
     7         return "I believe I can fly!";
     8     }
     9 
    10     @Override
    11     public void eat(String food) {
    12         System.out.println("我喜欢吃" + food);
    13     }
    14 }

        (3)创建代理对象

          要想实现动态代理,需要解决两个问题:

          ① 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。

          ② 问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。

     1 class ProxyFactory{
     2     //调用此方法,返回一个代理类的对象。解决问题一
     3     public static Object getProxyInstance(Object obj){//obj:被代理类的对象
     4         MyInvocationHandler handler = new MyInvocationHandler();
     5 
     6         handler.bind(obj);
     7 
     8         //参数1:被代理类对象的类加载器
     9         //参数2:被代理类对象实现的接口
    10         //参数3:InvocationHandler的子类,处理业务
    11         return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    12     }
    13 
    14 }

          InvocationHandler 的子类:MyInvocationHandler

     1 class MyInvocationHandler implements InvocationHandler{
     2 
     3     private Object obj;//需要使用被代理类的对象进行赋值
     4 
     5     public void bind(Object obj){
     6         this.obj = obj;
     7     }
     8 
     9     //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
    10     //将被代理类要执行的方法a的功能就声明在invoke()中
    11     //参数1:代理类的对象
    12     //参数2:要执行的方法
    13     //参数3:要执行方法的参数
    14     @Override
    15     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    16 
    17 
    18         //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
    19         //obj:被代理类的对象
    20         Object returnValue = method.invoke(obj,args);
    21 
    22         //上述方法的返回值就作为当前类中的invoke()的返回值。
    23         return returnValue;
    24 
    25     }
    26 }

        (4)测试

     1 public class ProxyTest {
     2 
     3     public static void main(String[] args) {
     4         SuperMan superMan = new SuperMan();
     5         //proxyInstance:代理类的对象
     6         Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
     7         //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
     8         String belief = proxyInstance.getBelief();
     9         System.out.println(belief);
    10         proxyInstance.eat("四川麻辣烫");
    11 
    12         System.out.println("*****************************");
    13 
    14         NikeClothFactory nikeClothFactory = new NikeClothFactory();
    15 
    16         ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
    17 
    18         proxyClothFactory.produceCloth();
    19 
    20     }
    21 }

    四、动态代理与AOP(Aspect Orient Programming)

      1、动态代理

        前面介绍的ProxyInvocationHandler,很难看出这种动态代理的优势,下面介绍一种更实用的动态代理机制

        

         改进后的说明:代码段1、代码段2、代码段3和深色代码段分离开了,但代码段123又和一个特定的方法A耦合了!最理想的效果是:代码块123既可以执行方法A,又无须在程序中以硬编码的方式直接调用深色代码的方法

        

      2、案例

        (1)定义接口

    1 public interface Dog{
    2     void info();
    3     void run();
    4 }

        (2)定义被代理类

    1 public class HuntingDog implements Dog{
    2     public void info(){
    3         System.out.println("我是一只猎狗");
    4     }
    5     public void run(){
    6         System.out.println("我奔跑迅速");
    7     }
    8 }

        (3)定义的通用方法

    1 public class DogUtil{
    2     public void method1(){
    3         System.out.println("=====模拟通用方法一=====");
    4     }
    5     public void method2(){
    6         System.out.println("=====模拟通用方法二=====");
    7     }
    8 }

        (4)创建代理对象

     1 public class MyProxyFactory{
     2     // 为指定target生成动态代理对象
     3     public static Object getProxy(Object target) throws Exception{
     4         // 创建一个MyInvokationHandler对象
     5         MyInvokationHandler handler = new MyInvokationHandler();
     6         
     7         // 为MyInvokationHandler设置target对象
     8         handler.setTarget(target);
     9         
    10         // 创建、并返回一个动态代理对象
    11         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces() , handler);
    12     }
    13 }

        (5)创建 invocationHandler 实现类

     1 public class MyInvocationHandler implements InvocationHandler{
     2     // 需要被代理的对象
     3     private Object target;
     4     
     5     public void setTarget(Object target){
     6         this.target = target;
     7     }
     8     
     9     // 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
    10     public Object invoke(Object proxy, Method method, Object[] args) throws Exception{
    11         DogUtil du = new DogUtil();
    12     
    13         // 执行DogUtil对象中的method1。
    14         du.method1();
    15     
    16         // 以target作为主调来执行method方法
    17         Object result = method.invoke(target , args);
    18         
    19         // 执行DogUtil对象中的method2。
    20         du.method2();
    21         
    22         return result;
    23     }
    24 }

        (6)测试

     1 public class Test{
     2     public static void main(String[] args) throws Exception{
     3     // 创建一个原始的HuntingDog对象,作为target
     4     Dog target = new HuntingDog();
     5     
     6     // 以指定的target来创建动态代理
     7     Dog dog = (Dog)MyProxyFactory.getProxy(target);
     8         dog.info();
     9         dog.run();
    10     }
    11 }

      3、动态代理与 AOP

        使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理。
        这种动态代理在AOP中被称为AOP代理, AOP代理可代替目标对象, AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:
        AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理

        

  • 相关阅读:
    C#中两个日期类型的变量如何进行比较
    Ajax基本原理讲解 (引用别人文章)
    Ajax程序设计入门
    ASP.NET中如何调用存储过程
    删除SAM文件真的能够消除XP系统管理员的密码吗?
    用XMLHttpRequest和struts实现AJAX(译)
    关于邮件系统域名(DNS)设置的小常识
    输出各种二进制流的格式
    WPF 3D编程介绍
    WPF 3D学习笔记
  • 原文地址:https://www.cnblogs.com/niujifei/p/14903664.html
Copyright © 2020-2023  润新知