• 设计模式动态代理



    随着学习的东西原来越多,发现设计模式越来越重要,很多是辛苦的想着要解决代码中的耦合问题,其实这些东西都已经被总计出来,并归纳为设计模式。这就是我们要去加强学习设计模式的原因。

    关于设计模式,其中感触最深的就是动态代理。刚开始接触这个模式的时候并不知道这个就是代理模式,只是在spring框架中的aop思想用到。后到听老师的设计模式,原来Spring框架就是运用了 代理来实现面向切面编程的。

    代理模式其实在很多地方都可以用到,如关于系统的日志记录功能,和记录操作时间等额外操作,都可以利用代理来实现。

    我想要弄清楚动态代理,那么就必须先要了解反射机制。准确理解反射,应该理解类的加载过程及Class相关的东西。这里大概讲一下类的加载过程,比如说要new一个对象,会是如下步骤:

    1) 在可知的路径下查找是否存在该类文件

    2) 使用相应的ClassLoader加载类文件

    3) 加载.class文件时,会初始化static部分

    4) 分配相应的内存空间来存储相关数据

    5) 内存空间清零(即获得默认值)

    6) 构造基类(只有基类构造完成,才能构造子类)

    7) 初始化成员

    8) 构造该类的对象

    反射机制中Class类是一个入口和核心:

    1) 得到该类对应的Class对象

    2) 由类的Class对象产生该类的实例(利用构造器)

    3) 由类的Class对象动态得到类的属性和方法

    4) 运行时动态调用对象的任意属性和方法

    5) 运行时动态产生一个对象的代理对象

    得到该类对应的Class对象三种方式:

    1) Class.forName(“类的完整字符串名字")

    2) 类名.class

    3) 对象.getClass()

    代表了load到内存中的Class对象

    Object的getClass()可以拿到该类对象(=类名.class)

    Class的getClassLoader可以拿到装载这个class的ClassLoader

    看下面的一个练习的例子,我们可以看如下代码来模拟框架配置文件功能:

          /**
    
     * 
    
     * IO流读取文件
    
     * ***/
    
        InputStream fis =
    
                    new FileInputStream("src/config.properties");
    
    
    Properties props = new Properties();
    
                props.load(fis);
    
    fis.close();
    
    /**
    
     * 获取配置文件属性
    
     * */
    
    String className = props.getProperty("className");
    
    System.out.println("className : " + className);
    
    String methodName = props.getProperty("methodName");
    
    System.out.println("methodName : " + methodName);
    
    String result = props.getProperty("result");
    
    System.out.println("result : " + result);
    
    
    /**
    
     * 以下为反射代码
    
     * ***/
    
    Class<?> clazz = Class.forName(className);
    
    Object obj = clazz.newInstance();
    
    
    Method method = clazz.getMethod(methodName);
    
    
    String resultVal = (String) method.invoke(obj);
    
    
    /**
    
     * 实现servlet接口跳转
    
     * 
    
     * ***/
    
    if (result.equals(resultVal)) {
    
    System.out.println("跳转index.jsp");
    
    } else {
    
    System.out.println("返回");
    
    }

    那么以上程序就可以实现通过工程中的配置文件config.properties中的配置去模拟框架中的配置文件实现的功能。

    Class<?> clazz = Class.forName(className);通过反射加载className对应的类文件,再去调用newInstance()来实例化对象。然后再接收方法名称,最后通过Method类的invoke()方法执行传入的方法。这就实现了方法的动态调用。可以实现让用户通过配置文件来修改系统。增强系统的灵活性。当需求改变的时候不再需要修改源文件。

    动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandlder接口提供了生成动态代理类的能力.先来看一下动态代理的架构图:

    根据上面的架构图可以看出来,这里的代理类是和目标类或者说是目标是实现了同一个父类接口。它们只是兄弟的关系,但不是互相并不互相认识。通常代理类会目标类多如一些功能,这也就是代理的功能所在。利用代理类去完成日志的记录,权限的控制功能。

    那么现在就让我们来了解一下如何使用 Java 动态代理。具体有如下四步骤:

    1) 通过实现 InvocationHandler 接口创建自己的调用处理器;

    2) 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建 动态代理类;

    3) 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

    4) 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入

    /**
    
     *代理类中的源代码
    
     */
    
        public class MyProxy implements InvocationHandler{
    
    
    private Object obj;
    
    
    public MyProxy(Object obj){
    
    this.obj = obj;
    
    }
    
    
    @Override
    
    public Object invoke(Object proxy, Method method,
    
                 Object[] args)throws Throwable {
    
    start();
    
    Object o = method.invoke(obj,args);
    
    end();
    
    return o;
    
    }
    
    
    public static void start(){
    
    System.out.println("start...");
    
    }
    
    
    public static void end(){
    
    System.out.println("end...");
    
    }
    
    }
     

     代理类MyProxy实现InvocationHandler接口,实现invoke方法。该方法三个参数 proxy(被代理类),method(被调用的方法),args(传入的参数).这些参数会通过反射机制拿到并出去方法中。该方法会通过method的invoke()方法去调用传入的方法。当然invoke方法的前面和后面可以别个一些额外的操作,如日志功能。


     

    /**
    
     *Human为Person类的父类接口。 
    
     **/
    
        public static void dynamicProxyByConstructor() 
    
                          throws Exception{
    
      Person person = new Person();
    
    
      MyProxy myProxy = new MyProxy(person);
    
      Class<?> clazzProxy =
    
                  Proxy.getProxyClass(Person.class.getClassLoader(), 
    
                  Person.class.getInterfaces());
    
      Constructor<?> constructor =
    
                  clazzProxy.getConstructor(InvocationHandler.class);
    
      Human personPrxoy =
    
                  (Human)constructor.newInstance(myProxy);
    
    personPrxoy.save();
    
    }

    这利用java中的Proxy类动态生成一个代理类。上面说到代理类是需要实现和目标类一样的接口。所以这里需要传入二个参数.第一个被代理类的加载对象,其实也就相当于是代理对象。因为Proxy会通过类加载器找到被代理类的Class文件,第二个参数就为被代理类实现的接口.然后通过反射得到构造对象,实例化对象生成代理类,最后通过代理类去调用 save()方法。那么法代理类调用save方法的时候,会反射去指定代理类中的invoke方法。这时invoke接受到被代理类,需要执行的save()方法,以及传入的参数。然后根据这些参数执行方法。完成动态代理。

    关于在spring中AOP也是底层运用了java中的动态代理。通过在配置文件中配置连接点和切面等信息。通过通过动态代理来实现在连接点前后执行切面类中的方法.还有hibernate中的懒加载其实也是应用了动态代理。只有当应用到对象的时候才去建立对象。

    以上就是java中的动态代理就一些个人的理解。另外通过这次学了设计模式,感觉学到了很多东西。以前写代码,总是不断的去重复代码。现在学了设计知道国面向接口编程,面向抽象编程。我们需要做在不仅仅是实现程序,更重要的是写出高质量的代码,即使在需求做出变动的时候,也尽可能的减少需要修改的代码。当然,设计模式虽然好,还是需要慎重选择应用,毕竟没有最好的设计模式,只有最适合的设计模式。

  • 相关阅读:
    优先队列总结
    CodeForces 567D One-Dimensional Battle Ships
    CodeForces 567D One-Dimensional Battle Ships
    codeforces 1016B. Segment Occurrences
    codeforces 1016B. Segment Occurrences
    poj3249(求最长路)
    poj3249(求最长路)
    poj 2186
    2017年第八蓝桥杯C/C++ A组国赛 —— 第二题:生命游戏
    Fence Repair POJ
  • 原文地址:https://www.cnblogs.com/yangzhi/p/3576630.html
Copyright © 2020-2023  润新知