随笔 - 63  文章 - 0  trackbacks - 0
<2025年3月>
2324252627281
2345678
9101112131415
16171819202122
23242526272829
303112345

常用链接

留言簿(2)

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

利用Java的反射与代理实现AOP

 

一.AOP概述
       AOP(Aspect Oriented Programing),即面向切面编程,它主要用于日志记录、性能统计、控制、事务处理、异常处理等方面。它的主要意图就要将日志记录,性能统计,安全控制、事务处理、异常处理等等代码从业务逻辑代码中清楚地划分出来。通过对这些行为的分离,我们希望可以将它们独立地配置到业务逻辑方法中,而要改变这些行为的时候也不需要影响到业务逻辑方法代码。
       下面让我们来看一个利用AOP来实现日志记录的例子,在没有使用AOP之前,我们的代码如下面所讲述。
       下面这段代码为业务的接口类代码:

package org.amigo.proxy; /** * 业务逻辑类接口. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:09:53 */ public interface BusinessObj { /** * 执行业务. */ public void process(); } BusinessObj接口的某个实现类代码如下: package org.amigo.proxy; /** * 业务逻辑对象实现类. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:11:49 */ public class BusinessObjImpl implements BusinessObj { /** * 执行业务. */ public void process() { try { System.out.println("before process"); System.out.println("执行业务逻辑"); System.out.println("after process"); } catch (Exception e) { System.err.println("发生异常:" + e.toString()); } } }
    在上例中我们可以看到,在执行业务方法前、执行业务方法后以及异常发生时的日志记录,我们都是通过在对应的类中写入记录日志的代码来实现的,当有这种日志记录需求的业务逻辑类不断增多时,将会给我们的维护带来很大困难,而且,在上面的例子中,日志代码和业务逻辑代码混合在一起,为日后的维护工作又抹上了一层“恐怖”色彩。
       按照AOP的思想,我们首先需要寻找一个切面,在这里我们已经找到,即在业务逻辑执行前后以及异常发生时,进行相应的日志记录。我们需要将这部分日志代码放入一个单独的类中,以便为以后的修改提供方便。
       我们在截获某个业务逻辑方法时,可以采用Java的动态代理机制来实现。
 
 
二.Java的动态代理机制
代理模式是常用的Java设计模式。代理类主要负责为委托类预处理消息、过滤信息、把消息转发给委托类,以及事后处理信息等。
动态代理类不仅简化了编程工作,而且提高了软件系统的扩展性和可维护性。我们可以通过实现java.lang.reflect.InvocationHandler接口提供一个执行处理器,然后通过java.lang.reflect.Proxy得到一个代理对象,通过这个代理对象来执行业务逻辑方法,在业务逻辑方法被调用的同时,自动调用会执行处理器。
      
下面让我们来创建一个日志拦截器类LogInterceptor.java文件,该类实现java.lang.reflect.InvocationHandler接口,其内容如下所示:

package org.amigo.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 日志拦截器,用来进行日志处理. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:31:44 */ public class LogInterceptor implements InvocationHandler { private Object delegate; /** * 构造函数,设置代理对象. */ public LogInterceptor(Object delegate){ this.delegate = delegate; } /** * 方法的调用. * @param proxy * @param method 对应的方法 * @param args 方法的参信息 * @return 返回操作结果对象 * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { System.out.println("before process" + method); //调用代理对象delegate的method方法,并将args作为参数信息传入 result = method.invoke(delegate, args); System.out.println("after process" + method); } catch (Exception e){ System.err.println("发生异常:" + e.toString()); } return result; } /** * 测试方法. * @param args * @throws Exception */ public static void main(String[] args) throws Exception { BusinessObj obj = new BusinessObjImpl(); //创建一个日志拦截器 LogInterceptor interceptor = new LogInterceptor(obj); //通过Proxy类的newProxyInstance(...)方法来获得动态的代理对象 BusinessObj proxy = (BusinessObj) Proxy.newProxyInstance( BusinessObjImpl.class.getClassLoader(), BusinessObjImpl.class.getInterfaces(), interceptor); //执行动态代理对象的业务逻辑方法 proxy.process(); } }
 
此时还需要对BusinessObj的实现类BusinessObjImpl类进行修改,去掉其日志记录等内容,修改后的文件如下:

package org.amigo.proxy; /** * 业务逻辑对象实现类. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:11:49 */ public class BusinessObjImpl implements BusinessObj { /** * 执行业务. */ public void process() { System.out.println("执行业务逻辑"); } }
运行LogInterceptor类我们可以发现,它实现了前面所需要的功能,但是很好的将业务逻辑方法的代码和日志记录的代码分离开来,并且所有的业务处理对象都可以利用该类来完成日志的记录,防止了重复代码的出现,增加了程序的可扩展性和可维护性,从而提高了代码的质量。那么Spring中的AOP的实现是怎么样的呢?接着让我们来对Spring的AOP进行探讨,了解其内部实现原理。
三.Spring中AOP的模拟实现
       在学习了Java的动态代理机制后,我们在本节中将学习Java的动态代理机制在Spring中的应用。首先我们创建一个名为AopHandler的类,该类可生成代理对象,同时可以根据设置的前置或后置处理对象分别在方法执行前后执行一些另外的操作,该类的内容如下所示:

package org.amigo.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * AOP处理器. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午10:13:28 */ public class AopHandler implements InvocationHandler { //需要代理的目标对象 private Object target; //方法前置顾问 Advisor beforeAdvisor; //方法后置顾问 Advisor afterAdvisor; /** * 设置代理目标对象,并生成动态代理对象. * @param target 代理目标对象 * @return 返回动态代理对象 */ public Object setObject(Object target) { //设置代理目标对象 this.target = target; //根据代理目标对象生成动态代理对象 Object obj = Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return obj; } /** * 若定义了前置处理,则在方法执行前执行前置处理, * 若定义了后置处理,则在方法调用后调用后置处理. * @param proxy 代理对象 * @param method 调用的业务方法 * @param args 方法的参数 * @return 返回结果信息 * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //进行业务方法的前置处理 if (beforeAdvisor != null) { beforeAdvisor.doInAdvisor(proxy, method, args); } //执行业务方法 Object result = method.invoke(target, args); //进行业务方法的后置处理 if (afterAdvisor != null) { afterAdvisor.doInAdvisor(proxy, method, args); } //返回结果对象 return result; } /** * 设置方法的前置顾问. * @param advisor 方法的前置顾问 */ public void setBeforeAdvisor(Advisor advisor) { this.beforeAdvisor = advisor; } /** * 设置方法的后置顾问. * @param advisor 方法的后置顾问 */ public void setAfterAdvisor(Advisor advisor) { this.afterAdvisor = advisor; } }
    在上类中,前置和后置顾问对象都继承Advisor接口,接下来让我们来看看顾问接口类的内容,该类定义了doInAdvisor(Object proxy, Method method, Object[] args)方法,如下所示:
package org.amigo.proxy; import java.lang.reflect.Method; /** * * 顾问接口类. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午10:21:18 */ public interface Advisor { /** * 所做的操作. */ public void doInAdvisor(Object proxy, Method method, Object[] args); } BeforeMethodAdvisor和AfterMethodAdvisor都实现了Advisor接口,分别为方法的前置顾问和后置顾问类。 BeforeMethodAdvisor.java文件(前置顾问类)的内容如下所示: package org.amigo.proxy; import java.lang.reflect.Method; /** * * 方法前置顾问,它完成方法的前置操作. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午10:19:57 */ public class BeforeMethodAdvisor implements Advisor { /** * 在方法执行前所进行的操作. */ public void doInAdvisor(Object proxy, Method method, Object[] args) { System.out.println("before process " + method); } } AfterMethodAdvisor.java文件(后置顾问类)的内容如下所示: package org.amigo.proxy; import java.lang.reflect.Method; /** * * 方法的后置顾问,它完成方法的后置操作. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午10:20:43 */ public class AfterMethodAdvisor implements Advisor { /** * 在方法执行后所进行的操作. */ public void doInAdvisor(Object proxy, Method method, Object[] args) { System.out.println("after process " + method); } }
这两个类分别在方法执行前和方法执行后做一些额外的操作。
       对于在配置文件中对某个bean配置前置或后置处理器,我们可以在bean中增加两个属性aop和aopType,aop的值为对应的前置顾问类或后置顾问类的名称,aopType用于指明该顾问类为前置还是后置顾问,为before时表示为前置处理器,为after时表示为后置处理器,这时候我们需要修改上一篇文章中的BeanFactory.java这个文件,添加其对aop和aopType的处理,修改后的该文件内容如下:

package org.amigo.proxy; import java.io.InputStream; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * bean工厂类. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 下午04:04:34 */ public class BeanFactory { private Map<String, Object> beanMap = new HashMap<String, Object>(); /** * bean工厂的初始化. * @param xml xml配置文件 */ public void init(String xml) { try { //读取指定的配置文件 SAXReader reader = new SAXReader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); InputStream ins = classLoader.getResourceAsStream(xml); Document doc = reader.read(ins); Element root = doc.getRootElement(); Element foo; //创建AOP处理器 AopHandler aopHandler = new AopHandler(); //遍历bean for (Iterator i = root.elementIterator("bean"); i.hasNext();) { foo = (Element) i.next(); //获取bean的属性id、class、aop以及aopType Attribute id = foo.attribute("id"); Attribute cls = foo.attribute("class"); Attribute aop = foo.attribute("aop"); Attribute aopType = foo.attribute("aopType"); //配置了aop和aopType属性时,需进行拦截操作 if (aop != null && aopType != null) { //根据aop字符串获取对应的类 Class advisorCls = Class.forName(aop.getText()); //创建该类的对象 Advisor advisor = (Advisor) advisorCls.newInstance(); //根据aopType的类型来设置前置或后置顾问 if ("before".equals(aopType.getText())) { aopHandler.setBeforeAdvisor(advisor); } else if ("after".equals(aopType.getText())) { aopHandler.setAfterAdvisor(advisor); } } //利用Java反射机制,通过class的名称获取Class对象 Class bean = Class.forName(cls.getText()); //获取对应class的信息 java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean); //获取其属性描述 java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors(); //设置值的方法 Method mSet = null; //创建一个对象 Object obj = bean.newInstance(); //遍历该bean的property属性 for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) { Element foo2 = (Element) ite.next(); //获取该property的name属性 Attribute name = foo2.attribute("name"); String value = null; //获取该property的子元素value的值 for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) { Element node = (Element) ite1.next(); value = node.getText(); break; } for (int k = 0; k < pd.length; k++) { if (pd[k].getName().equalsIgnoreCase(name.getText())) { mSet = pd[k].getWriteMethod(); //利用Java的反射极致调用对象的某个set方法,并将值设置进去 mSet.invoke(obj, value); } } } //为对象增加前置或后置顾问 obj = (Object) aopHandler.setObject(obj); //将对象放入beanMap中,其中key为id值,value为对象 beanMap.put(id.getText(), obj); } } catch (Exception e) { System.out.println(e.toString()); } } /** * 通过bean的id获取bean的对象. * @param beanName bean的id * @return 返回对应对象 */ public Object getBean(String beanName) { Object obj = beanMap.get(beanName); return obj; } /** * 测试方法. * @param args */ public static void main(String[] args) { BeanFactory factory = new BeanFactory(); factory.init("config.xml"); BusinessObj obj = (BusinessObj) factory.getBean("businessObj"); obj.process(); } }
    观察此类我们可以发现,该类添加了对bean元素的aop和aopType属性的处理。编写完该文件后,我们还需要修改src目录下的配置文件:config.xml文件,在该文件中添加名为businessObj的bean,我们为其配置了aop和aopType属性,增加了方法的前置顾问。增加的部分为:

<bean id="businessObj" class="org.amigo.proxy.BusinessObjImpl" aop="org.amigo.proxy.BeforeMethodAdvisor" aopType="before"/>
       此时运行BeanFactory.java这个类文件,运行结果如下:
before process public abstract void org.amigo.proxy.BusinessObj.process()
执行业务逻辑
       由运行结果可以看出,前置处理已经生效。
       本节中的例子只是实现了Spring的AOP一小部分功能,即为某个bean添加前置或后置处理,在Spring中,考虑的比这多很多,例如,为多个bean配置动态代理等等。但是究其根源,Spring中AOP的实现,是基于Java中无比强大的反射和动态代理机制。
 
四.总结
       本文讲述了AOP的概念等信息,并详细讲解了Java的动态代理机制,接着又通过一个简单的实例讲解了Spring中AOP的模拟实现,使得读者能够更好地学习Java的反射和代理机制。
通过这两篇文章,使得我们能够更加深入地理解Java的反射和动态代理机制,同时对Spring中盛行的IOC和AOP的后台实现原理有了更加清晰的理解,Java的反射和动态代理机制的强大功能在这两篇文章中可见一斑。有兴趣的朋友可以通过学习Spring框架的源码来进一步的理解Java的反射和动态代理机制,从而在实际的开发工作中更好地理解
posted @ 2009-04-06 11:50 lanxin1020 阅读(165) | 评论 (0)编辑 收藏
     摘要: spring IOC 机制模拟实现收藏            在Java中,其反射和动态代理机制极其强大,我们可以通过其反射机制在运行时获取信息。而代理是一种基本的设计模式,它是一种为了提供额外的或不同的操作而插入到真实对象中的某个对象。而Java的动态代理在代理上更进一步,既能动态的创建代理对象,又...  阅读全文
posted @ 2009-04-06 11:43 lanxin1020 阅读(469) | 评论 (0)编辑 收藏

工厂模式是最重要的模式,因为大多数模式都需要用到工厂模式。如果不能正确的运用工厂模式,那么可以说无法成为合格的架构师。
多数设计模式的内容讲解的都是如何设计接口。
接口如何产生呢?如果在客户代码(类库的使用者称之为客户)中直接使用具体类,那么就失去了接口的意义。因为接口的使用目的,就是要降低客户对具体类的依赖程度。如果在客户代码中直接使用接口,那么就造成了客户对具体类名称的依赖。(客户最终需要以某种方式指明所需要的具体类,如配置文件或代码,但是只需要指出一次,所以说降低对具体类的依赖程度)。要使客户代码不依赖具体类,唯一的方法,就是让客户代码不依赖具体类的部分不知道具体类的名称。知道具体类名称的部分,仅仅是配置部分。(配置文件或者配置代码)。
依赖不可能完全消除,除非二者毫无联系。但是可以将这种依赖的程度降到最低。
既然不能直接创建具体类,那么就需要通过一个创建者类来创建接口的实现类。这样就产生了工厂类。
那么现在已经知道工厂类存在的理由,抽象创建接口的过程。
这样,就可以使用简单工厂。
简单工厂,一般是两级结构。工厂类创建接口。
随着接口创建复杂性的增强,可能在接口创建的过程中,一个创建者类,无法承担创建所有的接口类的职责。
可能会有这样的情况,我们定义了一个接口,有6个实现类分别是123456号。但是,这六个实现类不可能用一个工厂创建出来,因为123号是 windows下的实现,而456号是linux上的实现。(假设我们使用的语言不是广大人民群众热爱的java语言),那么这个时候,我还需要客户方用相同的方式来创建这个借口,而不是在代码中到处写

代码
     if  (操作系统 == " windows " ){  
      
    }  
     
else {  
      
    }  

那样就太麻烦了。设计模式就是为了减少麻烦,而不是什么别的废话,比如什么太极八卦、天人合一、面向xx之类的。因为怕麻烦,所以搞出设计模式这个咚咚减少麻烦。如果你发现用了设计模式更麻烦了,那么肯定是你用错了。
这个时候为了省事,我就把工厂也抽象成一个接口(因为我有两个相似的工厂,如果只有一个,我还废话什么呢),这样就成了工厂方法。
当然,既然工厂方法成了一个接口,那么当然也需要用一个工厂来创建它。这个时候,创建是三级结构,简单工厂(此时是工厂的工厂)创建工厂接口(本来是个类,现在因为进一步的抽象,成为接口了),工厂接口创建产品。
过了一段时间,随着我们的工厂业务不断发展,我们有了很多产品。
比如,我们有锤子和钉子两种产品。这两种产品都有windows品牌和linux品牌的。我们给锤子和钉子各自定义了一个创建的接口。
代码
    interface 锤子工厂{  
    造锤子();  
    }  
    
interface 钉子工厂{  
    造钉子();  
    }  

可是,我们发现某些用户,用windows的锤子去敲linux的钉子,从而把程序敲出了bug。这当然是我们的错误,因为我们违反了一条金科玉律:
要想使你的程序稳定运行,你假设用户是猪。
所以,我们把锤子和钉子的工厂合并,让一个工厂只能造出配套的锤子和钉子,这样猪就没有犯错误的机会了。
于是我们搞出一个抽象工厂:
interface 铁匠铺{
造锤子();
造钉子();

当然,这个铁匠铺是个接口,所以同样需要用一个工厂来创建它。所以,这个时候,工厂还是三级结构。
我们的工厂,业务很多,而且产品一般都是配套使用的(这样可以多骗点钱),所以,我们大多数情况下,都是使用抽象工厂和简单工厂。简单工厂用来创建工厂,抽象工厂创建产品。
工厂的作用,就是创建接口。
其实我们不知道什么是设计模式,我们只是怕麻烦。什么是麻烦呢?
我们觉得把同样的代码写两遍就非常麻烦。所以,我们宁可多写几句,也要解决麻烦。猪不怕麻烦,可以日复一日的重复相同的事情,可是我们不是猪。





例子:
public interface Plant { }//标志接口     
  • //具体产品PlantA,PlantB       
  • public class PlantA implements Plant {       
  •      
  •  public PlantA () {       
  •   System.out.println("create PlantA !");       
  •  }       
  •      
  •  public void doSomething() {       
  •   System.out.println(" PlantA do something ...");       
  •  }       
  • }       
  • public class PlantB implements Plant {       
  •  public PlantB () {       
  •   System.out.println("create PlantB !");       
  •  }       
  •      
  •  public void doSomething() {       
  •   System.out.println(" PlantB do something ...");       
  •  }       
  • }       
  • // 产品 Fruit接口       
  • public interface Fruit { }       
  • //具体产品FruitA,FruitB       
  • public class FruitA implements Fruit {       
  •  public FruitA() {       
  •   System.out.println("create FruitA !");       
  •  }       
  •  public void doSomething() {       
  •   System.out.println(" FruitA do something ...");       
  •  }       
  • }       
  • public class FruitB implements Fruit {       
  •  public FruitB() {       
  •   System.out.println("create FruitB !");       
  •  }       
  •  public void doSomething() {       
  •   System.out.println(" FruitB do something ...");       
  •  }       
  • }       
  • // 抽象工厂方法       
  • public interface AbstractFactory {       
  •  public Plant createPlant();       
  •  public Fruit createFruit();       
  • }       
  • //具体工厂方法       
  • public class FactoryA implements AbstractFactory {       
  •  public Plant createPlant() {       
  •   return new PlantA();       
  •  }       
  •  public Fruit createFruit() {       
  •   return new FruitA();       
  •  }       
  • }       
  • public class FactoryB implements AbstractFactory {       
  •  public Plant createPlant() {       
  •   return new PlantB();       
  •  }       
  •  public Fruit createFruit() {       
  •   return new FruitB();       
  •  }       
  • }     
  •  



    public Client {      

  •       public method1() {      
  •              AbstractFactory instance = new FactoryA();      
  •              instance.createPlant();      
  •        }      
  • }  

  •  

    posted @ 2009-04-06 10:02 lanxin1020 阅读(142) | 评论 (0)编辑 收藏

    Spring的模块化是很强的,各个功能模块都是独立的,我们可以选择的使用。这一章先从Spring的IoC开始。所谓IoC就是一个用XML来定义生成对象的模式,我们看看如果来使用的。

       数据模型

       1、如下图所示有三个类,Human(人类)是接口,Chinese(中国人)是一个子类,American(美国人)是另外一个子类。

    源代码如下:

    java 代码
    1. package cn.com.chengang.spring;   
    2. public interface Human {   
    3. void eat();   
    4. void walk();   
    5. }   
    6.   
    7. package cn.com.chengang.spring;   
    8. public class Chinese implements Human {   
    9. /* (非 Javadoc)  
    10. * @see cn.com.chengang.spring.Human#eat()  
    11. */  
    12. public void eat() {   
    13. System.out.println("中国人对吃很有一套");   
    14. }   
    15.   
    16. /* (非 Javadoc)  
    17. * @see cn.com.chengang.spring.Human#walk()  
    18. */  
    19. public void walk() {   
    20. System.out.println("中国人行如飞");   
    21. }   
    22. }   
    23.   
    24. package cn.com.chengang.spring;   
    25. public class American implements Human {   
    26. /* (非 Javadoc)  
    27. * @see cn.com.chengang.spring.Human#eat()  
    28. */  
    29. public void eat() {   
    30. System.out.println("美国人主要以面包为主");   
    31. }   
    32.   
    33. /* (非 Javadoc)  
    34. * @see cn.com.chengang.spring.Human#walk()  
    35. */  
    36. public void walk() {   
    37. System.out.println("美国人以车代步,有四肢退化的趋势");   
    38. }   
    39. }  

     

    2、对以上对象采用工厂模式的用法如下

       创建一个工厂类Factory,如下。这个工厂类里定义了两个字符串常量,所标识不同的人种。getHuman方法根据传入参数的字串,来判断要生成什么样的人种。

    java 代码
    1. package cn.com.chengang.spring;   
    2. public class Factory {   
    3. public final static String CHINESE = "Chinese";   
    4. public final static String AMERICAN = "American";   
    5.   
    6. public Human getHuman(String ethnic) {   
    7. if (ethnic.equals(CHINESE))   
    8. return new Chinese();   
    9. else if (ethnic.equals(AMERICAN))   
    10. return new American();   
    11. else  
    12. throw new IllegalArgumentException("参数(人种)错误");   
    13. }   
    14. }  

     

    下面是一个测试的程序,使用工厂方法来得到了不同的“人种对象”,并执行相应的方法。

    java 代码
    1. package cn.com.chengang.spring;   
    2. public class ClientTest {   
    3. public static void main(String[] args) {   
    4. Human human = null;   
    5. human = new Factory().getHuman(Factory.CHINESE);   
    6. human.eat();   
    7. human.walk();   
    8. human = new Factory().getHuman(Factory.AMERICAN);   
    9. human.eat();   
    10. human.walk();   
    11. }   
    12. }  

     

     3、采用Spring的IoC的用法如下:

       在项目根目录下创建一个bean.xml文件

    xml 代码
    1. <?xml version="1.0" encoding="UTF-8"?>   
    2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">   
    3. <beans>   
    4. <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>   
    5. <bean id="American" class="cn.com.chengang.spring.American"/>   
    6. </beans>  

    修改ClientTest程序如下:

    java 代码
    1. package cn.com.chengang.spring;   
    2. import org.springframework.context.ApplicationContext;   
    3. import org.springframework.context.support.FileSystemXmlApplicationContext;   
    4. public class ClientTest {   
    5. public final static String CHINESE = "Chinese";   
    6. public final static String AMERICAN = "American";   
    7.   
    8. public static void main(String[] args) {   
    9. // Human human = null;   
    10. // human = new Factory().getHuman(Factory.CHINESE);   
    11. // human.eat();   
    12. // human.walk();   
    13. // human = new Factory().getHuman(Factory.AMERICAN);   
    14. // human.eat();   
    15. // human.walk();   
    16.   
    17. ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");   
    18. Human human = null;   
    19. human = (Human) ctx.getBean(CHINESE);   
    20. human.eat();   
    21. human.walk();   
    22. human = (Human) ctx.getBean(AMERICAN);   
    23. human.eat();   
    24. human.walk();   
    25. }   
    26. }  

     从这个程序可以看到,ctx就相当于原来的Factory工厂,原来的Factory就可以删除掉了。然后又把Factory里的两个常量移到了ClientTest类里,整个程序结构基本一样。

       再回头看原来的bean.xml文件的这一句:

    <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>

       id就是ctx.getBean的参数值,一个字符串。class就是一个类(包名+类名)。然后在ClientTest类里获得Chinese对象就是这么一句

    human = (Human) ctx.getBean(CHINESE);

       因为getBean方法返回的是Object类型,所以前面要加一个类型转换。

       总结

       (1)也许有人说,IoC和工厂模式不是一样的作用吗,用IoC好象还麻烦一点。

       举个例子,如果用户需求发生变化,要把Chinese类修改一下。那么前一种工厂模式,就要更改Factory类的方法,并且重新编译布署。而IoC只需要将class属性改变一下,并且由于IoC利用了Java反射机制,这些对象是动态生成的,这时我们就可以热插拨Chinese对象(不必把原程序停止下来重新编译布署)

       (2)也许有人说,即然IoC这么好,那么我把系统所有对象都用IoC方式来生成。

       注意,IoC的灵活性是有代价的:设置步骤麻烦、生成对象的方式不直观、反射比正常生成对象在效率上慢一点。因此使用IoC要看有没有必要,我认为比较通用的判断方式是:用到工厂模式的地方都可以考虑用IoC模式。

       (3)在上面的IoC的方式里,还有一些可以变化的地方。比如,bean.xml不一定要放在项目录下,也可以放在其他地方,比如cn.com.chengang.spring包里。不过在使用时也要变化一下,如下所示:

    new FileSystemXmlApplicationContext("src/cn/com/chengang/spring/bean.xml");

       另外,bean.xml也可以改成其他名字。这样我们在系统中就可以分门别类的设置不同的bean.xml。

       (4)关于IoC的低侵入性。

       什么是低侵入性?如果你用过Struts或EJB就会发现,要继承一些接口或类,才能利用它们的框架开发。这样,系统就被绑定在Struts、EJB上了,对系统的可移植性产生不利的影响。如果代码中很少涉及某一个框架的代码,那么这个框架就可以称做是一个低侵入性的框架。

       Spring的侵入性很低,Humen.java、Chinese.java等几个类都不必继承什么接口或类。但在ClientTest里还是有一些Spring的影子:FileSystemXmlApplicationContext类和ctx.getBean方式等。
    现在,低侵入性似乎也成了判定一个框架的实现技术好坏的标准之一。

       (5)关于bean.xml的用法

       bean.xml的用法还有很多,其中内容是相当丰富的。假设Chinese类里有一个humenName属性(姓名),那么原的bean.xml修改如下。此后生成Chinese对象时,“陈刚”这个值将自动设置到Chinese类的humenName属性中。而且由于singleton为true这时生成Chinese对象将采用单例模式,系统仅存在一个Chinese对象实例。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    <bean id="Chinese" class="cn.com.chengang.spring.Chinese" singleton="true">
    <property name="humenName">
    <value>陈刚</value>
    </property>
    </bean>
    <bean id="American" class="cn.com.chengang.spring.American"/>
    </beans>

       关于bean.xml的其它用法,不再详细介绍了,大家自己拿Spring的文档一看就明白了。

     Spring能有效地组织J2EE应用各层的对象。不管是控制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的管理下有机地协调、运行。Spring将各层的对象以松耦合的方式组织在一起,Action对象无须关心Service对象的具体实现,Service对象无须关心持久层对象的具体实现,各层对象的调用完全面向接口。当系统需要重构时,代码的改写量将大大减少。

      上面所说的一切都得宜于Spring的核心机制,依赖注入。依赖注入让bean与bean之间以配置文件组织在一起,而不是以硬编码的方式耦合在一起。理解依赖注入

      依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。

      不管是依赖注入,还是控制反转,都说明Spring采用动态、灵活的方式来管理各种对象。对象与对象之间的具体实现互相透明。在理解依赖注入之前,看如下这个问题在各种社会形态里如何解决:一个人(Java实例,调用者)需要一把斧子(Java实例,被调用者)。

      (1)原始社会里,几乎没有社会分工。需要斧子的人(调用者)只能自己去磨一把斧子(被调用者)。对应的情形为:Java程序里的调用者自己创建被调用者。

      (2)进入工业社会,工厂出现。斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。对应Java程序的简单工厂的设计模式。

      (3)进入“按需分配”社会,需要斧子的人不需要找到工厂,坐在家里发出一个简单指令:需要斧子。斧子就自然出现在他面前。对应Spring的依赖注入。

      第一种情况下,Java实例的调用者创建被调用的Java实例,必然要求被调用的Java类出现在调用者的代码里。无法实现二者之间的松耦合。

      第二种情况下,调用者无须关心被调用者具体实现过程,只需要找到符合某种标准(接口)的实例,即可使用。此时调用的代码面向接口编程,可以让调用者和被调用者解耦,这也是工厂模式大量使用的原因。但调用者需要自己定位工厂,调用者与特定工厂耦合在一起。

      第三种情况下,调用者无须自己定位工厂,程序运行到需要被调用者时,系统自动提供被调用者实例。事实上,调用者和被调用者都处于Spring的管理下,二者之间的依赖关系由Spring提供。

      所谓依赖注入,是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。依赖注入通常有两种:

      ·设值注入。

      ·构造注入。
    .......

    posted @ 2009-04-06 00:13 lanxin1020 阅读(710) | 评论 (0)编辑 收藏
         摘要: 最简单的解决方案 我们先写一个接口IHello.java代码如下:  1package sinosoft.dj.aop.staticaop;  2  3public interface IHello {  4    /** *//**  5&nbs...  阅读全文
    posted @ 2009-04-05 22:40 lanxin1020 阅读(169) | 评论 (0)编辑 收藏
           Action类是用户请求和业务逻辑之间的桥梁,每个Action充当客户的一项业务代理。在RequestProcessor类预处理请求时,在创建了Action的实例后,就调用自身的processActionPerform()方法,该方法再调用Action类的execute()。
    Action的excute()方法调用模型的业务方法,完成用户请求,然后根据执行结果把请求转发给其他合适的WEB组件。

    一、Action类缓存

          struts应用的生命周期中RequestProcessor只保证一个Action实例,所有的客户请求都共享这个实例.所有请求可以同时执行它的excute()方法。RequestProcessor类包含一个HashMap,作为存放所有Action实例的缓存。每个Action实例在缓存中存放的key为Action类名。在RequestProcessor类的processActionCreate()方法中,首先检查在HashMap中是否存在Action实例,如果有直接使用,否则创建一个新的。创建Action实力的代码位于同步代码块中,以保证只有一个线程创建Action实例,然后放在HashMap中。供其他线程使用。
    如下代码
  • protected Action processActionCreate(HttpServletRequest request,   
  •                                        HttpServletResponse response,   
  •                                        ActionMapping mapping)   
  •       throws IOException 
  • {   
  •       // Acquire the Action instance we will be using (if there is one)   
  •       String className = mapping.getType();   
  •       if (log.isDebugEnabled()) 
  •      {   
  •           log.debug(" Looking for Action instance for class " + className);   
  •       }   
  •   
  •       // :TODO: If there were a mapping property indicating whether   
  •       // an Action were a singleton or not ([true]),   
  •       // could we just instantiate and return a new instance here?   
  •   
  •       Action instance = null;   
  •       synchronized (actions) 
  •      {   
  •           // Return any existing Action instance of this class   
  •           instance = (Action) actions.get(className);   
  •           if (instance != null)
  •          {   
  •               if (log.isTraceEnabled()) 
  •              {   
  •                   log.trace("  Returning existing Action instance");   
  •               }   
  •               return (instance);   
  •           }   
  •   
  •           // Create and return a new Action instance   
  •           if (log.isTraceEnabled()) 
  •          {   
  •               log.trace("  Creating new Action instance");   
  •           }   
  •              
  •           try
  •          {   
  •               instance = (Action) RequestUtils.applicationInstance(className);   
  •               // :TODO: Maybe we should propagate this exception   
  •               // instead of returning null.   
  •           } 
  •           catch (Exception e) 
  •          {   
  •               log.error(   
  •                   getInternal().getMessage("actionCreate", mapping.getPath()),   
  •                   e);   
  •                      
  •               response.sendError(   
  •                   HttpServletResponse.SC_INTERNAL_SERVER_ERROR,   
  •                   getInternal().getMessage("actionCreate", mapping.getPath()));                         
  •               return (null);   
  •           }         
  •           instance.setServlet(this.servlet);   
  •           actions.put(className, instance);   
  •       }   
  •   
  •       return (instance);   
  •   
  •   }  

     

    二.创建支持多线程的Action
    1.什么是线程安全的代码
    在多线程环境下能正确执行的代码就是线程安全的。
    安全的意思是能正确执行,否则后果是程序执行错误,可能出现各种异常情况。

    2.如何编写线程安全的代码
    很多书籍里都详细讲解了如何这方面的问题,他们主要讲解的是如何同步线程对共享资源的使用的问题。主要是对synchronized关键字的各种用法,以及锁的概念。
    Java1.5中也提供了如读写锁这类的工具类。这些都需要较高的技巧,而且相对难于调试。

    但是,线程同步是不得以的方法,是比较复杂的,而且会带来性能的损失。等效的代码中,不需要同步在编写容易度和性能上会更好些。
    我这里强调的是什么代码是始终为线程安全的、是不需要同步的。如下:
    1)常量始终是线程安全的,因为只存在读操作。
    2)对构造器的访问(new 操作)是线程安全的,因为每次都新建一个实例,不会访问共享的资源。
    3)最重要的是:局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量。

    Servlet是在多线程环境下的。即可能有多个请求发给一个servelt实例,每个请求是一个线程。 struts下的action也类似,同样在多线程环境下,你也必须编写线程安全的Action类。
    保证线程安全的原则就是仅仅使用局部变量,谨慎使用实例变量(拥有状态的实例,尤其是拥有业务对象状态的实例). 如果要用到那些有状态的实例,唯一和最好的办法是在Action类中,仅仅在Action类的execute()方法中使用局部变量,对于每个调用execute()方法的线程,JVM会在每个线程的堆栈中创建局部变量,因此每个线程拥有独立的局部变量,不会被其他线程共享.当线程执行完execute()方法后,它的局部变量就会被销毁.
    如果Action类的实例变量是必须的话,需要采用JAVA同步机制(synchronized)对访问共享资源的代码块进行同步


    三、Struts的几种Action
    Struts提供了一些现成的Action类,直接使用可以大大节省时间,如下
    ForwardAction
    可以转发到其他web组件,仅仅提供一个转发功能,不作处理。
    IncludeAction
    包含其他web组件。
    DiapatchAction
    通常一个Action只完成一个操作,用这个Action可以完成一组相关的操作。
    LookupDispatchAction
    他是DiapatchAction的子类,也可以定义多个方法,但主要用于一个表单里有多个按钮,而这些按钮又有一个共同的名字的场合。
    SwitchAction
    用于子模块之间的切换。


    四.ActionForward类
    Action类的excute()方法返回一个ActionForward对象,它代表了web资源的逻辑抽象,这里的web资源可以是jsp页面、Java servlet、或Action
    从excute返回ActionForward可以有两种方法。
    1) 动态创建一个ActionForward实例
    return new ActionForward(”Failure”,”login.jsp”,true);
    2) 调用ActionMappin实例的findForward方法
    这个方法先从action级别找,然后在<global-forwards />级别找
    return mapping.findForward(“Failure”);

  • posted @ 2009-04-05 11:19 lanxin1020 阅读(191) | 评论 (0)编辑 收藏
    Struts框架只允许应用中存在一个ActionServlet类,但是可以存在多个客户化的RequestProcessor类,每个子应用模块都可以有单独的RequestProcessor类,

    ActionServlet主要负责初始化,以及介绍请求并找到合适的RequestRrocessor,之后真正干活的是RequestProecssor和Action.
    上回说到ActionServlet的process方法最终会调用RequestProcessor类的process方法.下面介绍这个方法.
    一.RequestProcessor的process方法
    public void process(HttpServletRequest request,   
  •                         HttpServletResponse response)   
  •         throws IOException, ServletException 
  • {   
  •         // Wrap multipart requests with a special wrapper   
  •         request = processMultipart(request);   
  •         // Identify the path component we will use to select a mapping   
  •         String path = processPath(request, response);   
  •         if (path == null
  •        {   
  •             return;   
  •         }    
  •         if (log.isDebugEnabled()) 
  •        {   
  •             log.debug("Processing a '" + request.getMethod() +   
  •                       "' for path '" + path + "'");   
  •         }   
  •         // Select a Locale for the current user if requested   
  •         processLocale(request, response);   
  •         // Set the content type and no-caching headers if requested   
  •         processContent(request, response);   
  •         processNoCache(request, response);   
  •         // General purpose preprocessing hook   
  •         if (!processPreprocess(request, response)) 
  •         {   
  •             return;   
  •         }   
  •         this.processCachedMessages(request, response);   
  •         // Identify the mapping for this request   
  •         ActionMapping mapping = processMapping(request, response, path);   
  •         if (mapping == null)
  •        {   
  •             return;   
  •         }   
  •         // Check for any role required to perform this action   
  •         if (!processRoles(request, response, mapping)) 
  •        {   
  •             return;   
  •         }   
  •         // Process any ActionForm bean related to this request   
  •         ActionForm form = processActionForm(request, response, mapping);   
  •         processPopulate(request, response, form, mapping);   
  •         // Validate any fields of the ActionForm bean, if applicable   
  •         try
  •        {   
  •             if (!processValidate(request, response, form, mapping)) 
  •            {   
  •                 return;   
  •             }   
  •         } 
  •        catch (InvalidCancelException e) 
  •        {   
  •             ActionForward forward = processException(request, response, e, form, mapping);   
  •             processForwardConfig(request, response, forward);   
  •             return;   
  •         } catch (IOException e) 
  •        {   
  •             throw e;   
  •         } catch (ServletException e) 
  •        {   
  •             throw e;   
  •         }   
  •                
  •         // Process a forward or include specified by this mapping   
  •         if (!processForward(request, response, mapping))
  •        {   
  •             return;   
  •         }   
  •         if (!processInclude(request, response, mapping)) 
  •        {   
  •             return;   
  •         }   
  •         // Create or acquire the Action instance to process this request   
  •         Action action = processActionCreate(request, response, mapping);   
  •         if (action == null)
  •         {   
  •             return;   
  •         }   
  •         // Call the Action instance itself   
  •         ActionForward forward =   
  •             processActionPerform(request, response,   
  •                                  action, form, mapping);   
  •   
  •         // Process the returned ActionForward instance   
  •         processForwardConfig(request, response, forward);   
  •   
  •     }   

    1) 调用processMultipart()方法
    如果HTTP请求方式为post,并且contentType为”multipart/form-data”开头,标准的HttpServletRequest对象将被重新包装,以方便处理”multipart”类型的HTTP请求.如果请求方式为get,或正congtentType属性不是”mulitipart”,就直接返回原始的HttpServletRequest对象.

    2) 调用processPath()方法
    获得请求的URI的路径,这一信息可用于选择合适的Struts Action组件.

    3) 调用processLocale方法
    当ControllerConfig对象的locale属性为true,将读取用户请求中包含的Locale信息,然后把Locale实例保存在session范围内.

    4) 调用processContendType(contentType)方法
    读取ControllerConfig对象的conttentType属性,然后调用response.setContentType(contentType)方法,设置响应结果的文档类型和字符编码.
    processContent()方法如下

  • protected void processContent(HttpServletRequest request,   
  •                                  HttpServletResponse response) 
  •  {   
  •   
  •        String contentType = moduleConfig.getControllerConfig().getContentType();   
  •        if (contentType != null
  •       {   
  •            response.setContentType(contentType);   
  •        }   
  •   
  •    }   

     


    5) 调用processNoCache()方法
    读取ControllerConfig对象的nocache属性,如果nocache属性为true,在响应结果中将加入特定的头参数:Pragma,Cache-Control和Expires,
    防止页面被存储在客户的浏览器的缓存中,processNoCache方法的代码如下:

  • protected void processNoCache(HttpServletRequest request,   
  •                                   HttpServletResponse response) 
  • {   
  •   
  •         if (moduleConfig.getControllerConfig().getNocache()) 
  •         {   
  •             response.setHeader("Pragma""No-cache");   
  •             response.setHeader("Cache-Control""no-cache,no-store,max-age=0");   
  •             response.setDateHeader("Expires"1);   
  •         }   
  •     }  



    6)调用processPreprocess()方法
    该方法不执行任何操作.直接返回true.子类可以覆盖这个方法.
    执行客户化的预处理请求操作.

    7)调用processMapping()方法
    寻找和用户请求的URI匹配的ActionMapping,如果不存在这样的ActionMapping,则向用户返回恰当的错误信息.

    8)调用processRoles()方法
    先判断是否为Action配置了安全角色,如果配置了安全角色,就调用isUserInRole()方法判断当前用户是否具备必需的角色,如果不具备,就结束请求处理流程.,向用户返回恰当的错误消息.

    9)调用processActionForm()方法
    先判断是否为ActionMapping配置了ActionForm,如果配置了ActionForm,就先从ActionForm的存在范围内(request或session)寻找改ActionForm实例,如果不存在,就创建一个实例,接下来把它保存在合适的范围内,保存时使用的属性key为ActionMapping的name属性。

    10)调用processPopulate()方法
    如果为ActionMapping配置了ActionForm,就先调用ActionForm的reset()方法,再把请求中的表单数据组装到ActionForm中。

    11)调用processValidate()方法
    如果为ActionMapping配置了ActionForm,并且ActionMapping的validate属性为true,就调用ActionForm的validate()方法,如果validate方法返回的ActionErrors对象中包含ActionMessage对象,说明表单验证失败。就把ActionErrors对象放在request范围内,再把请求转发到ActionMapping的input属性指定的Web组件。如果ActionForm的validate方法执行表单验证成功,就继续执行下面的处理流程。

    12)调用processForward()方法
    判断是否在ActionMapping中配置了forward属性。如果配置了这个属性,就调用RequestDispatcher的forward方法,请求处理流程结束。否则进行下一步。

    13)调用processInclude()方法
    判断是否在ActionMapping中配置了include属性。如果配置了这个属性,就调用RequestDispatcher的include方法,请求处理流程结束。否则进行下一步。

    14)调用processActionCreate()方法
    先判断是否在Action缓存中存在这个Action实例,如果没有就新建一个Action实例,把它放在Action缓存中。可以看出Action也是只有一个实例在运行的。

    15)调用processActionPerform
    该方法调用Action实例的execute方法,该方法位于try/catch中,以及捕获异常。processActionPerform()方放代码如下。

  • protected ActionForward   
  •        processActionPerform(HttpServletRequest request,   
  •                             HttpServletResponse response,   
  •                             Action action,   
  •                             ActionForm form,   
  •                             ActionMapping mapping)   
  •        throws IOException, ServletException {   
  •        try 
  •       {   
  •            return (action.execute(mapping, form, request, response));   
  •        } catch (Exception e)
  •        {   
  •            return (processException(request, response,   
  •                                     e, form, mapping));   
  •        }   
  •    

     


    16)调用processActionForward方法
    把你的Action的excute方法返回的ActionFoward对象作为参数传给它,processActionForward对象包的请求转发信息来执行请求转发或重定向。

    RequestProcessor类的process方法中,会访问ControllerConfig、ActionMappig和ActionForward实力的属性,ControllerConfig类和struts配置文件的<controlle>r元素对应,ActionMapping类和<action>元素对应,ActionForward和<forward>元素对应,process方法通过访问这三个类实例的属性来获得相关的配置信息。
    写了这么多,RequestProcessor干得事够多的吧。

    二.扩展RequestProcessor
    如果想修改RequestProcessor的一些默认功能,改易覆盖RequestProcessor基类中的相关方法.

  • Public class CustomRequestProcessor extends RequestProcessor{   
  •   protected void processPreprocess (HttpServletRequest request,   
  •                                  HttpServletResponse response) {    
  • ………………….   
  • }   
  • }  

    在struts配置文件中,<controller>元素的processorClass属性用于配置你自己的RequestProcessor

  • </controller    
  • contentType=“text/html:charset=”GB2312”   
  • locale=”true” nocache=”true” processorCalss=”com.test.CustomRequestProcessor”/>  

     




     

  • posted @ 2009-04-05 11:15 lanxin1020 阅读(176) | 评论 (0)编辑 收藏
            大家都知道,Struts控制器组件负责接受用户请求,更通模型,以及返回给用户合适的视图组件.
    控制器将模型层和视图层分开,这样分离,可以为同一个模型开发出不同的视图.
            下面时Struts的三大主要组件
    ActionServlet组件:充当Struts框架的中央控制器
    RequestProcessor组件:充当每个子应用模块的请求处理器
    Action组件:真正来处理一项具体的业务.

    一. Struts的init()方法
    Struts应用中只存在ActionServlet的一个实例,Servlet容器在启动时,或者用户首次请求ActionServlet时加载ActionServlet类.在这两种情况下,servlet容器都会在ActionServlet容器被加载后立即执行它的init()方法,这可以保证ActionServlet处理用户请求时已经被初始化.

    下面根据Init()讲述Struts的初始化过程
  • public void init() throws ServletException {   
  •   
  •         // Wraps the entire initialization in a try/catch to better handle   
  •         // unexpected exceptions and errors to provide better feedback   
  •         // to the developer   
  •         try {   
  • //调用initInternal()方法,初始化Struts框架内的消息资源,如与系统日志相关的通知,警告,和错误消息.   
  • 1)initInternal();   
  •   
  • //调用ininOther()方法,从web.xml文件中加载ActionServlet的初始化参数,如config参数   
  • 2)initOther();   
  •   
  • //调用initServlet()方法,从web.xml文件中加载ActionServlet的URL映射信息.同时还会注册web.xml文件和Struts配置文件所使用的DTD文件,这些DTD文件用户验证web.xml和struts配置文件的语法.其中方法里的 digester类负责解析web.xml,对字符串servletMapping属性进行初始化   
  • 3) initServlet();   
  •   
  • //把ActionServlet实例放到ServletContext里   
  • getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);   
  •   
  • //初始化一个factory,用于创建moduleConfig   
  • initModuleConfigFactory();   
  •   
  • //,加载并解析默认struts配置文件/WEB-INF/struts-config.xml,同时创建MoudleConfig实例,放到ServletContext中   
  • 4)ModuleConfig moduleConfig = initModuleConfig("", config);   
  •   
  • //加载并初始化默认子应用模块的消息资源;讲解MessageResources对象,把它存储在ServletContext中.   
  • 5)initModuleMessageResources(moduleConfig);   
  •   
  • //加载并初始化默认子应用模块的数据源,如果在struts配置文件中没有定义<data-sources >元素,忽略这一流程.   
  • 6)initModuleDataSources(moduleConfig);   
  •   
  • //加载并初始化默认子应用的所有插件   
  • 7)initModulePlugIns(moduleConfig);   
  •   
  • //冻结moduleConfig(,在方法返回之前不能修改它,否则将抛出异常)   
  • moduleConfig.freeze();   
  •            
  • //如果还有其他子应用模块,将重复4-7步   
  •       Enumeration names = getServletConfig().getInitParameterNames();   
  •             while (names.hasMoreElements()) {   
  •                 String name = (String) names.nextElement();   
  •                 if (!name.startsWith("config/")) {   
  •                     continue;   
  •                 }   
  •                 String prefix = name.substring(6);   
  •                 moduleConfig = initModuleConfig   
  •                     (prefix, getServletConfig().getInitParameter(name));   
  •                 initModuleMessageResources(moduleConfig);   
  •                 initModuleDataSources(moduleConfig);   
  •                 initModulePlugIns(moduleConfig);   
  •                 moduleConfig.freeze();   
  •      }   
  •   
  • //将各个子模块应用(除了默认的)的前缀存到一个字符数组中,并放到servletcontext中   
  • this.initModulePrefixes(this.getServletContext());   
  • //释放创建的用于读取配置文件的digester实例,释放内存   
  •             this.destroyConfigDigester();   
  •         } catch (UnavailableException ex) {   
  •             throw ex;   
  •         } catch (Throwable t) {   
  •             // The follow error message is not retrieved from internal message   
  •             // resources as they may not have been able to have been    
  •             // initialized   
  •             log.error("Unable to initialize Struts ActionServlet due to an "  
  •                 + "unexpected exception or error thrown, so marking the "  
  •                 + "servlet as unavailable.  Most likely, this is due to an "  
  •                 + "incorrect or missing library dependency.", t);   
  •             throw new UnavailableException(t.getMessage());   
  •         }       
  • }  
  •        将各个子模块应用(除了默认的)的前缀存到一个字符数组中,并放到servletcontext中,对于默认的子应用模块,在appclication范围内存放他的MoudleConfig实例的key为“org.apache.struts.action.MODULE”,其他模块如/account,存放的key为org.apache.struts.action.MODULE/account,消息,数据源和插件等部分存在servletcontext的key和上述方法类似,不在说明.


    二.ActionServlet的process方法
    ActionServlet接受到HTTP请求后,在doget()或doPost()方法中都会调用process()方法来处理请求.

  •  public void doGet(HttpServletRequest request,   
  •               HttpServletResponse response)   
  •         throws IOException, ServletException {   
  •   
  •         process(request, response);   
  •   
  • }   

  •   public void doPost(HttpServletRequest request,   

  •                HttpServletResponse response)   
  •         throws IOException, ServletException {   
  •   
  •         process(request, response);   
  •   
  • }   
  • 下面是process方法,它看上去并不复杂,但他调用的其他方法比较复杂. 
     protected void process(HttpServletRequest request, HttpServletResponse response)   

  •         throws IOException, ServletException {   
  •         //根据request里的信息从servletContext里找到相应的子模块ModuleConfig,和它下面的MessageResources,并放到request里,使其他组件可以方便的供request里取得应用配置信息和消息资源.   
  •         ModuleUtils.getInstance().selectModule(request, getServletContext());   
  • //取出MoudleConfig实例config   
  •         ModuleConfig config = getModuleConfig(request);   
  •     //根据config里这个子模块的信息,从servletcontext里,取出这个子模块的RequestProcessor实例   
  •         RequestProcessor processor = getProcessorForModule(config);   
  •     //如果processor实例为空,就新建一个.同时放到servletcontext里.   
  •         if (processor == null) {   
  •            processor = getRequestProcessor(config);   
  •         }   
  • //调用RequestProcessor的process方法处理,   
  •         processor.process(request, response);   
  •     }  

  • 三. 扩展ActionServlet
    从Struts1.1开始,为减轻ActionServlet的负担,多数功能已经移到RequestProcessor类中,所以基本不用扩展ActionServlet

    如果需要创建自己的ActionServlet,则可以创建一个它的子类.覆盖init()方法(或其他方法),可以写一些自己的操作,但要先调用super.init();
    定义如下的类:

  • package sample;    
  • public class ExtendedActionServlet extends ActionServlet {    
  •         public void init() throws ServletException {    
  •                super.init();    
  •                //do some operations    
  •                ……………    
  •         }    
  • }   


  • 扩展完类后,还应该在web.xml文件中如下配置:

  • <servlet>    
  •         <servlet-name>sample</servlet-name>    
  •         <servlet-class>sample.ExtendedActionServlet</servlet-class>    
  • </servlet>    
  •     
  • <servlet-mapping>    
  •        <servlet-name>sample</servlet-name>    
  •        <url-pattern>/action/*<url-pattern> 



  • 上面的/action/*表示负责处理所有以/action为前缀的URL,后面的/表示转义
    posted @ 2009-04-05 11:10 lanxin1020 阅读(190) | 评论 (0)编辑 收藏
         摘要:         Struts Recipes 的合著者 George Franciscus 将介绍另一个重大的 Struts 整合窍门 —— 这次是将 Struts 应用程序导入 Spring 框架。请跟随 George,他将向您展示如何改变 Struts 动作,使得管理 Struts 动作就像管理 Spring beans 那...  阅读全文
    posted @ 2009-04-05 10:17 lanxin1020 阅读(133) | 评论 (0)编辑 收藏

    三种整合 Struts 应用程序与 Spring 的方式

     

    Struts Recipes 的合著者 George Franciscus 将介绍另一个重大的 Struts 整合窍门 —— 这次是将 Struts 应用程序导入 Spring 框架。请跟随 George,他将向您展示如何改变 Struts 动作,使得管理 Struts 动作就像管理 Spring beans 那样。结果是一个增强的 web 框架,这个框架可以方便地利用 Spring AOP 的优势。

    您肯定已经听说过控制反转 (IOC) 设计模式,因为很长一段时间以来一直在流传关于它的信息。如果您在任何功能中使用过 Spring 框架,那么您就知道其原理的作用。在本文中,我利用这一原理把一个 Struts 应用程序注入 Spring 框架,您将亲身体会到 IOC 模式的强大。

    将一个 Struts 应用程序整合进 Spring 框架具有多方面的优点。首先,Spring 是为解决一些关于 JEE 的真实世界问题而设计的,比如复杂性、低性能和可测试性,等等。第二,Spring 框架包含一个 AOP 实现,允许您将面向方面技术应用于面向对象的代码。第三,一些人可能会说 Spring 框架只有处理 Struts 比 Struts 处理自己好。但是这是观点问题,我演示三种将 Struts 应用程序整合到 Spring 框架的方法后,具体由您自己决定使用哪一种。

    我所演示的方法都是执行起来相对简单的,但是它们却具有明显不同的优点。我为每一种方法创建了一个独立而可用的例子,这样您就可以完全理解每种方法。请参阅 下载 部分获得完整例子源代码。请参阅 参考资料,下载 Struts MVC 和 Spring 框架。

    为什么 Spring 这么了不起?

    Spring 的创立者 Rod Johnson 以一种批判的眼光看待 Java™ 企业软件开发,并且提议很多企业难题都能够通过战略地使用 IOC 模式(也称作依赖注入)来解决。当 Rod 和一个具有奉献精神的开放源码开发者团队将这个理论应用于实践时,结果就产生了 Spring 框架。简言之,Spring 是一个轻型的容器,利用它可以使用一个外部 XML 配置文件方便地将对象连接在一起。每个对象都可以通过显示一个 JavaBean 属性收到一个到依赖对象的引用,留给您的简单任务就只是在一个 XML 配置文件中把它们连接好。

    IOC 和 Spring

    IOC 是一种使应用程序逻辑外在化的设计模式,所以它是被注入而不是被写入客户机代码中。将 IOC 与接口编程应用结合,就像 Spring 框架那样,产生了一种架构,这种架构能够减少客户机对特定实现逻辑的依赖。请参阅 参考资料 了解更多关于 IOC 和 Spring 的信息。

    依赖注入是一个强大的特性,但是 Spring 框架能够提供更多特性。Spring 支持可插拔的事务管理器,可以给您的事务处理提供更广泛的选择范围。它集成了领先的持久性框架,并且提供一个一致的异常层次结构。Spring 还提供了一种使用面向方面代码代替正常的面向对象代码的简单机制。

    Spring AOP 允许您使用拦截器 在一个或多个执行点上拦截应用程序逻辑。加强应用程序在拦截器中的日志记录逻辑会产生一个更可读的、实用的代码基础,所以拦截器广泛用于日志记录。您很快就会看到,为了处理横切关注点,Spring AOP 发布了它自己的拦截器,您也可以编写您自己的拦截器。






    整合 Struts 和 Spring

    与 Struts 相似,Spring 可以作为一个 MVC 实现。这两种框架都具有自己的优点和缺点,尽管大部分人同意 Struts 在 MVC 方面仍然是最好的。很多开发团队已经学会在时间紧迫的时候利用 Struts 作为构造高品质软件的基础。Struts 具有如此大的推动力,以至于开发团队宁愿整合 Spring 框架的特性,而不愿意转换成 Spring MVC。没必要进行转换对您来说是一个好消息。Spring 架构允许您将 Struts 作为 Web 框架连接到基于 Spring 的业务和持久层。最后的结果就是现在一切条件都具备了。

    在接下来的小窍门中,您将会了解到三种将 Struts MVC 整合到 Spring 框架的方法。我将揭示每种方法的缺陷并且对比它们的优点。 一旦您了解到所有三种方法的作用,我将会向您展示一个令人兴奋的应用程序,这个程序使用的是这三种方法中我最喜欢的一种。





    三个小窍门

    接下来的每种整合技术(或者窍门)都有自己的优点和特点。我偏爱其中的一种,但是我知道这三种都能够加深您对 Struts 和 Spring 的理解。在处理各种不同情况的时候,这将给您提供一个广阔的选择范围。方法如下:

    • 使用 Spring 的 ActionSupport 类整合 Structs
    • 使用 Spring 的 DelegatingRequestProcessor 覆盖 Struts 的 RequestProcessor
    • 将 Struts Action 管理委托给 Spring 框架

    装载应用程序环境

    无论您使用哪种技术,都需要使用 Spring 的 ContextLoaderPlugin 为 Struts 的 ActionServlet 装载 Spring 应用程序环境。就像添加任何其他插件一样,简单地向您的 struts-config.xml 文件添加该插件,如下所示:

    <plug-in className=
                            "org.springframework.web.struts.ContextLoaderPlugIn">
                            <set-property property=
                            "contextConfigLocation" value="/WEB-INF/beans.xml"/>
                            </plug-in>
                            

    前面已经提到过,在 下载 部分,您能够找到这三个完全可使用的例子的完整源代码。每个例子都为一个书籍搜索应用程序提供一种不同的 Struts 和 Spring 的整合方法。您可以在这里看到例子的要点,但是您也可以下载应用程序以查看所有的细节。





    窍门 1. 使用 Spring 的 ActionSupport

    手动创建一个 Spring 环境是一种整合 Struts 和 Spring 的最直观的方式。为了使它变得更简单,Spring 提供了一些帮助。为了方便地获得 Spring 环境,org.springframework.web.struts.ActionSupport 类提供了一个 getWebApplicationContext() 方法。您所做的只是从 Spring 的 ActionSupport 而不是 Struts Action 类扩展您的动作,如清单 1 所示:


    清单 1. 使用 ActionSupport 整合 Struts
    package ca.nexcel.books.actions;
                            import java.io.IOException;
                            import javax.servlet.ServletException;
                            import javax.servlet.http.HttpServletRequest;
                            import javax.servlet.http.HttpServletResponse;
                            import org.apache.struts.action.ActionError;
                            import org.apache.struts.action.ActionErrors;
                            import org.apache.struts.action.ActionForm;
                            import org.apache.struts.action.ActionForward;
                            import org.apache.struts.action.ActionMapping;
                            import org.apache.struts.action.DynaActionForm;
                            import org.springframework.context.ApplicationContext;
                            import org.springframework.web.struts.ActionSupport;
                            import ca.nexcel.books.beans.Book;
                            import ca.nexcel.books.business.BookService;
                            public class SearchSubmit extends ActionSupport {   |(1)
                            public ActionForward execute(
                            ActionMapping mapping,
                            ActionForm form,
                            HttpServletRequest request,
                            HttpServletResponse response)
                            throws IOException, ServletException {
                            DynaActionForm searchForm = (DynaActionForm) form;
                            String isbn = (String) searchForm.get("isbn");
                            //the old fashion way
                            //BookService bookService = new BookServiceImpl();
                            ApplicationContext ctx =
                            getWebApplicationContext();    |(2)
                            BookService bookService =
                            (BookService) ctx.getBean("bookService");   |(3)
                            Book book = bookService.read(isbn.trim());
                            if (null == book) {
                            ActionErrors errors = new ActionErrors();
                            errors.add(ActionErrors.GLOBAL_ERROR,new ActionError
                            ("message.notfound"));
                            saveErrors(request, errors);
                            return mapping.findForward("failure") ;
                            }
                            request.setAttribute("book", book);
                            return mapping.findForward("success");
                            }
                            }
                            

    让我们快速思考一下这里到底发生了什么。在 (1) 处,我通过从 Spring 的 ActionSupport 类而不是 Struts 的 Action 类进行扩展,创建了一个新的 Action。在 (2) 处,我使用 getWebApplicationContext() 方法获得一个 ApplicationContext。为了获得业务服务,我使用在 (2) 处获得的环境在 (3) 处查找一个 Spring bean。

    这种技术很简单并且易于理解。不幸的是,它将 Struts 动作与 Spring 框架耦合在一起。如果您想替换掉 Spring,那么您必须重写代码。并且,由于 Struts 动作不在 Spring 的控制之下,所以它不能获得 Spring AOP 的优势。当使用多重独立的 Spring 环境时,这种技术可能有用,但是在大多数情况下,这种方法不如另外两种方法合适。





    窍门 2. 覆盖 RequestProcessor

    将 Spring 从 Struts 动作中分离是一个更巧妙的设计选择。分离的一种方法是使用 org.springframework.web.struts.DelegatingRequestProcessor 类来覆盖 Struts 的 RequestProcessor 处理程序,如清单 2 所示:


    清单 2. 通过 Spring 的 DelegatingRequestProcessor 进行整合
    <?xml version="1.0" encoding="ISO-8859-1" ?>
                            <!DOCTYPE struts-config PUBLIC
                            "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
                            "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
                            <struts-config>
                            <form-beans>
                            <form-bean name="searchForm"
                            type="org.apache.struts.validator.DynaValidatorForm">
                            <form-property name="isbn"    type="java.lang.String"/>
                            </form-bean>
                            </form-beans>
                            <global-forwards type="org.apache.struts.action.ActionForward">
                            <forward   name="welcome"                path="/welcome.do"/>
                            <forward   name="searchEntry"            path="/searchEntry.do"/>
                            <forward   name="searchSubmit"           path="/searchSubmit.do"/>
                            </global-forwards>
                            <action-mappings>
                            <action    path="/welcome" forward="/WEB-INF/pages/welcome.htm"/>
                            <action    path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/>
                            <action    path="/searchSubmit"
                            type="ca.nexcel.books.actions.SearchSubmit"
                            input="/searchEntry.do"
                            validate="true"
                            name="searchForm">
                            <forward name="success" path="/WEB-INF/pages/detail.jsp"/>
                            <forward name="failure" path="/WEB-INF/pages/search.jsp"/>
                            </action>
                            </action-mappings>
                            <message-resources parameter="ApplicationResources"/>
                            <controller processorClass="org.springframework.web.struts.
                            DelegatingRequestProcessor"/> |(1)
                            <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
                            <set-property property="pathnames"
                            value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
                            </plug-in>
                            <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
                            <set-property property="csntextConfigLocation" value="/WEB-INF/beans.xml"/>
                            </plug-in>
                            </struts-config>
                            

    我利用了 <controller> 标记来用 DelegatingRequestProcessor 覆盖默认的 Struts RequestProcessor。下一步是在我的 Spring 配置文件中注册该动作,如清单 3 所示:


    清单 3. 在 Spring 配置文件中注册一个动作
    <?xml version="1.0" encoding="UTF-8"?>
                            <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                            "http://www.springframework.org/dtd/spring-beans.dtd">
                            <beans>
                            <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>
                            <bean name="/searchSubmit"
                            class="ca.nexcel.books.actions.SearchSubmit"> |(1)
                            <property name="bookService">
                            <ref bean="bookService"/>
                            </property>
                            </bean>
                            </beans>
                            

    注意:在 (1) 处,我使用名称属性注册了一个 bean,以匹配 struts-config 动作映射名称。SearchSubmit 动作揭示了一个 JavaBean 属性,允许 Spring 在运行时填充属性,如清单 4 所示:


    清单 4. 具有 JavaBean 属性的 Struts 动作
    package ca.nexcel.books.actions;
                            import java.io.IOException;
                            import javax.servlet.ServletException;
                            import javax.servlet.http.HttpServletRequest;
                            import javax.servlet.http.HttpServletResponse;
                            import org.apache.struts.action.Action;
                            import org.apache.struts.action.ActionError;
                            import org.apache.struts.action.ActionErrors;
                            import org.apache.struts.action.ActionForm;
                            import org.apache.struts.action.ActionForward;
                            import org.apache.struts.action.ActionMapping;
                            import org.apache.struts.action.DynaActionForm;
                            import ca.nexcel.books.beans.Book;
                            import ca.nexcel.books.business.BookService;
                            public class SearchSubmit extends Action {
                            private BookService bookService;
                            public BookService getBookService() {
                            return bookService;
                            }
                            public void setBookService(BookService bookService) { | (1)
                            this.bookService = bookService;
                            }
                            public ActionForward execute(
                            ActionMapping mapping,
                            ActionForm form,
                            HttpServletRequest request,
                            HttpServletResponse response)
                            throws IOException, ServletException {
                            DynaActionForm searchForm = (DynaActionForm) form;
                            String isbn = (String) searchForm.get("isbn");
                            Book book = getBookService().read(isbn.trim());  |(2)
                            if (null == book) {
                            ActionErrors errors = new ActionErrors();
                            errors.add(ActionErrors.GLOBAL_ERROR,new ActionError("message.notfound"));
                            saveErrors(request, errors);
                            return mapping.findForward("failure") ;
                            }
                            request.setAttribute("book", book);
                            return mapping.findForward("success");
                            }
                            }
                            

    在清单 4 中,您可以了解到如何创建 Struts 动作。在 (1) 处,我创建了一个 JavaBean 属性。DelegatingRequestProcessor自动地配置这种属性。这种设计使 Struts 动作并不知道它正被 Spring 管理,并且使您能够利用 Sping 的动作管理框架的所有优点。由于您的 Struts 动作注意不到 Spring 的存在,所以您不需要重写您的 Struts 代码就可以使用其他控制反转容器来替换掉 Spring。

    DelegatingRequestProcessor 方法的确比第一种方法好,但是仍然存在一些问题。如果您使用一个不同的 RequestProcessor,则需要手动整合 Spring 的 DelegatingRequestProcessor。添加的代码会造成维护的麻烦并且将来会降低您的应用程序的灵活性。此外,还有过一些使用一系列命令来代替 Struts RequestProcessor 的传闻。 这种改变将会对这种解决方法的使用寿命造成负面的影响。





    窍门 3. 将动作管理委托给 Spring

    一个更好的解决方法是将 Strut 动作管理委托给 Spring。您可以通过在 struts-config 动作映射中注册一个代理来实现。代理负责在 Spring 环境中查找 Struts 动作。由于动作在 Spring 的控制之下,所以它可以填充动作的 JavaBean 属性,并为应用诸如 Spring 的 AOP 拦截器之类的特性带来了可能。

    清单 5 中的 Action 类与清单 4 中的相同。但是 struts-config 有一些不同:


    清单 5. Spring 整合的委托方法
    <?xml version="1.0" encoding="ISO-8859-1" ?>
                            <!DOCTYPE struts-config PUBLIC
                            "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
                            "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
                            <struts-config>
                            <form-beans>
                            <form-bean name="searchForm"
                            type="org.apache.struts.validator.DynaValidatorForm">
                            <form-property name="isbn"    type="java.lang.String"/>
                            </form-bean>
                            </form-beans>
                            <global-forwards type="org.apache.struts.action.ActionForward">
                            <forward   name="welcome"                path="/welcome.do"/>
                            <forward   name="searchEntry"            path="/searchEntry.do"/>
                            <forward   name="searchSubmit"           path="/searchSubmit.do"/>
                            </global-forwards>
                            <action-mappings>
                            <action    path="/welcome" forward="/WEB-INF/pages/welcome.htm"/>
                            <action    path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/>
                            <action    path="/searchSubmit"
                            type="org.springframework.web.struts.DelegatingActionProxy" |(1)
                            input="/searchEntry.do"
                            validate="true"
                            name="searchForm">
                            <forward name="success" path="/WEB-INF/pages/detail.jsp"/>
                            <forward name="failure" path="/WEB-INF/pages/search.jsp"/>
                            </action>
                            </action-mappings>
                            <message-resources parameter="ApplicationResources"/>
                            <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
                            <set-property
                            property="pathnames"
                            value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
                            </plug-in>
                            <plug-in
                            className="org.springframework.web.struts.ContextLoaderPlugIn">
                            <set-property property="contextConfigLocation" value="/WEB-INF/beans.xml"/>
                            </plug-in>
                            </struts-config>
                            

    清单 5 是一个典型的 struts-config.xml 文件,只有一个小小的差别。它注册 Spring 代理类的名称,而不是声明动作的类名,如(1)处所示。DelegatingActionProxy 类使用动作映射名称查找 Spring 环境中的动作。这就是我们使用 ContextLoaderPlugIn 声明的环境。

    将一个 Struts 动作注册为一个 Spring bean 是非常直观的,如清单 6 所示。我利用动作映射使用 <bean> 标记的名称属性(在这个例子中是 "/searchSubmit")简单地创建了一个 bean。这个动作的 JavaBean 属性像任何 Spring bean 一样被填充:


    清单 6. 在 Spring 环境中注册一个 Struts 动作
    <?xml version="1.0" encoding="UTF-8"?>
                            <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                            "http://www.springframework.org/dtd/spring-beans.dtd">
                            <beans>
                            <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>
                            <bean name="/searchSubmit"
                            class="ca.nexcel.books.actions.SearchSubmit">
                            <property name="bookService">
                            <ref bean="bookService"/>
                            </property>
                            </bean>
                            </beans>
                            





    动作委托的优点

    动作委托解决方法是这三种方法中最好的。Struts 动作不了解 Spring,不对代码作任何改变就可用于非 Spring 应用程序中。RequestProcessor 的改变不会影响它,并且它可以利用 Spring AOP 特性的优点。

    动作委托的优点不止如此。一旦让 Spring 控制您的 Struts 动作,您就可以使用 Spring 给动作补充更强的活力。例如,没有 Spring 的话,所有的 Struts 动作都必须是线程安全的。如果您设置 <bean> 标记的 singleton 属性为“false”,那么不管用何种方法,您的应用程序都将在每一个请求上有一个新生成的动作对象。您可能不需要这种特性,但是把它放在您的工具箱中也很好。您也可以利用 Spring 的生命周期方法。例如,当实例化 Struts 动作时,<bean> 标记的 init-method 属性被用于运行一个方法。类似地,在从容器中删除 bean 之前,destroy-method 属性执行一个方法。这些方法是管理昂贵对象的好办法,它们以一种与 Servlet 生命周期相同的方式进行管理。





    拦截 Struts

    前面提到过,通过将 Struts 动作委托给 Spring 框架而整合 Struts 和 Spring 的一个主要的优点是:您可以将 Spring 的 AOP 拦截器应用于您的 Struts 动作。通过将 Spring 拦截器应用于 Struts 动作,您可以用最小的代价处理横切关注点。

    虽然 Spring 提供很多内置拦截器,但是我将向您展示如何创建自己的拦截器并把它应用于一个 Struts 动作。为了使用拦截器,您需要做三件事:

    1. 创建拦截器。
    2. 注册拦截器。
    3. 声明在何处拦截代码。

    这看起来非常简单的几句话却非常强大。例如,在清单 7 中,我为 Struts 动作创建了一个日志记录拦截器。 这个拦截器在每个方法调用之前打印一句话:


    清单 7. 一个简单的日志记录拦截器
    package ca.nexcel.books.interceptors;
                            import org.springframework.aop.MethodBeforeAdvice;
                            import java.lang.reflect.Method;
                            public class LoggingInterceptor implements MethodBeforeAdvice {
                            public void before(Method method, Object[] objects, Object o) throws Throwable {
                            System.out.println("logging before!");
                            }
                            }
                            

    这个拦截器非常简单。before() 方法在拦截点中每个方法之前运行。在本例中,它打印出一句话,其实它可以做您想做的任何事。下一步就是在 Spring 配置文件中注册这个拦截器,如清单 8 所示:


    清单 8. 在 Spring 配置文件中注册拦截器
    <?xml version="1.0" encoding="UTF-8"?>
                            <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                            "http://www.springframework.org/dtd/spring-beans.dtd">
                            <beans>
                            <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>
                            <bean name="/searchSubmit"
                            class="ca.nexcel.books.actions.SearchSubmit">
                            <property name="bookService">
                            <ref bean="bookService"/>
                            </property>
                            </bean>
                            <!--  Interceptors -->
                            <bean name="logger"
                            class="ca.nexcel.books.interceptors.LoggingInterceptor"/> |(1)
                            <!-- AutoProxies -->
                            <bean name="loggingAutoProxy"
                            class="org.springframework.aop.framework.autoproxy.
                            BeanNameAutoProxyCreator"> |(2)
                            <property name="beanNames">
                            <value>/searchSubmit</valuesgt; |(3)
                            </property>
                            <property name="interceptorNames">
                            <list>
                            <value>logger</value> |(4)
                            </list>
                            </property>
                            </bean>
                            </beans>
                            

    您可能已经注意到了,清单 8 扩展了 清单 6 中所示的应用程序以包含一个拦截器。具体细节如下:

    • 在 (1) 处,我注册了这个拦截器。
    • 在 (2) 处,我创建了一个 bean 名称自动代理,它描述如何应用拦截器。还有其他的方法定义拦截点,但是这种方法常见而简便。
    • 在 (3) 处,我将 Struts 动作注册为将被拦截的 bean。如果您想要拦截其他的 Struts 动作,则只需要在 "beanNames" 下面创建附加的 <value> 标记。
    • 在 (4) 处,当拦截发生时,我执行了在 (1) 处创建的拦截器 bean 的名称。这里列出的所有拦截器都应用于“beanNames”。

    就是这样。就像这个例子所展示的,将您的 Struts 动作置于 Spring 框架的控制之下,为处理您的 Struts 应用程序提供了一系列全新的选择。在本例中,使用动作委托可以轻松地利用 Spring 拦截器提高 Struts 应用程序中的日志记录能力。





    结束语

    在本文中,您已经学习了将 Struts 动作整合到 Spring 框架中的三种窍门。使用 Spring 的 ActionSupport 来整合 Struts(第一种窍门中就是这样做的)简单而快捷,但是会将 Struts 动作与 Spring 框架耦合在一起。如果您需要将应用程序移植到一个不同的框架,则需要重写代码。第二种解决方法通过委托 RequestProcessor 巧妙地解开代码的耦合,但是它的可扩展性不强,并且当 Struts 的 RequestProcessor 变成一系列命令时,这种方法就持续不了很长时间。第三种方法是这三种方法中最好的:将 Struts 动作委托给 Spring 框架可以使代码解耦,从而使您可以在您的 Struts 应用程序中利用 Spring 的特性(比如日志记录拦截器)。

     


    posted @ 2009-04-05 09:37 lanxin1020 阅读(142) | 评论 (0)编辑 收藏
    仅列出标题
    共7页: 上一页 1 2 3 4 5 6 7 下一页