spring struts
控制倒置(IOC)设计模式已经产生嗡嗡声已经很长时间了,您肯定已经听说过。 如果您以任何能力使用过Spring框架,那么您已经了解了其工作原理。 在本文中,当我使用IOC模式的原理将Struts应用程序注入Spring框架时,您将直接了解IOC模式的功能。
将Struts应用程序集成到Spring框架中的优点是多方面的。 首先,Spring的显式设计旨在解决JEE的一些实际问题,例如复杂性,性能低下,可测试性等等。 其次,Spring框架包含一个AOP实现,使您可以将面向方面的技术应用于普通的面向对象的代码。 第三,有人可能会说,Spring框架比Struts本身处理 Struts更好。 但这是一个见解,因此在我演示将Struts应用程序集成到Spring框架中的三种方法之后,然后自己决定。
我演示的方法都相对容易执行,但是它们提供了明显不同的优势。 为了使您能够充分理解每种方法,我为每种方法创建了一个单独的工作示例。 有关完整的示例源代码,请参见下载部分。 请参阅相关主题 ,以下载Struts MVC和Spring框架。
Spring有什么了不起的?
Spring创建者Rod Johnson着眼于Java™Enterprise软件开发,并建议通过战略性使用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
类集成Struts - 用Spring的
DelegatingRequestProcessor
覆盖StrutsRequestProcessor
- 将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提供了一些帮助。 org.springframework.web.struts.ActionSupport
类提供了一个getWebApplicationContext()
方法来轻松获取Spring上下文。 您所需要做的就是从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),我创建一个Action
从春节扩展ActionSupport
类而不是Struts的Action
类。 在(2),我使用getWebApplicationContext()
方法获取ApplicationContext
。 为了获得业务服务,我使用在(2)处获得的上下文在(3)处查找Spring bean。
此技术简单易懂。 不幸的是,它将Struts动作与Spring框架耦合在一起。 如果您决定替换Spring,则必须重写代码。 而且,由于Struts动作不受Spring的控制,因此无法获得Spring AOP的好处。 当使用多个独立的Spring上下文时,此技术可能很有用,但在大多数情况下,它不如其他两个选择那样理想。
食谱2.覆盖RequestProcessor
将Struts动作与Spring分离是一种更明智的设计选择。 一种实现方法是用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),我已经使用name属性注册了一个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的管理,同时为您提供Spring动作管理框架的所有优势。 因为您的Struts动作不了解Spring的存在,所以您可以将Spring换成其他的控制容器倒置,而无需重构Struts代码。
尽管DelegatingRequestProcessor
方法绝对比第一种更好,但确实存在一些问题。 如果您使用其他RequestProcessor
,则需要手动集成Spring DelegatingRequestProcessor
。 添加的代码将成为维护麻烦,并且还会降低应用程序的灵活性。 此外,有人谈论用命令链代替Struts RequestProcessor
。 此类更改将负面影响此解决方案的寿命。
食谱3.将动作管理委托给Spring
更好的解决方案是将Struts动作管理委托给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>
标记的name属性(在本例中为“ /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功能。
行动委派的好处也不止于此。 一旦将Struts动作控制在Spring的控制之下,就可以利用Spring来给他们更多的便利。 例如,没有Spring,所有Struts操作都必须是线程安全的。 但是,如果将<bean>
标记的singleton属性设置为“ false”,则您的应用程序将在每个请求上都有一个新创建的操作对象。 您可能不需要此功能,但是很高兴知道您的后袋有此功能。 您还可以利用Spring的生命周期方法。 例如,当实例化Struts操作时, <bean>
标记的init-method
属性用于运行方法。 同样, destroy-method
属性在将bean从容器中删除之前执行一个方法。 这些方法是一种与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名称autoproxy,描述如何应用拦截器。 还有其他定义交点的方法,但是这种方法很常见且易于实现。
- 在(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的功能(例如日志记录拦截器)。
三个Struts-Spring集成配方中的每一个都可以作为一个完整的工作应用程序来实现。 请参阅下载部分以详细研究它们。
翻译自: https://www.ibm.com/developerworks/web/library/j-sr2/index.html
spring struts