struts2简介
struts2是在struts1的基础上,融合了另一个web框架Webwork,已Webwork作为核心,与struts1已经有了很大的区别。struts2与springMVC都是MVC框架,区别如下:
- 框架机制:struts2采用Filter(StrutsPrepareAndExecuteFilter)实现,SpringMVC采用Servlet(DispatcherServlet)实现。
- 拦截机制:struts2框架是类级别的拦截,每次请求就会创建一个Action,和Spring整合时struts2的ActionBean注入作用域是原型模式prototype(否则会出现线程并发问题), 然后通过setter、getter将request数据注入到属性;Struts2中,一个Action对应一个request,response上下文,在接收参数时,可以通过属性接收,这说明属性参数是让多个方法共享的。SpringMVC是方法级别的拦截,一个方法对应一个Request上下文,所以方法直接基本上是独立的,独享request,response数据。而每个方法同时又何一个url对应,参数的传递是直接注入到方法中的,是方法所独有的。处理结果通过ModeMap返回给框架;与Spring整合时SpringMVC的Controller Bean默认为单例模式Singleton,因为没有共享属性所以线程是安全的
- 性能方面:SpringMVC可以实现零配置(注解),SpringMVC是基于方法的拦截,只加载一次单例模式bean注入。而struts2是类级别的拦截,每次请求都会创建一个新的Action实例,加载所有的属性值注入。因此SpringMVC开发效率和性能高于struts2。
- 拦截器:struts2有自己的拦截Interceptor机制,SpringMVC采用的是独立的Aop方式,Struts2的配置文件相对会比SpringMVC大。
- 配置方面:SpringMVC与Spring是无缝的,可以实现100%零配置
- 集成方面:SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。
Struts2架构
处理流程:
- 客户端发起HTTP请求
- 核心过滤器StrutsPrepareAndExecuteFilter拦截请求,将请求转发到Action映射器
- Action映射器负责识别当前请求是否需要交给Struts2处理,Action映射器返回需要Struts2处理的信息,StrutsPrepareAndExecuteFilter会创建一个Action代理
- Action代理通过询问配置管理器从struts.xml中获取需要调用的Action信息
- Action代理创建相关请求的Action对象(代理模式),调用相关的方法之前,会先执行struts2的一系列拦截器(如参数拦截器注入属性值等)
- 默认调用execute方法(也可以通过相关配置指定调用的方法)返回字符串去匹配响应的页面
- 获取模板页面渲染后生成最终返回的页面,在倒序执行拦截的方法
- 生成HTTP响应返回给客户端
Struts2入门案例
(1)编辑web.xml配置文件,配置struts2核心拦截器
<!-- 配置struts2的前端控制器(Filter) -->
<filter>
<filter-name>s2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>s2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(2)编写请求表单login.jsp
<h3>${errorMessage }</h3>
<form action="login" method="post">
UserName:<input type="text" name="userName"/><br/>
Password:<input type="password" name="password"/><br/>
<input type="submit" value="login"/>
</form>
(3)编写控制器Action类处理用户请求LoginAction
public class LoginAction implements Action{
private String userName;
private String password;
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;
}
private String errorMessage;
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
@Override
public String execute() throws Exception {
System.out.println("before:"+this.userName);
String userName = new String(this.userName.getBytes("iso-8859-1"),"GBK");
System.out.println("after:"+userName);
if("admin".equals(userName)&&"123".equals(password)){
ActionContext ac = ActionContext.getContext();
Map<String,Object> request = (Map<String,Object>)ac.get("request");
request.put("message", "requestMessage");
return SUCCESS;
}else{
this.errorMessage = "用户名或者密码错误";
return LOGIN;
}
}
}
(4)src目录下struts.xml中配置Action
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="struts01" namespace="/" extends="struts-default"> <!--包名name随便取,namespace命名空间,extends继承struts-core包下struts-default.xml配置-->
<!--处理login请求,默认执行execute方法-->
<action name="login" class="com.bwf.struts01.controllers.LoginAction" >
<!--根据返回字符串映射响应页面-->
<result name="success" >/welcome.jsp</result>
<result name="login" type="redirect">/login.jsp</result>
</action>
</package>
</struts>
struts2配置文件
Struts2核心配置文件是struts.xml,该文件主要负责管理业务控制器Action,
struts2还有其他配置文件通过核心过滤器的init初始化方法来加载,加载顺序如下(配置可覆盖):
default.properties:该文件保存在struts2-core-2.3.32.jar中的org.apache.struts2包里面,保存一些常量,可以使用constant来配置。
struts-default.xml :该文件保存在struts2-core-2.3.32.jar中,定义了
一些struts2的基础Bean和struts2内建支持的结果类型,还struts2内建的拦截器
(拦截器有很多,分为一系列的拦截器块,我们默认使用defaultStack拦截器块)
struts-plugin.xml :该文件保存在struts-xxx-2.3.32.jar等struts2插件jar包中。
struts.xml:是web应用默认的自己的struts2配置文件
struts.properties :是struts2的默认配置文件
web.xml:web应用的配置文件
struts2常量(constant)
struts2配置文件中常用常量
struts.i18n.encoding:该常量指定struts2应用默认使用的字符集。
struts.objectFactory.spring.autoWire:和spring框架整合有关。
struts.multipart.parser:指定文件上传用的组件。默认为jakarta(即common-fileupload上传组件)
struts.multipart.saveDir:指定上传文件的临时保存路径
struts.multipart.maxSize:指定上传中整个请求所允许上传的最大字节数。
struts.action.extension:指定struts2需要处理的请求后缀。默认值为.action或者什么都不写
struts.serve.static.browserCache:设置浏览器是否缓存静态内容,当应用处于开发阶段时,我们希望不缓存,可设置为false
struts.enable.DynamicMethodInvocation:设置struts2是否支持动态方法调用,默认值为true
struts.devMode:设置struts2是否使用开发模式。如果设置为true,为开发模式,修改struts.xml配置文件不需要重启服务器,会显示更多更友好的错误提示。
struts.ui.theme:指定视图主题。
struts.url.includeParams:指定struts2生成url时是否包含请求参数。有none,get和all三个值。分别对应不包含,仅包含get类型请求参数和包含全部请求参数。
修改struts2常量
在struts.xml文件中配置
<constant name="struts.i18n.encoding" value="utf-8"/>
<constant name="struts.devMode" value="true"/>
<constant name="struts.action.extension" value="do"/>
<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
在web.xml文件中配置常量
<init-param>
<param-name>struts.action.extension</param-name>
<param-value>do</param-value>
</init-param>
bean配置(了解即可)
struts2是一个高度可扩展的框架。框架的大部分核心组件,并不是硬编码写在代码中,而是以自己的IOC容器管理框架的核心组件。
struts2以可配置的方式来管理核心组件,从而允许开发者很方便的扩展该框架的核心组件,当开发者需要扩展,替换核心组件时,只需要提供自己组件的实现类,将其部署在struts2的IoC容器中即可。
struts-default.xml文件中配置了大量的struts2框架的内置Bean
<struts>
<!--
class:必填属性。指定了Bean实例的实现类
type:可选属性。制定了Bean实例实现的struts2规范,该规范通过某个接口实现
name:可选属性。Bean实例的名字
scope:可选属性。指定Bean实例的作用域request/session/singleton/thread
static:可选属性。指定Bean是否使用静态方法注入
optional:可选属性。指定该Bean是否是一个可选Bean
-->
<bean name="myHandler" class="com.bwf.struts01.exception.MyUnkownAction" type="com.opensymphony.xwork2.UnknownHandler"/>
</struts>
public class MyUnkownAction implements UnknownHandler{
/**
* -handleUnknownAction:用户请求未知Action时,该方法会被回调
* -handleUnknownActionMethod:用户请求指定Action的未知方法时,该方法会被回调
* -handleUnknownResult:Action处理结束后返回一个未知Result时,该方法会被回调
*/
@Override
public ActionConfig handleUnknownAction(String namespace, String actionName) throws XWorkException {
System.out.println(actionName+"不存在");
return null;
}
@Override
public Result handleUnknownResult(ActionContext actionContext, String actionName, ActionConfig actionConfig,
String resultCode) throws XWorkException {
System.out.println(resultCode+"不存在");
return null;
}
@Override
public Object handleUnknownActionMethod(Object action, String methodName) {
System.out.println(methodName+"不存在");
return null;
}
}
分文件编写
实际项目中,将struts2配置文件拆分成多个配置文件来进行模块化设计,提高配合文件的可读性
<struts>
<include file="struts1.xml"/>
<include file="struts2.xml"/>
.......
</struts>
package配置
<!--
name:必选属性。配置包名,是包的唯一标识
extends:可选属性。表示继承其他包,一般指定继承struts-default
abstract:可选属性。指定该包是否为抽象包,抽象包不可定义Action
namespace:可选属性。定义命名空间,用于分模块,同一命名空间不可定义同名的Action;请求url会带有命名空间,默认命名空间为""
strict-method-invocation:可选属性。struts 2.5默认为true,用于限制DynamicMethodInvocation,
未通过method属性配置或<allowed-methods>标签标明的方法将不允许被访问,启用DMI需要设置为false,
-->
<package name="demo" extends="struts-default" abstract="false" namespace="" strict-method-invocation="true">
</package>
命名空间的匹配优先级为:精准命名空间 > 根命名空间 > 默认命名空间。
package内部配置
<package name="demo" extends="struts-default" abstract="false" namespace="/" strict-method-invocation="true">
<!--result类型和处理类,默认的有chain、dispatcher、freemarker、httpheader、redirect、redirect、
Action、stream、velocity、xslt、plainText、postback-->
<result-types>
<result-type name="" class=""></result-type>
</result-types>
<!--注册拦截器和拦截器栈-->
<interceptors>
<!--拦截器-->
<interceptor name="" class=""/>
<!--拦截器栈-->
<interceptor-stack name="">
<interceptor-ref name=""/>
</interceptor-stack>
</interceptors>
<!--默认拦截器或拦截器栈配置,默认为defaultStack-->
<default-interceptor-ref name="defaultStack"/>
<!--当没有Action匹配时,使用默认Action-->
<default-action-ref name="fileDl"/>
<!--当Action没有配置class时,使用ActionSupport处理-->
<default-class-ref class="com.opensymphony.xwork2.ActionSupport"/>
<!--全局result配置-->
<global-results>
<result name="success" type="redirect">/success.jsp</result>
<result name="error" type="dispatcher">/error.jsp</result>
</global-results>
<!--全局可访问的方法,配置Action可访问的方法,中间用逗号隔开-->
<global-allowed-methods>execute,input</global-allowed-methods>
<!--全局异常映射,发生对应异常时返回result-->
<global-exception-mappings>
<exception-mapping exception="java.sql.SQLException" result="error"></exception-mapping>
</global-exception-mappings>
<!--Action配置-->
<action name="login" class="com.bwf.struts01.controllers.LoginAction" method="execute" converter="">
<!--参数注入-->
<param name="username">yt</param>
<!--当前Action结果result映射-->
<result name="success" type="redirect">/success.jsp</result>
<!--使用自定义拦截器,默认拦截器栈会失效,因此需要引用-->
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="">
<param name=""></param>
</interceptor-ref>
<!--当前Action中异常处理-->
<exception-mapping exception="java.lang.ArrayIndexOutOfBoundsException" result="error"/>
<!--当前Action允许的访问的方法-->
<allowed-methods>execute</allowed-methods>
</action>
</package>
上述配置后续会逐步讨论
Struts2的Action
Action是业务控制器,负责处理用户的请求,需要在struts.xml中配置。
Action的创建
(1)采用低侵入的方式创建,不需要实现或继承任何接口和类
public class SimpleAction {
public String execute() throws Exception{
System.out.println("in SimpleAction#execute...");
return Action.SUCCESS;
}
}
(2)实现Action接口,Action接口定义了五个常量和execute方法
public interface Action {
String SUCCESS = "success";
String NONE = "none";
String ERROR = "error";
String INPUT = "input";
String LOGIN = "login";
String execute() throws Exception;
}
(3)继承Action接口的实现类ActionSupport,该类提供了很多的默认方法,包括获取国际化信息,数据校验的方法等。大大简化了Action的开发。(推荐)
Action的调用
无论何种方式创建的Action,当我们请求Action时都会默认调用它的execute方法,当然我们也可以通过配置来执行我们指定的方法。
method属性
public class UserAction extends ActionSupport{
public String selectUser() {
return "success";
}
public String addUser() {
return "success";
}
public String delUser() {
return "success";
}
public String updateUser() {
return "success";
}
}
<!--struts.xml中配置Action-->
<struts>
<package name="user" namespace="/user" extends="struts-default">
<action name="selectUser" class="com.bwf.struts01.controllers.UserAction" method="selectUser">
<result name="success">/welcome.jsp</result>
<result name="error">/error.jsp</result>
</action>
<action name="addUser" class="com.bwf.struts01.controllers.UserAction" method="addUser">
<result name="success">/welcome.jsp</result>
<result name="error">/error.jsp</result>
</action>
<action name="delUser" class="com.bwf.struts01.controllers.UserAction" method="delUser">
<result name="success">/welcome.jsp</result>
<result name="error">/error.jsp</result>
</action>
<action name="updateUser" class="com.bwf.struts01.controllers.UserAction" method="updateUser">
<result name="success">/welcome.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
</struts>
由上面配置我们发现随着Action的增多,配置文件相当的冗余,struts2提供了通配符来解决上述问题。
通配符
特殊符号 | 意义 |
---|---|
* | 匹配除了斜杠(’/’)字符之外的零个或多个字符 |
** | 匹配零个或多个字符,包括斜杠(’/’)字符 |
\ | 反斜线字符用作转义序列,所以\*匹配字符星号(*),\\匹配字符反斜杠(\) |
之前的配置可使用通配符合成如下配置
<!--通配符*尽量放在末尾,否则可能会引起一些bug-->
<action name="user*" class="com.bwf.struts01.controllers.UserAction" method="{1}User">
<result name="success">/{1}.jsp</result>
<result name="error">/error.jsp</result>
</action>
{n}表示第几个通配符,n的范围是0~9,其中0表示完整的请求路径,如useradd。如果希望Action的name属性包含/
,需要配置常量<constant name="struts.enable.SlashesInActionNames" value="true"/>
,但是一般不建议这样做。
需要注意的:
- 使用通配符方法映射和使用!符号的“动态方法调用”可能会重叠,需要禁用动态调用
- .对于Struts2.3之前的版本可以不配置<allowed-methods>,但是对于之后的版本需要配置<allowed-methods>,负责无法访问
动态方法调用
启用动态方法调用(Dynamic Method Invocation)需要配置常量,官方文档说,DMI存在安全性问题,因此默认是关闭的
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
动态调用url格式为:actionName!methodName
struts 2.3以后使用动态调用需要配置package属性strict-method-invocation="false"
,或者global-allowed-methods
和allowed-methods
指定允许访问的方法。
Action中访问Servlet API
(1)通过ActionContext的子类ServletActionContext访问
@Override
public String execute() throws Exception {
PageContext pageContext = ServletActionContext.getPageContext();
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
ServletContext servletContext = ServletActionContext.getServletContext();
HttpServletResponse response = ServletActionContext.getResponse();
return "success";
}
(二)实现ServletContextAware,ServletRequestAware,ServletResponseAware三个接口(是不是与Spring的Aware接口很相似),它是通过struts内建拦截器ServletConfig通过instanceof判断Action是否属于ServletContextAware,ServletRequestAware,ServletResponseAware类型,并且调用其setter方法
public class ServletAPIAction extends ActionSupport implements ServletContextAware,ServletRequestAware,ServletResponseAware {
//定义三个参数
private HttpServletRequest request;
private HttpServletResponse response;
private ServletContext context;
public String execute() throws Exception {
return null;
}
//实现接口中设置参数的方法
@Override
public void setServletResponse(HttpServletResponse response) {
this.response=response;
}
@Override
public void setServletRequest(HttpServletRequest request) {
this.request=request;
}
@Override
public void setServletContext(ServletContext context) {
this.context=context;
}
}
result处理
当Action处理完用户请求都会返回一个字符串,该字符串通过<result…>进行配置,根据其结果类型做出不同的处理。
struts内建结果类型
struts的内建的结果类型配置在struts-default.xml的<result-types>中
-chain:Action链式处理。当一个Action处理完成之后,系统并不想转发到视图资源,而是希望下一个Action进行处理,此时就需要这个类型。
-dispatcher:请求转发到JSP/HTML等视图,默认结果类型
-redirect:重定向到视图
-redirectAction:重定向到其他Action
-stream:向浏览器返回一个InputStream的结果类型(一般用于文件下载)
-freemarker:返回freemarker视图
-velocity:返回velocity视图
-xslt:返回xml/xslt视图
-plainText:用于显示某个页面原始代码
-httpheader:返回http请求头信息
-postback:将请求参数以form的形式提交到指定地点
对于不同包之间的请求转发或重定向,我们可以注入actionName和namespace属性
<package name="test" extends="struts-default">
<action name="demo1" class="com.cad.struts2.Hello" >
<result type="chain">
<param name="actionName">demo2</param>
<param name="namespace">/user</param>
</result>
</action>
</package>
<package name="test1" extends="struts-default" namespace="/user">
<action name="demo2" >
<result name="success" >/success.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
自定义结果类型
(1)继承StrutsResultSupport类
public class ImageResult extends StrutsResultSupport {
@Override
protected void doExecute(String s, ActionInvocation actionInvocation) throws Exception {
FileInputStream fis = new FileInputStream("E:\\java\\javasrc\\javaweb\\struts01\\WebContent\\WEB-INF\\classes\\WEB-INF\\files\\channel.png");
ServletOutputStream out = ServletActionContext.getResponse().getOutputStream();
int len = -1;
while ((len = fis.read())!=-1) {
out.print((char)len);
out.flush();
}
out.close();
fis.close();
}
}
(2)编写Action
public class ImageAction extends ActionSupport {
}
(3)注册自定义结果类型和Action
<package name="result" namespace="/result" extends="struts01">
<result-types>
<result-type name="image" class="com.bwf.struts01.result.ImageResult"></result-type>
</result-types>
<action name="image" class="com.bwf.struts01.controllers.ImageAction">
<result name="success" type="image"></result>
</action>
</package>
定义全局的result和结果类型
由于在struts01包中定义的result-type只能在struts01包及其子包中使用,因此result包继承struts01包即可。global-results配置全局的result。
<package name="struts01" namespace="/" extends="struts-default">
<result-types>
<result-type name="image" class="com.bwf.struts01.result.ImageResult"></result-type>
</result-types>
<global-results>
<result name="success" type="image">
<param name="width">100</param>
<param name="height">100</param>
</result>
</global-results>
</package>