最近下了struts的2.5.10版本,想跑个小例子看看,结果遇到了不少的坑,大部分都是自己坑自己。先看操作步骤:首先导入struts2的8个核心jar包:
commons-fileupload-1.3.2.jar
commons-io-2.4.jar
commons-lang3-3.2.jar
commons-loggin-1.1.3.jar
freemarker-2.3.23.jar
javassist-3.20.0.GA.jar
ognl-3.1.12.jar
strut2-core-2.5.10.jar
然后在web.xml中加入核心过滤器类的配置:
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>test</filter-name> <url-pattern>/web/*</url-pattern> </filter-mapping>
接着新增struts.xml到src根目录:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache SoftWare Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apche.org/dtds/struts-2.5.dtd"> <struts> <constant name="struts.devMode" value="true" /> <constant name="struts.i18n.encoding" value="utf-8" /> <constant name="struts.custom.i18n.resources" value="globalMessages" /> <package name="default" extends="struts-default" namespace="/web/struts2"> <action name="login" class="com.wulinfeng.test.struts2.action.LoginAction" method="login"> <result>/web/struts2/success.jsp</result> <result name="input">/web/struts2/login.jsp</result> </action> </package> </struts>
最后新增一个action类和两个jsp就可以开跑了。然后坑就来了:
1、直接tomcat启动失败:
严重: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/Struts2]]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:911)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:890)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1403)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1393)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/Struts2]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:158)
… 6 more
Caused by: java.lang.IllegalArgumentException: Filter mapping specifies an unknown filter name test
at org.apache.catalina.core.StandardContext.validateFilterMap(StandardContext.java:2941)
at org.apache.catalina.core.StandardContext.addFilterMap(StandardContext.java:2903)
at org.apache.catalina.startup.ContextConfig.configureContext(ContextConfig.java:1266)
at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1181)
at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:771)
at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:298)
at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:94)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5075)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
… 6 more
一开始怀疑jar包缺失问题,导入了其他几个jar包试了下还是一样的现象,后来看到标红那一行,怀疑是web.xml里过滤器配置问题,一看傻眼了,我把struts2这个过滤器名称在filter-mapping里写成test了,都是复制粘贴惹的祸,因为之前该web.xml里还有个另外一个叫test的servlet配置。改成struts2后tomcat启动顺利,第二个坑出现:
2、执行action报错:
严重: Exception starting filter struts2
java.lang.ClassNotFoundException: org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1274)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1108)
at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:520)
at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:501)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:118)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:258)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:105)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4561)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5203)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1403)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1393)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
从报错信息看是找不到核心过滤器了。一开始我是选中工程后右键点开Build Path,导入以上8个jar,没注意在WEB-INF下的lib目录依然是空的,实际上tomcat容器是从这个目录下读取的jar包。怎么把jar放到WEB-INF下的lib目录下呢?还是需要选中工程,右键点开Properties->Deployment Assembly->点击Add->Java Build Path Entries->Next->选择需要的jar->Finish->OK。还有个小坑也少jar包,这个比较简单,日志一看就知道少了log4j-api-2.7.jar,所以最少需要9个核心jar包才能跑起来。
3、struts.xml配置文件错误:
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console…
ERROR Dispatcher Dispatcher initialization failed
Unable to load configuration. – Class: sun.net.www.protocol.http.HttpURLConnection
File: HttpURLConnection.java
Method: getInputStream0
Line: 1840 – sun/net/www/protocol/http/HttpURLConnection.java:1840:-1
at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:70)
at org.apache.struts2.dispatcher.Dispatcher.getContainer(Dispatcher.java:906)
at org.apache.struts2.dispatcher.Dispatcher.init_PreloadConfiguration(Dispatcher.java:445)
at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:486)
at org.apache.struts2.dispatcher.InitOperations.initDispatcher(InitOperations.java:75)
at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.init(StrutsPrepareAndExecuteFilter.java:63)
at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:279)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:260)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:105)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4561)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5203)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1403)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1393)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: Unable to load file:/D:/wulinfeng/tomcat/apache-tomcat-9.0.0.M9/webapps/Struts2/WEB-INF/classes/struts.xml – Class: sun.net.www.protocol.http.HttpURLConnection
这个坑从日志里也可以很明显的看出来是struts.xml文件有问题,而且是dtd的url有问题,复制http://struts.apche.org/dtds/struts-2.5.dtd到浏览器里一打开,果然跳不过去,因为是自己手敲的,竟然把apache少敲了个a。把少了的a还给它后终于页面出来了,提交表单后action到struts.xml读取视图名并跳转正常。
4、使用自定义拦截器login,页面form提交的model对象获取不到了。比如我用到了一个userBean输入用户名密码,现在点submit后userBean就是个null。定位发现原来表单提交后的数据都是通过默认拦截器获取的,所以自定义拦截器需要跟默认拦截器组装成拦截器栈才没问题,struts.xml配置如下:
<interceptors> <interceptor name="login" class="com.wulinfeng.test.struts2.interceptor.LoginInterceptor" /> <interceptor-stack name="login-stack"> <interceptor-ref name="login" /> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors>
5、自定义了一个局部类型转换器后始终不起作用,这个坑巨坑无比,不得不吐槽一下。struts2的数据类型转换、校验和国际化的条条框框太多了,用起来实在不趁手。就说这个类型转化,首先要求类型转换类必须跟action类在同一个目录下,还得搞个properties文件也在该目录下,而且自定义类型转换类里的属性bean、properties里文件的key、页面form里的bean都必须跟action里的属性bean是同一个名字。问题出在原来没使用自定义类型转换前,页面form里用userBean.name和userBean.pwd,没改。问题现象是页面form点击提交后,死活进不到自定义的类型转换类。下面贴代码:
action类:
package com.wulinfeng.test.struts2.action; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionContext; import com.wulinfeng.test.struts2.entity.UserBean; public class LoginAction { private UserBean userBean; public String login() { if (null != userBean && "wulinfeng".equals(userBean.getName()) && "123456".equals(userBean.getPwd())) { userBean.setTips("登录成功!"); ActionContext.getContext().getSession().put("user", userBean); System.out.println(userBean.getName()); return Action.SUCCESS; } if (null == userBean) { userBean = new UserBean(); } userBean.setTips("登录失败!请重新登录。"); return Action.INPUT; } public String register() { this.userBean.setTips("恭喜你" + userBean.getName() + ", 您已注册成功!"); return Action.SUCCESS; } public UserBean getUserBean() { return userBean; } public void setUserBean(UserBean userBean) { this.userBean = userBean; } }
自定义类型转换类:
package com.wulinfeng.test.struts2.action; import java.util.Map; import org.apache.struts2.util.StrutsTypeConverter; import com.wulinfeng.test.struts2.entity.UserBean; public class UserBeanConverter extends StrutsTypeConverter { @Override public Object convertFromString(Map context, String[] values, Class toClass) { UserBean userBean = new UserBean(); userBean.setName(values[0]); userBean.setPwd(values[1]); userBean.setAddress(values[2]); return userBean; } @Override public String convertToString(Map context, Object obj) { UserBean userBean = (UserBean) obj; return "用户名:" + userBean.getName() + "," + "密码:" + userBean.getPwd() + "," + "地址:" + userBean.getAddress(); } }
类型转换配置文件LoginAction-conversion.properties,必须在com.wulinfeng.test.struts2.action目录下:
userBean=com.wulinfeng.test.struts2.action.UserBeanConverter
login.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Longin</title> </head> <body> <s:property value="userBean.tips" /> <s:form action="login.action"> <s:textfield name="userBean.name" label="用户名"/> <s:textfield name="userBean.pwd" label="密 码"/> <s:textfield name="userBean.address" label="住 址"/> <s:submit value="登录" /> <s:submit value="注册" onclick="register()" /> </s:form> <script> function register() { var form = document.forms[0]; form.action = "register.action"; form.submit(); } </script> </body> </html>
我在action里属性bean叫userBean,而页面form里userBean.name跟userBean在Struts2眼里就是两个不同东西,所以就不会进行类型转换了,只能把所有属性字段都改成userBean才行。