第二十五部分_Struts2.1与Spring整合
依赖注入之后、对象销毁之前自动调用方法:
通过类似于之前Spring项目编码的方式,我们可以通过在setXXX()方法中输出相关的语句来获悉依赖关系注入的执行时机,通过下面介绍的方法可以在依赖关系注入完成之后自动执行一些方法。
如果我们想让一个类的实例在所有属性都已经设置好之后,就让它自动执行某些方法,有两种方式:
- 实现InitializingBean接口,并且实现里面的唯一方法afterPropertiesSet(),这个方法就会在所有的属性注入完之后自动的得到调用。
- 在该类中定义一个任意名称的方法(比如init()),在applicationContext.xml中对应类的bean标签中添加init-method="init"。(前面指定的init名称)
Spring官方文档推荐使用后者,可以做到尽量使得自定义类不与Spring发生关系(耦合),因为Spring倡导的原则是:尽量减少接口之间的耦合性,Spring号称是非侵入性的,即使得整个程序根本感觉不到Spring的存在,只需要把相关的信息都配置到配置文件里,Spring就能把本来一个一个独立的bean拼装成一系列互相关联的对象。
有初始化就有销毁,实现DisposableBean接口中的唯一抽象方法destroy()或者配置destroy-method属性,可以在对象销毁之前得到自动调用。
对于Spring的bean工厂(IoC)这块暂时先告一段落。
下面我们看下一如何将前端框架Struts和一站式框架Spring(本身提供了对MVC的支持,Spring MVC,之所以不讲这个是因为它在国外用的很多,但是在国内用的比较少)有机的整合起来。
首先新建一个Web Project,命名为strutsspring。
1.首先完成struts相关的配置,拷贝之前项目的struts.xml至src目录下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd" > <struts> < package name= "strutsspring" extends = "struts-default" > </ package > </struts> |
2.拷贝struts依赖的六个jar文件再加上io的jar包共七个jar文件至WEB-INF的lib目录下。
3.在web.xml中进行相关配置,使之支持struts(也增加了对Spring的支持,后文介绍)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?xml version= "1.0" encoding= "UTF-8" ?> <web-app version= "2.5" xmlns= "http://java.sun.com/xml/ns/javaee" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http: //java.sun.com/xml/ns/javaee http: //java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <filter-name>struts2</filter-name> <filter- class > org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter- class > </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener- class > org.springframework.web.context.ContextLoaderListener </listener- class > </listener> </web-app> |
4.做完以上步骤,现在的工程就是一个典型的struts的工程。接下来把其Contextpath配置到tomcat(server.xml)中。
<Context path="/strutsspring" docBase="D:\JavaWeb\strutsspring\WebRoot" reloadable="true"/>
启动服务器,访问localhost:8080/strutsspring,页面弹出This is my JSP page.即为成功。
下面引入对Spring的支持,由于现在是一个web项目,比之以前普通的Java Application引入的包则多一些。
选中项目,MyEclipse->Add Spring Capabilities,选择Spring version 为Spring 2.5,jar包选择Spring 2.5 AOP Libraries、Spring 2.5 Core Libraries、Spring 2.5 Web Libraries,JAR Library Installation 选择Copy checked Library contents to project folder,点击Next,去掉Enable AOP Builder前面的勾勾,然后在Folder中将src改为WebRoot/WEB-INF,点击Finish。
到此为止,Struts和Spring都纳入到项目中了,下面要做的就是将二者粘合起来,有两个步骤:
- 将Struts的Spring插件也增加到WEB-INF的lib目录下。在官网下载的struts2.1.6下的lib目录下有一个struts2-spring-plugin-2.1.6.jar。这样Action的创建、销毁等工作都由Spring代为管理,Struts就放手不管了。这是为什么呢?来到struts2-core-2.1.6.jar下面,里面有一个org.apache.struts2,其下有一个default.properties:在36-39行处有被注释掉的文字:
1 2 3 4 | ### if specified, the default object factory can be overridden here ### Note: short -hand notation is supported in some cases, such as "spring" ### Alternatively, you can provide a com.opensymphony.xwork2.ObjectFactory subclass name here # struts.objectFactory = spring |
而在struts-spring-plugin-2.1.6.jar中有一个struts-plugin.xml,有一个地方将Spring的object factory给启用了:
1 2 | <!-- Make the Spring object factory the automatic default --> <constant name= "struts.objectFactory" value= "spring" /> |
- 在web.xml中增加一个Spring的启动类(以前的版本通过一个filter启动,后续版本中通过一个listener启动:服务器一启动,listener的Class被实例化)
12345
<listener>
<listener-
class
>
org.springframework.web.context.ContextLoaderListener
</listener-
class
>
</listener>
到现在,前期的配置上的整合工作才算结束。
重启服务器,可以看到输出中有一行信息:
信息: Initializing Spring root WebApplicationContext
查看Spring的API文档,可知WebApplicationContext实现了BeanFactory接口。
下面,我们整合这两个框架,看看中间的流程是如何进行的。
新建login.jsp(引入Struts标签库<%@ taglib uri="/struts-tags" prefix="s" %>):
1 2 3 4 5 6 7 8 | <body> <s:form action= "login" > <s:textfield name= "username" size= "20" label= "username" ></s:textfield> <s:textfield name= "password" size= "20" label= "password" ></s:textfield> <s:submit value= "submit" ></s:submit> </s:form> </body> |
然后新建包com.test.action,在其下新建类LoginAction(增加了服务接口):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | package com.test.action; import com.opensymphony.xwork2.ActionSupport; import com.test.service.LoginService; import com.test.service.impl.LoginServiceImpl; public class LoginAction extends ActionSupport { private String username; private String password; private LoginService loginService; public LoginService getLoginService() { return loginService; } public void setLoginService(LoginService loginService) { this .loginService = loginService; } public String getUsername() { return username; } public void setUsername(String username) { this .username = username; } public String getPassword() { return password; } public void setPassword(String password) { this .password = password; } @Override public String execute() throws Exception { // 注意:业务逻辑永远不要写在Action里,在Action中应当只是完成数据校验、类型转换这样一些前期的处理工作。前期处理得到合法数据之后,才进入后端的业务逻辑层。 // 如果没有Spring可以考虑这样写(缺点:接口和实现耦合在一块了): // loginService = new LoginServiceImpl(); // loginService.isLogin(username, password); if (loginService.isLogin(username, password)) { return SUCCESS; } else { return ERROR; } } } |
业务逻辑的处理——新建包com.test.service,在下面新建接口LoginService:
1 2 3 4 5 6 | package com.test.service; public interface LoginService { public boolean isLogin(String username, String password); } |
新建com.test.service.impl包,对service接口的实现类写在里面LoginServiceImpl(完成真正的业务处理):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.test.service.impl; import com.test.service.LoginService; /** * 该类完成真正的业务处理 * @author asus * */ public class LoginServiceImpl implements LoginService { public boolean isLogin(String username, String password) { if ( "hello" .equals(username) && "world" .equals(password)) { return true ; } else { return false ; } } } |
实现类定义完了,最终肯定是供Action调用的,如何让Action知道是Spring帮我们做的这件事?
- 在LoginAction中声明它所需要的服务接口是什么。
- 然后在struts.xml中配置(注意action中的class属性不再是com.test.action.LoginAction):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd" > <struts> < package name= "strutsspring" extends = "struts-default" > <!-- loginAction是Spring的applicationContext.xml中bean的id中声明的一个名字 --> <action name= "login" class = "loginAction" > <result name= "success" >/success.jsp</result> <result name= "error" >/error.jsp</result> </action> </ package > </struts> |
在applicationContext.xml中配置:
1 2 3 4 5 6 7 8 9 10 11 12 | <?xml version= "1.0" encoding= "UTF-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns:p= "http://www.springframework.org/schema/p" xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" > <bean id= "loginService" class = "com.test.service.impl.LoginServiceImpl" ></bean> <bean id= "loginAction" class = "com.test.action.LoginAction" > <property name= "loginService" ref= "loginService" ></property> </bean> </beans> |
LoginAction中的execute方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Override public String execute() throws Exception { // 注意:业务逻辑永远不要写在Action里,在Action中应当只是完成数据校验、类型转换这样一些前期的处理工作。前期处理得到合法数据之后,才进入后端的业务逻辑层。 // 如果没有Spring可以考虑这样写(缺点:接口和实现耦合在一块了): // loginService = new LoginServiceImpl(); // loginService.isLogin(username, password); if (loginService.isLogin(username, password)) { return SUCCESS; } else { return ERROR; } } |
补充success.jsp(引入标签库)和error.jsp:
1 2 3 4 | <body> username:<s:property value= "username" /><br/> password:<s:property value= "password" /> </body> |
error.jsp:
1 2 3 | <body> login error!!! </body> |
重启服务,测试输入hello world显示success.jsp页面对应的信息,否则输出错误信息login error!!!
补充说明:
由于LoginServiceImpl是无状态的类,没有属性,只有方法,new多少次都没有变化,因此需要在applicationContext.xml的bean中增加一个scope属性(注意:对于action来说,一定要将其配置成prototype或是request):
1 2 | <bean id= "loginService" class = "com.test.service.impl.LoginServiceImpl" scope= "singleton" ></bean> <bean id= "loginAction" class = "com.test.action.LoginAction" scope= "prototype" > |
此外如果不配置scope,其默认值为singleton,因此配置scope除了影响效率,还影响程序的正确性。
对于Spring的配置文件的bean元素,其scope属性有如下几个值:
- singleton,单例。
- prototype,表示每次从容器中取出bean时,都会生成一个新实例。相当于new出来一个对象。
- request,该属性是基于web的,表示每次接受一个请求时,都会生成一个新实例。在这种情况下,request与prototype一样。
- session,表示每个session中该对象只有一个。
- globalSession。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· SQL Server如何跟踪自动统计信息更新?
· 官方的 MCP C# SDK:csharp-sdk
· 一款 .NET 开源、功能强大的远程连接管理工具,支持 RDP、VNC、SSH 等多种主流协议!
· “你见过凌晨四点的洛杉矶吗?”--《我们为什么要睡觉》
· 提示词工程师自白:我如何用一个技巧解放自己的生产力
· C#/.NET/.NET Core技术前沿周刊 | 第 31 期(2025年3.17-3.23)