struts2拦截器(JDK反射,JDK动态代理)

拦截器的意义:
  大家都知道,DRY(Don't Repeat Yourself,不要书写重复的代码)规则是软件开发过程中的一条重要法则,遵守该规则所开发出来的系统将会具有较好的可维护性。拦截器思想与此也是有相似点的,可以这样理解:
拦截器是对调用方法的改进。实际上,当称某个实例是一个拦截器时,这是就其行为上而言;但从代码角度来看,拦截器就是一个类,这个类也包含方法,只是这个方法是个特殊方法,它会在目标方法
调用之前“自动”执行。
PS:上面的自动执行的自动被加上了引号,这是因为在 编程过程中,没有自动的事情,任何事情都是代码驱动的。这里的自动是指无需开发者关心,由系统来驱动。
  大部分时候,拦截器方法都是通过代理的方式来调用的,下面以JDK动态代理为例来介绍如何调用拦截器方法:
  下面先定义一个简单的Person接口,因为JDK动态代理只能对实现了接口的实例来生成代理,因此必须提供一个Person接口,该接口代码如下:
public interface Person{
 //getName方法声明
 public void getName();
 //getAge方法声明
 public void getAge();
}
上面接口里简单定义了两个方法,并未提供方法实现,为了能正常使用该Person实例,必须提供该接口的实现类,实现类PersonImpl代码如下:
public class PersonImpl implements Person{
 //getName方法实现
 public void getName(){
  System.out.println("My name is jack!"); 
 }
 //getAge方法实现
 public void getAge(){
  System.out.println("I am 23!");
 }
}
  上面代码没有丝毫的特别之处,该Person的实现类仅仅为每个方法提供了一个简单实现。
  系统用户拦截Person实例的拦截器类如下:
  //定义系统拦截器类
  public class PersonIntercepter{
   //第一个拦截器方法
   public void method1(){
    System.out.println("=====模拟通用方法一=====");
   }
   //第二个拦截器方法
   public void method2(){
    System.out.println("=====模拟通用方法二=====");
   }
  }
  上面的拦截器没有我们想象的那么神奇,拦截器也是一个普通的Java类。称它为拦截器,只是就它的行为而言,并不是它在代码实现上与普通Java类有什么特别之处。
  假设我们要在getName方法执行前后分别调用拦截器里的方法,系统应该如何实现呢?关键是下面的ProxyHandler类,该类需要实现InvocationHandler接口,该接口是JDK反射体系里的一个接口,它可以动态调用目标对象的方法。
public class ProxyHandler implements InvocationHandler{
 //需要被代理的目标对象
 private Object target;
 //创建拦截器实例
 PersonIntercepter pi = new PersonIntercepter();
 //执行代理的目标方法时,该invoke方法会被自动调用
 public Object invoke(Object proxy, Method method, Object[] args) throws Exception{
  Object result = null;
  //如果被调用方法的方法名为getName
  if(method.getName().equals("getName")){
   //调用拦截器方法1
   pi.method1();
   result = method.invoke(target, args);
   //调用拦截器方法2
   pi.method2();
  }else{
   result = method.invoke(target, args);
  }
  return result;
 }
 //用户设置传入目标对象的方法
 public void setTarget(Object o){
  this.target = o;
 }
}
从上面代码中看出,这个类与前面的Person类没有丝毫关联之处,如果硬要说有关联,也就是在invoke方法中判断了目标方法的方法名是否为getName——至于这个方法是否属于Person实例,它并不关心。
通过ProxyHandler的帮助,系统实现了在执行getName方法之前,调用拦截器的method1方法;在指定getName方法之后,调用拦截器的method2方法。
正因为上面类与被拦截对象没有丝毫耦合,从而提供了更好解耦,以及更好的可扩展性。但上面的类与两个地方耦合了:
1、与拦截器类耦合:上面类固定使用PersonIntercepter拦截器。
2、与被拦截的方法耦合:上面类指定拦截名为getName的方法。
正是通过这两个地方的耦合,系统才能够知道在调用哪个拦截器的方法,以及需要拦截的目标对象。当然,这两个耦合也需要解决,通常的解决方案是将拦截器类放在配置文件中指定,将需要被拦截的目标方法也放在配置文件中指定。
系统还需要提供一个代理工厂,代理工厂的主要作用就是根据目标对象生成一个代理对象。
public class MyProxyfactory{
 /**
  * 实例Service对象
  * @param serviceName String
  * @return Object
  */
  public static Object getProxy(Object object){
   //代理的处理类
   ProxyHandler handler = new ProxyHandler();
   //把该Person实例托付给代理操作
   handler.setTarget(object);
   //第一个参数是用来创建动态代理的ClassLoader对象,只要该对象能访问Person接口即可
   //第二个参数是接口数组,正是代理该接口数组
   //第三个参数是代理包含的处理实例
   return Proxy.newProxyInstance(PersonImpl.class.getClassLoader(), object.getClass().getInterfaces(), handler);
  }
}
Proxy.newProxyInstance方法根据接口数组动态创建代理类实例,接口数组通过object.getClass().getInterfaces()方法获得,创建的代理类是JVM在内存中动态创建,该类实现传入
参数里接口数组中的全部接口。因此,Dynamic Proxy要求被代理的必须是接口的实现类,否则无法为其构造相应的动态实例。
从上面的介绍可以看出,代理工厂负责根据目标对象和对应的拦截器生成新的代理对象,代理对象里的方法是目标方法和拦截器方法的组合。正是通过这种方式,实现了在目标方法之前或者之后,自动调用拦截器方法的目的。
下面是主程序:
public class Test{
 public static void main(String[] args){
  //创建一个Person实例,该实例将被作为代理的目标对象
  Person targetObject = new PersonImpl();
  Person person = null;
  //以目标对象创建代理
  Object proxy = MyProxyFactory.getProxy(targetObject);
  if(proxy instanceof Person){
   person = (Person)proxy;
  }
  //测试代理的方法
  person.getName();
  person.getAge();
 }
}
上面代码先创建了一个Person实例作为目标对象,然后以该目标对象作为“蓝本”,创建该对象的代理对象——代理对象里的方法,是拦截器方法和目标对象方法的组合,这就是为什么可以在执行目标方法的前后,执行拦截器方法的原因。

拦截器和AOP的关系:
  拦截器与AOP(Aspect Orient Program,面向切面编程)是密切相关的,AOP从程序运行角度来考虑程序的流程,取得业务处理过程的切面,在特定切面通过系统自动插入特定方法。
AOP面向的是程序运行中各个步骤,以一种松耦合方式来组合业务处理的各个步骤。
  在AOP的编程方式中,有三个重要概念:
  1、目标对象:包含被拦截方法的原始对象。
  2、被插入的处理方法:定义在拦截器中,会在被拦截方法之前、之后自动执行的方法。
  3、代理对象:以目标对象为蓝本,由系统创建的新对象。
  在上面的三个概念中,被插入的处理方法不能独立存在,因为方法必须有一个存在的载体——这个载体就是拦截器,拦截器就是包含处理方法的特殊实例。
  代理对象也称为AOP代理,就是由系统动态生成的一个对象,该对象将代替目标对象来使用,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与
目标对象的方法存在差异:AOP方法在特定切面插入拦截器方法,在处理之间回调目标对象的方法,从而实现了在执行目标方法之前或之后调用拦截器方法,放佛拦截器
拦截了原有的目标方法一样。
  事实上,Struts2的拦截器机制来自于WebWork的拦截器机制,这种拦截器机制就是基于AOP的思想。

你可能感兴趣的:(AOP,jvm,jdk,编程,Webwork)