Spring整合Struts2有如下几步:
- 导入相关jar包
- 配置web.xml
- 编写测试类
- 配置applicationContext.xml
- 编写struts.xml配置文件
- 发布并且运行
IntelliJ IDEA:2016.3
Tomcat:apache-tomcat-7.0.67
JDK:com.sun.java.jdk.win32.x86_64_1.6.0.u43
本来想照着视频配置直接ssh,但是发现视频有点老,并且老是出错,所以只能先把spring整合struts搞定。
1. 首先导入Struts相关jar包
然后是spring包
(上图少导了spring-web-4.1.6.RELEASE.jar包,这个包在配置监听器时要用到)
除了导入Struts2和Spring的核心库之外,还要导入commons-logging和struts2-spring-plugin包,否则启动会出异常。我发现在只用Struts2时如果导入struts2-spring-plugin包会出现异常,导致程序不能正常启动。(就因为这个问题困扰了好久!!切记切记)。
2. 配置web.xml
既然有Struts2,核心拦截器的配置是不可少的
通过配置ContextLoaderListener监听器,使容器启动时,自动加载applicationContext配置,因为它实现了ServletContextListener这个接口,容器启动时会自动执行它实现的方法。
默认情况下,会加载WEB-INF/applicationContext.xml这个文件,我们可以通过配置contextConfigLocation参数改变配置文件的路径。
以上配置均在web.xml文件的<web-app></web-app>
区域。
3. 编写测试类
在浏览器请求一个Action方法,在Action方法内向一个对象请求一个List,然后转到index.jsp页面,在页面中输出Action请求到的List。
通过Spring依赖配置,控制Action请求的对象。
首先要编写一个接口,Action方法依赖这个接口,通过调用接口中的方法获取List:
package cn.test;
import java.util.List;
public interface TestInterface {
public List getList();
}
编写两个实现IocTestInterface接口的类,用来提供数据:
package cn.test;
import java.util.ArrayList;
import java.util.List;
public class TestImpl implements TestInterface {
@Override
public List getList() {
List l = new ArrayList();
l.add("abc");
l.add("def");
l.add("hig");
return l;
}
}
package cn.test;
import java.util.ArrayList;
import java.util.List;
public class Test2Impl implements TestInterface {
public List getList() {
List l = new ArrayList();
l.add("123");
l.add("456");
l.add("789");
return l;
}
}
下面编写Action类,这个类继承ActionSupport类,并覆盖其中的execute方法,
execute方法执行时,调用实现了上述接口对象的getList方法获取List:
package cn.test;
import com.opensymphony.xwork2.ActionSupport;
import java.util.List;
public class TestAction extends ActionSupport{
private TestInterface ti;
private List list;
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public TestInterface getTi() { return ti; }
public void setTi(TestInterface ti) {
this.ti = ti;
}
public String execute() throws Exception {
this.setList(ti.getList());
return super.execute();
}
}
编写用来显示运行结果的jsp文件
遍历list,并将每个元素作为一行来显示:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<body>
This is my JSP page. <br><br>
<s:iterator value="list" id="current">
<li><s:property value="current"/></li>
</s:iterator>
</body>
</html>
系统的结构就是这样。
4. 配置applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="testImpl" class="cn.test.TestImpl"/>
<bean id="testAction" class="cn.test.TestAction">
<property name="ti" ref="testImpl"/>
</bean>
</beans>
文件配置了id为testAction的bean,路径为刚刚编写的Action类的路径,其中ti对象(请求数据的对象)配置为TestImpl(这里引用另一个实现TestInterface的实体类)。
5. 编写struts.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!--告知Struts2运行时使用Spring创建对象-->
<constant name="struts.objectFactory" value="spring"/>
<!--开发者模式-->
<constant name="struts.devMode" value="true" />
<package name="test" extends="struts-default">
<action name="test" class="testAction">
<result>/WEB-INF/test/test.jsp</result>
</action>
</package>
</struts>
注意:
struts2与spring集成时,关于class属性及成员bean自动注入的问题
本来在action的class上我写的是TestAction的全限定路径,但是一直引发java.lang.NullPointerException,测试了一下发现Spring没有将实体类ti注入Struts属性中。后来网上查了一下发现如下说法:
正常来说按照spring官方配置,在struts2与spring整合时,struts配置文件中class属性指向spring配置的bean id,但是在class指向类路径时,依然能注入service
public class LoginAction extends ActionSupport{
private LoginService loginService;
public void setLoginService(LoginService loginService) {
System.out.println("init Service......");
this.loginService = loginService;
}
spring配置:
bean id="loginService" class="org.xxxxx.services.impl.LoginServiceImpl"></bean>
<bean id="loginAction" class="org.xxxxx.action.LoginAction">
</bean>
struts配置:
<action name="login" class="org.xxxxx.action.LoginAction">
<result name="success">/result.jsp</result>
<result name="error">/login.jsp</result>
</action>
1.注意看spring中第二个bean和struts中的class,在struts.xml中的class按照上面这样配置指定全类路径名的话,这时,不论spring配置文件中的第二个bean有没有指定配置<property name="loginService" ref="loginService"/>
,只要有<bean id="loginService" .../>
存在,并且这个ID的名字与Action中成员bean的名字一致,当实例化Action类时,会一并将loginService的实例注入。
并且还存在一个问题当struts.xml此种方式配置时,spring中如果配置了此action实例,并添加scope=“prototype”多例属性,则会在访问时报错。可能struts2本身是多例与spring实例化机制冲突。所以此种方式配置时,可废弃spring中的action类配置。
2.如果<action name="login" class="loginAction">
这里的class指定spring配置文件中的bean的id,则不会出现loginService自动注入问题,而是根据<bean id="loginAction" class="org.xxxxx.action.LoginAction"></bean>
有没有指定配置<property name="loginService" ref="loginService"/>
来决定,有<property name="loginService" ref="loginService"/>
的指定,则实例化Action类时,会一并将loginService实例注入,没有配置property,loginService则为空
所以会产生两种struts与spring的配置方案:
(1)配置方案一:
在配置Action时,需要将class属性和Spring配置文件中的相对应的Action的bean的Id的属性保持一致,系统即可通过Spring来装配和管理Action。
(1)配置方案二:
前面两个步骤和方案一的一样;
在配置struts.xml文件时,Action的class为该Action的类路径,而在applicationContext.xml配置文件中不需要添加Action的bean配置。这样,当我们使用Action类时,由于studentInfoService已经配置了相关的bean,所以会自动装配,并且action依然由spring进行实例化。
6. 发布并且运行
发布后启动Tomcat,用浏览器打开地址http://localhost:8080/XXX/test.action,获得了下面的页面:
这个算是初步搞完了,得继续把spring整合hibernate做出来!!!