![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
一、Action 二、result 三、OGNL(用来从ValueStack和ValueContext中取值) 四、Struts-Tags 五、声明式异常处理(通过拦截器来实现的) 六、拦截器原理 七、类型转换
一、Action
helloWorld
1、下载struts-2.3.24.1解压,打开apps目录下的struts2-blank.war文件,拷贝lib目录下的jar包到项目.
2、拷贝web.xml到项目WEB-INF目录到:
<?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>
</web-app>
3、拷贝struts.xml文件到src目录下:
<?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>
<package name="default" namespace="/" extends="struts-default">
<action name="hell">
<result>/hello.jsp</result>
</action>
</package>
</struts>
4、hello.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>HelloStruts2</title>
</head>
<body>
Hello Struts2<br>
</body>
</html>
5、web.xml中添加如下常量配置代码,开始开发模式,struts.xml和action修改时无需重启服务器,web.xml修改时仍然需要重启服务器.
<constant name="struts.devMode" value="true" />
ps:所有常量配置默认值在struts2-core.jar包org.apache.struts2包下default.properties文件中.
6、源码的粘贴,javadoc的粘贴,xml dtd文件粘贴,略.
7、从浏览器:localhost:8080/TestStruts/hell到看到Hello Struts2分析整个流程:
1>浏览器根据localhost:8080基于TCP连接,连接到web服务器
2>发送http请求到web服务器
3>web服务器从客户端发送的HTTP请求中依次解析出客户端要访问的主机、web应用、web资源
4>根据web.xml文件发现有Filter配置,执行StrutsPrepareAndExecuteFilter的doFilter方法.
5>doFilter方法执行一些预处理后会根据struts.xml,结合namespace找到要访问的action,执行action中的目标方法后,在根据当前action的result配置转发或重定向到jsp页面.
7>jsp(jsp会被编译为servlet)中的service执行会向response对象写入向客户端输出的数据
8>服务器从response中取出数据,构建一个HTTP响应,返回给客户端
9>浏览器解析HTTP响应,提取数据显示
ps:结合之前servlet的调用过程,把action当成servlet,其实过程基本一致,servlet是直接在web.xml中配置要访问的servlet,而struts2是采用拦截器机制拦截请求后,根据struts.xml配置文件访问action,结果页面的配置也放到了struts.xml中.
namespace
1、web.xml中package的作用仅仅是给action打包,防止重名,类似文件夹的作用.
2、namespace决定了action的访问路径,默认为"",接收所有路径的action,也就是说访问一个action时,先从对应namespace的package中找,找不到的话就会从namespace为空的package中找.
3、namespace可以写为/,或者/xxx,或者/xxx/yyy,对应的action访问路径为/index.action,/xxx/index.action,或者/xxx/yyy/index.action.
4、namespace最好用模块来进行命名.
action写法
1、之前我们没有在struts.xml中配置class,则默认执行的是ActionSupport中的execute方法,仅仅返回一个SUCCESS而已,如果要自定义action就需要在action中配置class.如下所示是class的配置和action的三种写法:
<package name="front" extends="struts-default" namespace="/">
<action name="index" class="com.bjsxt.struts2.front.action.IndexAction1">
<result>/ActionIntroduction.jsp</result>//jsp默认编码的设置window->preferences->jsp
</action>
</package>
public class IndexAction1 {
public String execute() {
return "success";
}
}
public class IndexAction2 implements Action {
@Override
public String execute() {
return "success";
}
}
public class IndexAction3 extends ActionSupport {
@Override
public String execute() {
return "success";
}
}
2、具体视图的返回由用户自己定义的Action来决定、服务器会根据返回的字符串找到对应的配置项,来决定视图的内容,result不写name默认为SUCCESS.
3、具体Action的实现可以是一个普通的java类,里面有public String execute方法即可,或者实现Action接口,不过最常用的是从ActionSupport继承,好处在于可以直接使用Struts2封装好的方法.
ps:action在每次请求时会new一个,这是与servlet和struts1巨大的区别,这样不会有线程同步问题.
path
1、直接访问web项目根目录时,会访问web.xml中配置的welcome-file页面.
2、struts2中的路径问题是根据action的路径而不是jsp路径来确定,所以尽量不要使用相对路径.如下例:
<package name="path" extends="struts-default" namespace="/path">
<action name="path" class="com.bjsxt.struts2.path.action.PathAction">
<result name="path">/path.jsp</result>(/代表web项目根目录)
</action>
</package>
path.jsp页面中有如下一行(在web项目根目录下有如下两个文件path.jsp和index.jsp):
<a href="index.jsp">index.jsp</a>
结果:
1>按理来说<a>是一个相对路径是可以访问的,但是点击这个<a>会跳到..../path/index.jsp,也就是说他是相对url的路径的而不是相对文件位置的路径.
2><a>标签href改为如下可以吗?
<a href="/index.jsp">index.jsp</a>
ps:不行的,不要忘记之前方立勋将的此处/代表的是整个站点根路径.
解决方案:全部使用绝对路径,利用el表达式
<a href="${pageContext.request.contextPath }/index.jsp">点点</a>
ActionMethod&DMI
我们之前一直没有指点Action的method这样的话默认执行的是Action的execute方法,指点method后会执行指定的方法,配置如下:
<a href="${pageContext.request.contextPath }/user/userAdd">添加用户</a>
<action name="userAdd" class="com.bjsxt.struts2.user.action.UserAction" method="add">
<result>/user_add_success.jsp</result>
</action>
这样有个问题就是每执行一个方法都要配置一个action较繁琐,可以使用动态方法调用DMI,如下:
<a href="${pageContext.request.contextPath }/user/user!add">添加用户</a>
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result>/user_add_success.jsp</result>
</action>
ActionWildcard
使用通配符,将配置量降到最低,不过,一定要遵守"约定优于配置"的原则
<a href="<%=context %>/actions/Studentadd">添加学生</a>
<a href="<%=context %>/actions/Studentdelete">删除学生</a>
<a href="<%=context %>/actions/Teacher_add">添加老师</a>
<a href="<%=context %>/actions/Teacher_delete">删除老师</a>
<a href="<%=context %>/actions/Course_add">添加课程</a>
<a href="<%=context %>/actions/Course_delete">删除课程</a>
<package name="actions" extends="struts-default" namespace="/actions">
<action name="Student*" class="com.bjsxt.struts2.action.StudentAction" method="{1}">
<result>/Student{1}_success.jsp</result>
</action>
<action name="*_*" class="com.bjsxt.struts2.action.{1}Action" method="{2}">
<result>/{1}_{2}_success.jsp</result>
<!-- {0}_success.jsp -->
</action>
</package>
ps:如果既匹配Student*又匹配Studentadd,那么则匹配Studentadd,首先匹配最精确的,如果匹配多个星号,则按action的前后顺序.
使用action属性接收参数
<a href="user/user!add?name=a&age=8">添加用户</a>
<package name="user" extends="struts-default" namespace="/user">
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result>/user_add_success.jsp</result>
</action>
</package>
public class UserAction extends ActionSupport {
private String name;
private int age;
public String add() {
System.out.println("name=" + name);
System.out.println("age=" + age);
return SUCCESS;
}
}
ps:客户端访问new一个新的UserAction时,会将参数自动传递给属性(需要提供get,set方法,为简化代码,所有实例中没有写set,get方法),是根据set方法赋值的而不是属性名称,并且会进行参数类型的自动转换如name是String,age是int.
用DomainModel接收参数
<a href="user/user!add?user.name=a&user.age=8">添加用户</a>
<package name="user" extends="struts-default" namespace="/user">
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result>/user_add_success.jsp</result>
</action>
</package>
public class UserAction extends ActionSupport {
private User user;//不需要new
//private UserDTO ;
public String add() {
System.out.println("name=" + user.getName());
System.out.println("age=" + user.getAge());
return SUCCESS;
}
}
ps:实际开发中很多时候传递的数据不能和domainModel匹配,比如注册的时候会传递确认密码到后台,而我们的domainModel中没有这个属性,这时候就要使用DTO(或叫VO或叫DO)来接收请求参数再传递给domainModel,如果匹配的话则不需要DTO.
用ModelDriven接收参数(实际开发用的不多)
<a href="user/user!add?name=a&age=8">添加用户</a>
<package name="user" extends="struts-default" namespace="/user">
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result>/user_add_success.jsp</result>
</action>
</package>
public class UserAction extends ActionSupport implements ModelDriven<User>{
private User user = new User();
public String add() {
System.out.println("name=" + user.getName());
System.out.println("age=" + user.getAge());
return SUCCESS;
}
@Override
public User getModel() {
return user;
}
}
ps:如果name输入中文的话会有中文问题,配置如下常量即可,在web.xml中struts2过滤器前配置一个编码过滤器也可,但是很明显下面的常量配置简单.
<constant name="struts.i18n.encoding" value="GBK" />
简单处理数据校验
<a href="user/user!add?name=a" >添加用户</a>
public class UserAction extends ActionSupport {
private String name;
public String add() {
if(name == null || !name.equals("admin")) {
this.addFieldError("name", "name is error");
this.addFieldError("name", "name is too long");
return ERROR;
}
return SUCCESS;
}
}
<!-- user_add_error.jsp -->
<body>
User Add Error!
<s:fielderror fieldName="name" theme="simple"/>
<br />
<s:property value="errors.name[0]"/>
<s:debug></s:debug>
</body>
</html>
浏览器显示结果发现<s:fielderror>标签取出的错误信息会强加效果li,ul什么的,处理起来比较麻烦,所以我们想只取到错误信息的字符串,<s:property>即可,<s:debug>可打印值栈信息(一次request只有一次ValueStack):
ps:各种的错误以及各种消息的属性用相应struts标签取时都会强加格式,所以都采用<s:property>标签来取出存的字符串,value是ognl表达式的写法,errors是一个map,采用点,里面的name是一个数组(同一个name可以加多个错误),采用[],注:各种错误只是名称不同为了见名知意,其实原理相同.
AccessWebElements
访问web元素就是在action中访问servlet中的HttpServletRequest, HttpSession, ServletContext对应于jsp中就是request,session,application.如下是四种方式:
public class LoginAction1 extends ActionSupport {
private Map request;
private Map session;
private Map application;
public LoginAction1() {
request = (Map)ActionContext.getContext().get("request");
session = ActionContext.getContext().getSession();
application = ActionContext.getContext().getApplication();
}
public String execute() {
request.put("r1", "r1");
session.put("s1", "s1");
application.put("a1", "a1");
return SUCCESS;
}
}
//DI dependency injection(IoC inverse of control)
public class LoginAction extends ActionSupport implements RequestAware,SessionAware, ApplicationAware{
private Map<String, Object> request;
private Map<String, Object> session;
private Map<String, Object> application;
public String execute() {
request.put("r1", "r1");
session.put("s1", "s1");
application.put("a1", "a1");
return SUCCESS;
}
@Override
public void setRequest(Map<String, Object> request) {
this.request = request;
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
@Override
public void setApplication(Map<String, Object> application) {
this.application = application;
}
}
public class LoginAction3 extends ActionSupport {
private HttpServletRequest request;
private HttpSession session;
private ServletContext application;
public LoginAction3() {
request = ServletActionContext.getRequest();
session = request.getSession();
application = session.getServletContext();
}
public String execute() {
request.setAttribute("r1", "r1");
session.setAttribute("s1", "s1");
application.setAttribute("a1", "a1");
return SUCCESS;
}
}
public class LoginAction4 extends ActionSupport implements ServletRequestAware {
private HttpServletRequest request;
private HttpSession session;
private ServletContext application;
public String execute() {
request.setAttribute("r1", "r1");
session.setAttribute("s1", "s1");
application.setAttribute("a1", "a1");
return SUCCESS;
}
@Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
this.session = request.getSession();
this.application = session.getServletContext();
}
}
<s:property value="#request.r1"/> | <%=request.getAttribute("r1") %> <br />
<s:property value="#session.s1"/> | <%=session.getAttribute("s1") %> <br />
<s:property value="#application.a1"/> | <%=application.getAttribute("a1") %> <br />
<s:property value="#attr.a1"/><br />
<s:property value="#attr.s1"/><br />
<s:property value="#attr.r1"/><br />
ps:
1>第一种方式中ActionContext是ThreadLocal,每次请求(也就是每个线程)会创建一个.第二种方式依赖注入(底层使用反射很容易实现),这种方式用的最多,如果要使用HttpServletRequest中特有的方法采用第三种和第四种方式.
2><s:debug>看到的另一部分内容stack context如下:
#attr依次从各个域中查找key,用的不多,因为我们应当明确知道把值存在了哪个域中.
3>其实平常request很少拿,因为我们action中的属性会自动放到ValueStack中,不过其实valuestack就是把属性放到了request中,一般拿HttpSession来用,比如注册的时候.
模块包含
当配置很多时,还是很有意义
<struts>
<constant name="struts.devMode" value="true" />
<include file="login.xml" />
</struts>
默认action
<package name="default" namespace="/" extends="struts-default">
<default-action-ref name="index"></default-action-ref>
<action name="index">
<result>/default.jsp</result>
</action>
</package>
ps:当访问的action不存在时会访问默认action,这点在设置错误页面时有意义,而不是直接返回404.
二、result
result_type
<!-- Result类型,很多不常用的了解即可 -->
<ol>
<li><a href="r/r1">dispatcher</a></li>
<li><a href="r/r2">redirect</a></li>
<li><a href="r/r3">chain</a></li>
<li><a href="r/r4">redirectAction</a></li>
<li>freemarker</li>
<li>httpheader</li>
<li>stream</li>
<li>velocity</li>
<li>xslt</li>
<li>plaintext</li>
<li>tiles</li>
</ol>
<package name="resultTypes" namespace="/r" extends="struts-default">
<!--服务器端forward,只能跳转到视图,url不变,默认跳转方式 -->
<action name="r1">
<result type="dispatcher">/r1.jsp</result>
</action>
<!--客户端重定向,也只能是视图,url改变 -->
<action name="r2">
<result type="redirect">/r2.jsp</result>
</action>
<!--服务器端forward到另个action,也可以到另外的action(param查文档) -->
<action name="r3">
<result type="chain">r1</result>
</action>
<!--客户端重定向到另一个action -->
<action name="r4">
<result type="redirectAction">r2</result>
</action>
</package>
ps:当用到不常用的类型时查文档,切记查文档的重要性.
global-results&extends
<ol>
<li><a href="user/user?type=1">返回success</a></li>
<li><a href="user/user?type=2">返回error</a></li>
<li><a href="user/user?type=3">返回global result</a></li>
<li><a href="admin/admin">admin,继承user包</a></li>
</ol>
<package name="user" namespace="/user" extends="struts-default">
<global-results>
<result name="mainpage">/main.jsp</result>
</global-results>
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result>/user_success.jsp</result>
<result name="error">/user_error.jsp</result>
</action>
</package>
<package name="admin" namespace="/admin" extends="user">
<action name="admin" class="com.bjsxt.struts2.user.action.AdminAction">
<result>/admin.jsp</result>
</action>
</package>
public class UserAction extends ActionSupport {
private int type;
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
@Override
public String execute() throws Exception {
if(type == 1) return "success";
else if (type == 2) return "error";
else return "mainpage";
}
}
全局结果页面用在结果页面是同一个页面时简化配置.如果要用别的package中的global-results,可以通过extends继承别的package,extends默认配置是struts-default,这个包对应struts-default.xml文件中的配置(定义了各种bean,结果集,拦截器(和Filter原理一样)),这个文件用户不要修改,就是用来被继承的.
dynamic_result
<ol>
<li><a href="user/user?type=1">返回success</a></li>
<li><a href="user/user?type=2">返回error</a></li>
</ol>
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result>${r}</result>
</action>
public class UserAction extends ActionSupport {
private int type;
private String r;//提供set get
public String getR() {
return r;
}
public void setR(String r) {
this.r = r;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
@Override
public String execute() throws Exception {
if(type == 1) r="/user_success.jsp";
else if (type == 2) r="/user_error.jsp";
return "success";
}
}
${r}用于在配置文件中读取value statck中的值.
ResultWithParams
<a href="user/user?type=1">向结果中传参数</a>
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result type="redirect">/user_success.jsp?t=${type}</result>
</action>
public class UserAction extends ActionSupport {
private int type;
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
@Override
public String execute() throws Exception {
return "success";
}
}
<body>
User Success!
from valuestack: <s:property value="t"/><br/> //取不到值
from actioncontext: <s:property value="#parameters.t"/>//可以取到值
<s:debug></s:debug>
</body>
ps:
1>只有客户端跳转时,才需要传值,服务器端不需要,因为是一次请求,共用一个值栈.
2><s:property value="t"/>是用来从值栈中取值的,上例中from valuestack取不到值是因为直接redirect到jsp页面是没有值栈的,只能从valueConetxt中取值.
3>书用来查,不要抠.
三、OGNL(用来从ValueStack和ValueContext中取值)
<ol> <li>访问值栈中的action的普通属性: username = <s:property value="username"/> </li> <li>访问值栈中对象的普通属性(get set方法):<s:property value="user.age"/> | <s:property value="user['age']"/> | <s:property value="user[\"age\"]"/>
<li>访问值栈中对象的普通属性(get set方法): <s:property value="cat.friend.name"/></li> <li>访问值栈中对象的普通方法:<s:property value="password.length()"/></li> <li>访问值栈中对象的普通方法:<s:property value="cat.miaomiao()" /></li> <li>访问值栈中action的普通方法:<s:property value="m()" /></li> <li>访问静态方法:<s:property value="@com.bjsxt.struts2.ognl.S@s()"/></li> <li>访问静态属性:<s:property value="@com.bjsxt.struts2.ognl.S@STR"/></li> <li>访问Math类的静态方法:<s:property value="@@max(2,3)" /></li> <li>访问普通类的构造方法:<s:property value="new com.bjsxt.struts2.ognl.User(8)"/></li> <hr /> <li>访问List:<s:property value="users"/></li> <li>访问List中某个元素:<s:property value="users[1]"/></li> <li>访问List中元素某个属性的集合:<s:property value="users.{age}"/></li> <li>访问List中元素某个属性的集合中的特定值:<s:property value="users.{age}[0]"/> | <s:property value="users[0].age"/></li> <li>访问Set:<s:property value="dogs"/></li> <li>访问Set中某个元素:<s:property value="dogs[1]"/></li> <li>访问Map:<s:property value="dogMap"/></li> <li>访问Map中某个元素:<s:property value="dogMap.dog101"/> | <s:property value="dogMap['dog101']"/> | <s:property value="dogMap[\"dog101\"]"/></li> <li>访问Map中所有的key:<s:property value="dogMap.keys"/></li> <li>访问Map中所有的value:<s:property value="dogMap.values"/></li> <li>访问容器的大小:<s:property value="dogMap.size()"/> | <s:property value="users.size"/> </li> <hr /> <li>投影(过滤):<s:property value="users.{?#this.age==1}[0]"/></li> <li>投影:<s:property value="users.{^#this.age>1}.{age}"/></li> <li>投影:<s:property value="users.{$#this.age>1}.{age}"/></li> <li>投影:<s:property value="users.{$#this.age>1}.{age} == null"/></li> <hr /> <li>[]:<s:property value="[0].username"/></li>//访问栈顶action中的username </ol>
ps:
1>ognl如果取不到值就不显示,而不是报错.
2>list,set,数组取数据的方式一模一样.
3>ValueStack中action永远在栈顶,如果服务器端跳转的时候会压入多个action.
四、Struts-Tags
<!--<s:property>从值栈和ActionContext中取值 -->
<li>property: <s:property value="username"/> </li><!-- 默认按OGNL解析 -->
<li>property 取值为字符串: <s:property value="'username'"/> </li><!-- 不会解析username -->
<li>property 设定默认值: <s:property value="admin" default="管理员"/> </li>
<li>property 设定HTML: <s:property value="'<hr/>'" escape="false"/> </li>
<!--<s:set>用的不多,可以用在给变量换名时,避免每次输入长的名称 -->
<li>set 设定adminName值(默认为request和 ActionContext): <s:set var="adminName" value="username" /></li>
<li>set 从request取值: <s:property value="#request.adminName" /></li>
<li>set 从ActionContext取值: <s:property value="#adminName" /></li>
<li>set 设定var,范围为ActionContext: <s:set var="adminPassword" value="password" scope="session"/></li>
<li>set 使用#取值: <s:property value="#adminPassword"/> </li>
<li>set 从相应范围取值: <s:property value="#session.adminPassword"/> </li>
<!-- <s:bean>不知道有啥用 -->
<li>定义bean,并使用param来设定新的属性值:
<s:bean name="com.bjsxt.struts2.tags.Dog" >
<s:param name="name" value="'pp'"></s:param>
<s:property value="name"/>
</s:bean>
</li>
<li>
<s:bean name="com.bjsxt.struts2.tags.Dog" var="myDog">
<s:param name="name" value="'oudy'"></s:param>
</s:bean>
<s:property value="#myDog.name"/>
</li>
<!--上面两种方式,第一种定义完就没了,没有var所有没有保存,而第二种定义的bean放到了ActionContext中看到var,就表示把当前值以var为名存到某个域中-->
<!--<s:include>静态包含 -->
<li>include _include1.html 包含静态英文文件
<s:include value="/_include1.html"></s:include>
</li>
<!--这里是引出了%{}的用法,%{}中的内容会按OGNL解析,否则按普通字符串处理 -->
<li>
<s:set var="incPage" value="%{'/_include1.html'}" />
<s:include value="%{#incPage}"></s:include>
</li>
<!--<s:if><s:elseif><s:else> -->
<li>
<s:set var="age" value="#parameters.age[0]" />
<s:if test="#age < 0">wrong age!</s:if>
<s:elseif test="#parameters.age[0] < 20">too young!</s:elseif>
<s:else>yeah!</s:else><br />
<s:if test="#parameters.aaa == null">null</s:if>
</li>
<!--<s:iterator>遍历 -->
<s:iterator value="{'aaa', 'bbb', 'ccc'}" var="x">
<s:property value="#x.toUpperCase()"/> |
</s:iterator>
<s:iterator value="{'aaa', 'bbb', 'ccc'}" status="status">
<s:property/> |
遍历过的元素总数:<s:property value="#status.count"/> |
遍历过的元素索引:<s:property value="#status.index"/> |
当前是偶数?:<s:property value="#status.even"/> |
当前是奇数?:<s:property value="#status.odd"/> |
是第一个元素吗?:<s:property value="#status.first"/> |
是最后一个元素吗?:<s:property value="#status.last"/>
<br />
</s:iterator>
ps:$,#,%几个符号的作用
1、ui标签,略
2、$用于i18n和struts配置文件中读取值栈中的值
3、#取得ActionContext的值
4、%将原本的文本属性解析为ognl,对于本来就是ognl的属性不起作用
4、OGNL和Struts-Tags结合el和JSTL一起学,基本一样,剩下的查文档,足够.
五、声明式异常处理(通过拦截器来实现的)
//action中,可以直接抛出异常,发现ActionSupport的execute方法也是有异常抛出的,就是告诉我们可以不处理,直接抛给struts2帮我们处理.
public String list() throws Exception {
categories = categoryService.list();
return SUCCESS;
}
//categoryService
public List<Category> list() throws SQLException {
Connection conn = DB.createConn();
String sql = "select * from _category_";
PreparedStatement ps = DB.prepare(conn, sql);
List<Category> categories = new ArrayList<Category>();
try {
ResultSet rs = ps.executeQuery();
Category c = null;
while(rs.next()) {
c = new Category();
c.setId(rs.getInt("id"));
c.setName(rs.getString("name"));
c.setDescription(rs.getString("description"));
categories.add(c);
}
} catch (SQLException e) {
e.printStackTrace();
throw(e);
}
DB.close(ps);
DB.close(conn);
return categories;
}
<action name="*-*" class="com.bjsxt.bbs2009.action.{1}Action" method="{2}">
<result>/admin/{1}-{2}.jsp</result>
<result name="input">/admin/{1}-{2}.jsp</result>
<exception-mapping result="error" exception="java.sql.SQLException" />
<result name="error">/error.jsp</result>
</action>
可以对异常进行统一处理,这样就不需要在每个action中进行配置,需要继承这个package:
<package name="bbs2009_default" extends="struts-default">
<global-results>
<result name="error">/error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping result="error" exception="java.lang.Exception"></exception-mapping>
</global-exception-mappings>
</package>
六、拦截器原理
1、原理图
2、ActionInvocation原理模拟
//实际是ActionProxy中的代码
public class Main {
public static void main(String[] args) {
new ActionInvocation().invoke();
}
}
public class ActionInvocation {
List<Interceptor> interceptors = new ArrayList<Interceptor>();
int index = -1;
Action a = new Action();
public ActionInvocation() {
this.interceptors.add(new FirstInterceptor());
this.interceptors.add(new SecondInterceptor());
}
public void invoke() {
index ++;
if(index >= this.interceptors.size()) {
a.execute();
}else {
this.interceptors.get(index).intercept(this);
}
}
}
public interface Interceptor {
public void intercept(ActionInvocation invocation) ;
}
public class FirstInterceptor implements Interceptor {
public void intercept(ActionInvocation invocation) {
System.out.println(1);
invocation.invoke();
System.out.println(-1);
}
}
public class SecondInterceptor implements Interceptor {
public void intercept(ActionInvocation invocation) {
System.out.println(2);
invocation.invoke();
System.out.println(-2);
}
}
public class Action {
public void execute() {
System.out.println("execute!");
}
}
ps:老马说自定义拦截器99.9不使用(有疑问),filter和interceport其实是一回事,具体intercepor的用途可以参考Filter,基于安全的拦截器 spring securty.
3、自定义拦截器
<struts>
<constant name="struts.devMode" value="true"></constant>
<package name="test" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="my" class="com.bjsxt.interceptor.MyInterceptor"></interceptor>
</interceptors>
<action name="test" class="com.bjsxt.action.TestAction">
<result>/test.jsp</result>
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="my"></interceptor-ref>
</action>
</package>
</struts>
public class MyInterceptor implements Interceptor {
public String intercept(ActionInvocation invocation) throws Exception {
long start = System.currentTimeMillis();
String r = invocation.invoke();
long end = System.currentTimeMillis();
System.out.println("action time = " + (end - start));
return r;
}
public void destroy() {
}
public void init() {
}
}
public class TestAction extends ActionSupport {
@Override
public String execute() throws Exception {
return super.execute();
}
}
4、使用token拦截器控制表单重复提交
浏览器输入:localhost:8080/struts2_3600_token_interceptor/input1
<struts>
<constant name="struts.devMode" value="true"></constant>
<package name="test" namespace="/" extends="struts-default">
<action name="input1" class="com.bjsxt.action.InputAction">
<result>/input.jsp</result>
</action>
<action name="user" class="com.bjsxt.action.UserAction">
<result>/addOK.jsp</result>
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="token"></interceptor-ref>
<result name="invalid.token">/error.jsp</result>
</action>
</package>
</struts>
public class InputAction extends ActionSupport {
@Override
public String execute() throws Exception {
return super.execute();
}
}
<form action="user" method="post">
name:<input name="name">
age:<input name="age">
<input type="submit" value="add">
<s:token></s:token>
</form>
public class UserAction extends ActionSupport {
private String name;
private int age;
@Override
public String execute() throws Exception {
System.out.println("a user added!");
return super.execute();
}
}
七、类型转换
所谓的类型转换就是把客户端传递过来的参数(全部默认为字符串)转换为特定类型,比如List,Map,Date,还有一些没有提供set方法的Jdk的对象(用的很少).
1、对于date类型,参数按1988-08-08 12:22:22这种格式传递到后台,jsp中如下解析即可:
<s:date name="d" format="yyyy/MM/dd HH:mm:ss"/><br/>
2、对于checkbox这种向后台传递时一个名字对应多个值的情况,后台拿一个list,或set接受即可.
3、当比如说要往Point对象中的X,Y属性传值时,因为没有set,get方法就需要自定义类型转换器,略,用到的时候查.
ps: 上面没有讲到的内容:Lambda表达式、验证框架、UI标签、国际化、自定义类型转换、类型转换中的异常处理、上传与下载文件、Struts2注解、Struts2和framemark、velocity和结合、Struts2和ajax、json的结合,Struts2集合了大量的内容,但都很简单,知道那么一回事,用到的时候查即可,马哥说的话,Struts2的内容掌握百分之10即可,其它的用到的时候查.