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