现在的位置: 首页 > 综合 > 正文

设计模式-动态代理

2013年08月05日 ⁄ 综合 ⁄ 共 3781字 ⁄ 字号 评论关闭

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

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

抱歉!评论已关闭.