George Franciscus , 负责? Nexcel
(zhn)肯定已l听说过控制反{ (IOC) 设计模式Q因为很长一D|间以来一直在传关于它的信息。如果?zhn)在Q何功能中使用q?Spring 框架Q那么?zhn)q道其原理的作用。在本文中,我利用这一原理把一?Struts 应用E序注入 Spring 框架Q?zhn)亲w体?x)?IOC 模式的强大?/P>
一?Struts 应用E序整合q?Spring 框架h多方面的优点。首先,Spring 是ؓ(f)解决一些关?JEE 的真实世界问题而设计的Q比如复杂性、低性能和可试性,{等。第二,Spring 框架包含一?AOP 实现Q允许?zhn)面向方面技术应用于面向对象的代码。第三,一些h可能?x)?Spring 框架只有处理 Struts ?Struts 处理自己好。但是这是观炚w题,我演CZU将 Struts 应用E序整合?Spring 框架的方法后Q具体由(zhn)自己决定用哪一U?/P>
我所演示的方法都是执行v来相对简单的Q但是它们却h明显不同的优炏V我为每一U方法创Z(jin)一个独立而可用的例子Q这h可以完全理解每U方法?/P>
Z?Spring q么?jin)不P
Spring 的创立?Rod Johnson 以一U批判的眼光看待 Java? 企业软g开发,q且提议很多企业N都能够通过战略C?IOC 模式Q也UC依赖注入Q来解决。当 Rod 和一个具有奉献精的开放源码开发者团队将q个理论应用于实跉|Q结果就产生?Spring 框架。简a之,Spring 是一个轻型的容器Q利用它可以使用一个外?XML 配置文g方便地将对象q接在一赗每个对象都可以通过昄一?JavaBean 属性收C个到依赖对象的引用,留给(zhn)的单Q务就只是在一?XML 配置文g中把它们q接好?/P>
 |
IOC ?Spring
IOC 是一U应用E序逻辑外在化的设计模式Q所以它是被注入而不是被写入客户Z码中。将 IOC 与接口编E应用结合,像 Spring 框架那样Q生了(jin)一U架构,q种架构能够减少客户机对特定实现逻辑的依赖?/P> | |
依赖注入是一个强大的Ҏ(gu),但是 Spring 框架能够提供更多Ҏ(gu)。Spring 支持可插拔的事务理器,可以l?zhn)的事务处理提供更q泛的选择范围。它集成?jin)领先的持久性框Ӟq且提供一个一致的异常层次l构。Spring q提供了(jin)一U用面向方面代码代替正常的面向对象代码的简单机制?/P>
Spring AOP 允许(zhn)?I>拦截?/I> 在一个或多个执行点上拦截应用E序逻辑。加强应用程序在拦截器中的日志记录逻辑?x)生一个更可读的、实用的代码基础Q所以拦截器q泛用于日志记录。?zhn)很快׃?x)看到Qؓ(f)?jin)处理横切关注点QSpring AOP 发布?jin)它自己的拦截器Q?zhn)也可以编写(zhn)自己的拦截器?/P>
整合 Struts ?Spring
?Struts 怼QSpring 可以作ؓ(f)一?MVC 实现。这两种框架都具有自q优点和缺点,管大部分h同意 Struts ?MVC 斚w仍然是最好的。很多开发团队已l学?x)在旉紧迫的时候利?Struts 作ؓ(f)构造高品质软g的基。Struts h如此大的推动力,以至于开发团队宁愿整?Spring 框架的特性,而不愿意转换?Spring MVC。没必要q行转换Ҏ(gu)来说是一个好消息。Spring 架构允许(zhn)将 Struts 作ؓ(f) Web 框架q接到基?Spring 的业务和持久层。最后的l果是现在一切条仉具备?jin)?/P>
在接下来的小H门中,(zhn)将?x)?jin)解到三种?Struts MVC 整合?Spring 框架的方法。我揭C每U方法的~陷q且Ҏ(gu)它们的优炏V?一旦?zhn)了(jin)解到所有三U方法的作用Q我会(x)向?zhn)展示一个o(h)人兴奋的应用E序Q这个程序用的是这三种Ҏ(gu)中我最喜欢的一U?/P>
三个窍?/FONT>
接下来的每种整合技术(或者窍门)(j)都有自己的优点和特点。我偏爱其中的一U,但是我知道这三种都能够加深?zhn)?Struts ?Spring 的理解。在处理各种不同情况的时候,q将l?zhn)提供一个广阔的选择范围。方法如下:(x)
- 使用 Spring ?
ActionSupport
cL?Structs
- 使用 Spring ?
DelegatingRequestProcessor
覆盖 Struts ?RequestProcessor
- ?Struts
Action
理委托l?Spring 框架
装蝲应用E序环境
无论(zhn)用哪U技术,都需要?Spring ?ContextLoaderPlugin
?Struts ?ActionServlet
装蝲 Spring 应用E序环境。就像添加Q何其他插件一P单地向?zhn)?struts-config.xml 文gd该插Ӟ如下所C:(x)
<plug-in className=
"org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property=
"contextConfigLocation" value="/WEB-INF/beans.xml"/>
</plug-in>
|
H门 1. 使用 Spring ?ActionSupport
手动创徏一?Spring 环境是一U整?Struts ?Spring 的最直观的方式。ؓ(f)?jin)它变得更单,Spring 提供?jin)一些帮助。ؓ(f)?jin)方便地获?Spring 环境Q?CODE>org.springframework.web.struts.ActionSupport cL供了(jin)一?getWebApplicationContext()
Ҏ(gu)。?zhn)所做的只是?Spring ?ActionSupport
而不?Struts Action
cL展?zhn)的动作,如清?1 所C:(x)
清单 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");
}
}
|
让我们快速思考一下这里到底发生了(jin)什么。在 (1) 处,我通过?Spring ?ActionSupport
c而不?Struts ?Action
c进行扩展,创徏?jin)一个新?Action
。在 (2) 处,我?getWebApplicationContext()
Ҏ(gu)获得一?ApplicationContext
。ؓ(f)?jin)获得业务服务,我用?(2) 处获得的环境?(3) 处查找一?Spring bean?/P>
q种技术很单ƈ且易于理解。不q的是,它将 Struts 动作?Spring 框架耦合在一赗如果?zhn)x换掉 SpringQ那么?zhn)必须重写代码。ƈ且,׃ Struts 动作不在 Spring 的控制之下,所以它不能获得 Spring AOP 的优ѝ当使用多重独立?Spring 环境Ӟq种技术可能有用,但是在大多数情况下,q种Ҏ(gu)不如另外两种Ҏ(gu)合适?/P>
H门 2. 覆盖 RequestProcessor
?Spring ?Struts 动作中分L一个更巧妙的设计选择。分ȝ一U方法是使用 org.springframework.web.struts.DelegatingRequestProcessor
cL覆盖 Struts ?RequestProcessor
处理E序Q如清单 2 所C:(x)
清单 2. 通过 Spring ?DelegatingRequestProcessor q行整合
<?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>
|
我利用了(jin) <controller>
标记来用 DelegatingRequestProcessor
覆盖默认?Struts RequestProcessor
。下一步是在我?Spring 配置文g中注册该动作Q如清单 3 所C:(x)
清单 3. ?Spring 配置文g中注册一个动?/B>
<?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>
|
注意Q在 (1) 处,我用名U属性注册了(jin)一?beanQ以匚w struts-config 动作映射名称?CODE>SearchSubmit 动作揭示?jin)一?JavaBean 属性,允许 Spring 在运行时填充属性,如清?4 所C:(x)
清单 4. h 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 中,(zhn)可以了(jin)解到如何创徏 Struts 动作。在 (1) 处,我创Z(jin)一?JavaBean 属性?CODE>DelegatingRequestProcessor自动地配|这U属性。这U设计 Struts 动作q不知道它正?Spring 理Qƈ且(zhn)能够利?Sping 的动作管理框架的所有优炏V由于?zhn)?Struts 动作注意不到 Spring 的存在,所以?zhn)不需要重写?zhn)?Struts 代码可以用其他控制反转容器来替换?Spring?/P>
DelegatingRequestProcessor
Ҏ(gu)的确比第一U方法好Q但是仍然存在一些问题。如果?zhn)使用一个不同的 RequestProcessor
Q则需要手动整?Spring ?DelegatingRequestProcessor
。添加的代码?x)造成l护的麻?ch)ƈ且将来?x)降低(zhn)的应用E序的灵zL。此外,q有q一些用一pd命o(h)来代?Struts RequestProcessor
的传闅R?q种改变会(x)对这U解x法的使用寿命造成负面的媄(jing)响?/P>
H门 3. 动作管理委托给 Spring
一个更好的解决Ҏ(gu)是将 Strut 动作理委托l?Spring。?zhn)可以通过?struts-config
动作映射中注册一个代理来实现。代理负责在 Spring 环境中查?Struts 动作。由于动作在 Spring 的控制之下,所以它可以填充动作?JavaBean 属性,qؓ(f)应用诸如 Spring ?AOP 拦截器之cȝҎ(gu)带来了(jin)可能?
清单 5 中的 Action
cM清单 4 中的相同。但?struts-config 有一些不同:(x)
清单 5. Spring 整合的委托方?/B>
<?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 文gQ只有一个小的差别。它注册 Spring 代理cȝ名称Q而不是声明动作的cdQ如Q?Q处所C。DelegatingActionProxy cM用动作映名U查?Spring 环境中的动作。这是我们使用 ContextLoaderPlugIn
声明的环境?/P>
一?Struts 动作注册Z?Spring bean 是非常直观的Q如清单 6 所C。我利用动作映射使用 <bean>
标记的名U属性(在这个例子中?"/searchSubmit
"Q简单地创徏?jin)一?bean。这个动作的 JavaBean 属性像M Spring bean 一栯填充Q?
清单 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>
|
动作委托的优?/FONT>
动作委托解决Ҏ(gu)是这三种Ҏ(gu)中最好的。Struts 动作不了(jin)?SpringQ不对代码作M改变可用于?Spring 应用E序中?CODE>RequestProcessor 的改变不?x)?jing)响它Qƈ且它可以利用 Spring AOP Ҏ(gu)的优点?
动作委托的优点不止如此。一旦让 Spring 控制(zhn)的 Struts 动作Q?zhn)可以?Spring l动作补充更强的zd。例如,没有 Spring 的话Q所有的 Struts 动作都必LU程安全的。如果?zhn)讄?<bean>
标记?singleton 属性ؓ(f)“false”,那么不管用何U方法,(zhn)的应用E序都将在每一个请求上有一个新生成的动作对象。?zhn)可能不需要这U特性,但是把它攑֜(zhn)的工具׃也很好。?zhn)也可以利?Spring 的生命周期方法。例如,当实例化 Struts 动作Ӟ<bean>
标记?init-method 属性被用于q行一个方法。类似地Q在从容器中删除 bean 之前Qdestroy-method 属性执行一个方法。这些方法是理昂贵对象的好办法Q它们以一U与 Servlet 生命周期相同的方式进行管理?/P>
拦截 Struts
前面提到q,通过?Struts 动作委托l?Spring 框架而整?Struts ?Spring 的一个主要的优点是:(x)(zhn)可以将 Spring ?AOP 拦截器应用于(zhn)的 Struts 动作。通过?Spring 拦截器应用于 Struts 动作Q?zhn)可以用最的代h(hun)处理横切x炏V?/P>
虽然 Spring 提供很多内置拦截器,但是我将向?zhn)展示如何创徏自己的拦截器q把它应用于一?Struts 动作。ؓ(f)?jin)用拦截器Q?zhn)需要做三g事:(x)
- 创徏拦截器?
- 注册拦截器?
- 声明在何处拦截代码?/LI>
q看h非常单的几句话却非常强大。例如,在清?7 中,我ؓ(f) Struts 动作创徏?jin)一个日志记录拦截器?q个拦截器在每个Ҏ(gu)调用之前打印一句话Q?/P>
清单 7. 一个简单的日志记录拦截?/B>
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!");
}
}
|
q个拦截器非常简单?CODE>before() Ҏ(gu)在拦截点中每个方法之前运行。在本例中,它打印出一句话Q其实它可以做?zhn)惛_的Q何事。下一步就是在 Spring 配置文g中注册这个拦截器Q如清单 8 所C:(x)
清单 8. ?Spring 配置文g中注册拦截器
<?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>
|
(zhn)可能已l注意到?jin),清?8 扩展?清单 6 中所C的应用E序以包含一个拦截器。具体细节如下:(x)
- ?(1) 处,我注册了(jin)q个拦截器?
- ?(2) 处,我创Z(jin)一?bean 名称自动代理Q它描述如何应用拦截器。还有其他的Ҏ(gu)定义拦截点,但是q种Ҏ(gu)常见而简ѝ?
- ?(3) 处,我将 Struts 动作注册为将被拦截的 bean。如果?zhn)惌拦截其他?Struts 动作Q则只需要在 "beanNames" 下面创徏附加?
<value>
标记?
- ?(4) 处,当拦截发生时Q我执行?jin)?(1) 处创建的拦截?bean 的名U。这里列出的所有拦截器都应用于“beanNames”?/LI>
是q样。就像这个例子所展示的,?zhn)?Struts 动作|于 Spring 框架的控制之下,为处理?zhn)?Struts 应用E序提供?jin)一pd全新的选择。在本例中,使用动作委托可以L地利?Spring 拦截器提?Struts 应用E序中的日志记录能力?/P>
l束?/FONT>
在本文中Q?zhn)已经学?fn)?jin)?Struts 动作整合?Spring 框架中的三种H门。?Spring ?ActionSupport
来整?StrutsQ第一U窍门中是q样做的Q简单而快P但是?x)?Struts 动作?Spring 框架耦合在一赗如果?zhn)需要将应用E序ULC个不同的框架Q则需要重写代码。第二种解决Ҏ(gu)通过委托 RequestProcessor
巧妙地解开代码的耦合Q但是它的可扩展性不强,q且?Struts ?RequestProcessor
变成一pd命o(h)Ӟq种Ҏ(gu)持l不?jin)很长时间。第三种Ҏ(gu)是这三种Ҏ(gu)中最好的Q将 Struts 动作委托l?Spring 框架可以使代码解耦,从而(zhn)可以在(zhn)的 Struts 应用E序中利?Spring 的特性(比如日志记录拦截器)(j)?/P>
参考资?
学习(fn)
关于作?/FONT>

]]>