struts2基础

一、Action
二、result
三、OGNL(用来从ValueStack和ValueContext中取值)
四、Struts-Tags
五、声明式异常处理(通过拦截器来实现的)
六、拦截器原理
七、类型转换
View Code

一、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即可,其它的用到的时候查.

转载于:https://www.cnblogs.com/wangweiNB/p/5102758.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值