副标题#e#
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 框架的要领后,详细由您本身抉择利用哪一种。
我所演示的要领都是执行起来相对简朴的,可是它们却具有明明差异的利益。我为每一种要领建设了一个独立而可用的例子,这样您就可以完全领略每种要领。
为什么 Spring 这么了不得?
Spring 的创建者 Rod Johnson 以一种批驳的目光对待 Java? 企业软件开拓,而且提议许多企业困难都可以或许通过计谋地利用 IOC 模式(也称作依赖注入)来办理。当 Rod 和一个具有奉献精力的开放源码开拓者团队将这个理论应用于实践时,功效就发生了 Spring 框架。简言之,Spring 是一个轻型的容器,操作它可以利用一个外部 XML 设置文件利便地将工具毗连在一起。每个工具都可以通过显示一个 JavaBean 属性收到一个到依赖工具的引用,留给您的简朴任务就只是在一个 XML 设置文件中把它们毗连好。
依赖注入是一个强大的特性,可是 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 的整合要领。您可以在这里看到例子的要点,可是您也可以下载应用措施以查察所有的细节。
#p#副标题#e#
窍门 1. 利用 Spring 的 ActionSupport
#p#分页标题#e#
手动建设一个 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>
#p#分页标题#e#
我操作了 <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。
#p#分页标题#e#
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>
行动委托的利益
#p#分页标题#e#
行动委托办理要领是这三种要领中最好的。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 行动。为了利用拦截器,您需要做三件事:
建设拦截器。 注册拦截器。 声明在那里拦截代码。
这看起来很是简朴的几句话却很是强大。譬喻,在清单 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”。
#p#分页标题#e#
就是这样。就像这个例子所展示的,将您的 Struts 行动置于 Spring 框架的节制之下,为处理惩罚您的 Struts 应用措施提供了一系列全新的选择。在本例中,利用行动委托可以轻松地操作 Spring 拦截器提高 Struts 应用措施中的日志记录本领。
竣事语
在本文中,您已经进修了将 Struts 行动整合到 Spring 框架中的三种窍门。利用 Spring 的 ActionSupport 来整合 Struts(第一种窍门中就是这样做的)简朴而快捷,可是会将 Struts 行动与 Spring 框架耦合在一起。假如您需要将应用措施移植到一个差异的框架,则需要重写代码。第二种办理要领通过委托 RequestProcessor 巧妙地解开代码的耦合,可是它的可扩展性不强,而且当 Struts 的 RequestProcessor 酿成一系列呼吁时,这种要领就一连不了很长时间。第三种要领是这三种要领中最好的:将 Struts 行动委托给 Spring 框架可以使代码解耦,从而使您可以在您的 Struts 应用措施中操作 Spring 的特性(好比日志记录拦截器)。