关于学习struts2的总结与心得

在此处我将记录一些我学习struts2时总结的要点,分章节与学习进度保持同步,与君共勉。

注:视频学习资料来自北京尚学堂培训公司-马士兵所有。

开发环境:IDE为IDEA 2016.2.5,JDK1.8,struts版本为2.1.6,Tomcat为7.0.75

学习资料下载:点我下载(涵盖源码+视频)

=============================================================

如何使用struts2

1.将7个jar包导入到项目lib中去,IDEA需在WEB-INFO文件夹下新建文件夹lib,导入jar包,右键文件夹--add as librery--完成


2.编写web.xml配置文件,将以下代码插入至<web-app>标签中

    <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>

3.导入struts.xml配置文件至项目src根目录

4.创建JavaAction类进行操作

=====================================================================

命名空间

namespace决定了action的访问路径,也就是namespace里面写了什么,你的请求地址栏里就必须要包括什么。

namespace默认为“”,可以接收到所有路径的action,可以写为/,/xxx,/xxx/yyy 对应的访问路径就应该为/xxx/xxx.action,/xxx/yyy/xxx.action

namespace最好用模块来命名。

如下代码:

    <constant name="struts.devMode" value="true" />
    <package name="login" namespace="/login" extends="struts-default">
        <action name="login">
            <result>/login_success.jsp</result>
        </action>
    </package>

constant name="struts.devMode" value="true" 为调试模式开启(IDEA无法调用此功能,调试须重新部署)

package *name*属性用来区分重名的情况。


=====================================================================

Action

struts配置文件内的Action标签为你的Java操作的导航(个人理解)与返回值的判定和页面的跳转的集合体

struts.xml内代码如下:

    <package name="path" namespace="/path" extends="struts-default">
        <action name="path" class="test.example">
            <result name="success">
                /path.jsp
            </result>
        </action>
    </package>
action标签里的class值为你在src目录创建的JavaAction类的路径

Action操作类我用继承了一个ActionSupport类,类里包含了一些预设好了的对象和属性供开发者利用,后续struts2的Action类也都将继承此类。

在此处我重载了ActionSupport里的execute()方法,返回了一个值为“success”的String对象。

execute()为默认执行的方法,即在上面的配置文件里的action标签内,你不声明method属性时默认执行的方法。

example.java代码如下:

package test;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/8.
 */
public class example extends ActionSupport{

    @Override
    public String execute(){
        return "success";//result默认值为success
    }
}
这个action类返回一个“success”的String至struts.xml,里面的result标签负责接收这个值,其中name属性为结果值,默认为"success" 

也就是说 若返回值为success,那么result标签内就不用再写name属性。

另外一种action类的的写法,此方法为主流方法,可以自定义方法名来进行开发操作,而不仅仅局限与execute()这一种方法。

另外返回值我们利用了SUCCESS这一在父类ActionSupport中接口Action中的变量值,

可看到struts2的源码中为我们内置了这些变量值:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.opensymphony.xwork2;

public interface Action {
    String SUCCESS = "success";
    String NONE = "none";
    String ERROR = "error";
    String INPUT = "input";
    String LOGIN = "login";

    String execute() throws Exception;
}

Action代码如下:

package action;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/9.
 */
public class CourseAction extends ActionSupport {
    public String add(){
        return SUCCESS; //接口包含变量
    }
    public String delete(){
        return SUCCESS;
    }
}
可以看到此处我们不在重载execute方法,而是选择自定义命名的方法,那么在struts.xml里面我们应该如何接受返回值? 请看

    <package name="actions" namespace="/actions" extends="struts-default">
        <action name="course" class="action.CourseAction" method="add">
            <result>
                /User_add_success.jsp
            </result>
        </action>
        <action name="course" class="action.CourseAction" method="delete">
            <result>
                /User_delete_success.jsp
            </result>
        </action>
    </package>

可以看到,在此处action标签内我们编写了一个method属性,里面的值就是我们action类里自定义方法的名称

这样一来,我们就可以通过这种方法来调用自定义的方法了。


=====================================================================

通配符

上面的代码大家有没有觉得很冗余,其实我们可以精简一点通配符的运用:

struts.xml

<package name="actions" namespace="/actions" extends="struts-default">
        <action name="*_*" class="action.{1}Action" method="{2}" >
            <result>
                /{1}_{2}_success.jsp
            </result>
        </action>
</package>
可以看到action的name值变为了*_*, 这些*就代表了我们要传(要变)的参数,可以为add,可以为delete等等,后面的{1}{2}分别代表了第一个*所代表的值与第二个*所代表的值,依次代入,就可以实现动态接受与判断数据了。

配置代码是不是精简了很多?但是需要遵循约定优于配置这个原则

对应action类与jsp页面



=====================================================================

 用Action的属性接收参数

在JSP页面内,我们如果需要传参数至后端,该如何进行?
如:超链接可以这一样写 http://localhost:8080/xxxxx/user/user!add?name=xxx&age=xxx
其中方法名写在!后面,需要传的参数则写在?后面与平常无异。
在Action类中,我们需要声明变量用来接收参数, 变量名最好与参数名一致(也可不一致,get set方法名与参数名一致即可,但不提倡)
并编写get() set() 方法。
UserAction.java写法:
package action;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/9.
 */
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;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

可以看到这种方式可以接收到页面传递过来的参数值,并进行处理。 但是这种传参模式有缺点,就是如果变量过多,会导致代码异常冗余

针对这点,下面这种方法就对其进行了优化。

用DomainModel接受参数


首先我们需要编写一个实体类,包含我们需要用到的数据,如:
package bean;

/**
 * Created by Alex on 2017/5/9.
 */
public class User {
    private String name;
    private int age;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

我们在页面内进行传值的地址链接更改为如下这种:
http://localhost:8080/user/user!add?user.name=xxx&user.age=xxx
将传的参数前加上了我们实体类的名字user,然后.参数名

我们再来看看Action类是如何编写的:
package action;

import bean.User;
import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/9.
 */
public class UserAction2 extends ActionSupport{
    private User user; //实例化User实体类
    public String add(){
        System.out.println("username:"+user.getName());
        System.out.println("username:"+user.getAge());
        return SUCCESS;
    }
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
我们可以理解为,参数传过来,调用了 set方法,我们使用 get方法取值。
在这种方式下,我们使用实体类加载对象,然后在Action里实例化,再直接调用实体类的get方法,就可取到页面传过来的参数了。
这种方式我们称之为 DomainModel 预模型

DTO(VO\DO)

如果我们需要传的参数并不是所有都需要用到,那这些参数我们应该如何处理呢?
Data Transform Object(DTO)数据传输对象 就可以解决此问题

用ModelDriven接收参数

传参地址仍然为:
http://localhost:8080/test/user/user!add?name=xxx&age=xxx

他的Action 是这样写的
package action;

import bean.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

/**
 * Created by Alex on 2017/5/11.
 */
public class UserAction3 extends ActionSupport  implements ModelDriven<User>{
    private User user = new User();

    public String add(){
        System.out.println("name"+user.getName());
        System.out.println("name"+user.getAge());
        return SUCCESS;
    }
    @Override
    public  User getModel(){
        return user;
    }
}


可以看到,在Action里我们使用了一个叫ModelDriven的接口。这样一来在Action内我们可以重载他的getModel方法,一旦成功
就可以 直接使用set方法来传参数至实体类,进而可用get方法来获取参数进行操作。

这种方法实际使用并不多,学习它是因为它包含了一个很重要的设计概念,即MVC模型思想
struts2代表了MVC中的Controller部分

以上三种传参方式,最为常用的为DomainModel这种方式,其余两种了解概念即可

2.1.6版本的中文问题


在传参过程中,我们会发现一个问题,如果我们输入中文,那么传过来的参数会是乱码,这是因为项目本身编码不支持中文,我们需要更改部分配置来进行转码。
我们可以在struts配置文件插入以下配置,可完成中文转码(仅支持struts版本为2.1.7以上,以下的会有BUG)
<constand name=“struts.i18n.encoding" value="utf-8" />
若版本在2.1.7以下,则可用过滤器的方式来进行转码

post请求转码过滤器写法:
package filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * Created by Alex on 2017/5/11.
 */
@WebFilter(filterName = "EncodingFilter")
public class EncodingFilter implements Filter {
    public void destroy() {
    }//过滤器销毁

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }//过滤器初始化

}


get请求转码需要更改Tomcat根目录下server.xml中的 具体位置:tomcat根目录/conf/server.xml
找到
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"  redirectPort="8443" />
更改为:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"  URLEncoding="utf-8" />
即可修复中文输入乱码问题

简单的数据验证


我们传入的参数需要进行简单的数据验证。接下来我将利用上面的例子进行验证。
首先我们来配置struts.xml文件
    <package name="actions" namespace="/actions" extends="struts-default">
        <action name="user2" class="action.UserAction2">
            <result> /User_add_success.jsp </result>
            <result name="error">/user_add_error.jsp</result>
        </action>
    </package>
此处有两个result代表了两种处理情况,成功与失败
Action类:
package action;


import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/11.
 */
public class LoginAction  extends ActionSupport {
    private String name;

    public String add(){
       if(name==null || !name.equals("fjnmbb12")){
            this.addFieldError("name","name is error");
            return ERROR;
        }
        return SUCCESS;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
可以看到这里add()内有两种情况,返回了不同的结果。其中发生错误的结果内有一个addFieldError方法,此方法将会发送错误的原因至前端页面。
请看前端页面写法:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/11
  Time: 22:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
    <title>Title</title>
</head>
<body>
    用户添加异常!
    <s:fielderror fieldName="name" theme="simple" /> <br/>
    <s:property value="errors.name[0]"/> <br/>
    <s:debug></s:debug>
</body>
</html>
在前端页面我们引用了struts 的标签,首先在顶部插入引用语,
在内容中即可调用struts标签。
<s:fielderror>标签是输出错误原因,但是有标准格式(不可改)
<s:property>标签是可输出多个错误原因,以Map的方式输出(第一个下标为0),并可自定义格式,截取长度等 ,比较常用。
<s:debug>是嵌入一个超链接 点击可打开struts的调试内容

访问Web元素

struts取request,response(一般不用),session,application的方式
我们先编写前端JSP页面的HTML代码,如下所示:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/10
  Time: 16:43
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <base href="http://localhost:8080/">
    <title>$Title$</title>
  </head>
  <body>
  <form action="" id="f" name="f" method="post">
    <input type="text" name="name" placeholder="输入用户名" /><br />
    <input type="password" name="password" placeholder="输入密码" /> <br />
    <input type="button" value="提交1" οnclick="javascript:document.f.action='login/login1';document.f.submit();"  />
    <input type="button" value="提交2" οnclick="javascript:document.f.action='login/login2';document.f.submit();"  />
    <input type="button" value="提交3" οnclick="javascript:document.f.action='login/login3';document.f.submit();"  />
    <input type="button" value="提交4" οnclick="javascript:document.f.action='login/login4';document.f.submit();"  />
  </form>
  </body>
</html>
效果如下图:
struts配置文件代码:
    <package name="login" namespace="/login" extends="struts-default">
        <action name="login*" class="action.LoginAction{1}">
            <result>/login_success.jsp</result>
        </action>
    </package>


第一种访问的方式,为调用ActionSupport内的ActionContext.getContext()方法进行取值,然后重载execute()方法传值。
三大对象在本质上还是Map。
Action1代码:
package action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

import javax.net.ssl.SSLSessionContext;
import java.util.Map;

/**
 * Created by Alex on 2017/5/10.
 */
public class LoginAction1 extends ActionSupport{
    private Map request;//声明类型为Map的request变量
    private Map session;//声明类型为Map的session变量
    private Map application;//声明类型为Map的application变量

    public LoginAction1(){
        //调用ActionSupport内的方法对三大对象进行取值
        request = (Map)ActionContext.getContext().get("request");
        session=ActionContext.getContext().getSession();
        application = ActionContext.getContext().getApplication();
    }
    @Override
    public String execute(){
        request.put("r1","r1");
        session.put("s1","s1");
        application.put("a1","a1");
        return SUCCESS;
    }
}

context:环境,周围环境

第二种访问取值的方式,依赖于struts2, 实现了几个叫做RequestAware,SessionAware,ApplicationAware的接口,不需要再从context中取值。

此处涉及到一个非常重要的设计思想(Spring框架会提到)叫做 IoC,全称Inverse of Control( 控制反转),也成为 DI,全称Dependency Injection( 依赖注入


实现了这三个接口后,请求可直接调用set方法访问web元素,非常方便。

Action2代码:
package action;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;

import java.util.Map;

/**
 * Created by Alex on 2017/5/10.
 */
public class LoginAction2 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","r2");
        session.put("s1","s2");
        application.put("a1","a2");
        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;
    }

}

第一种方式是主动去访问主动去拿的,第二种方式是被动询问取值,若实现了接口,则可直接访问与取值。这就是依赖注入的思想。后者是最常用的方法。

其余2种过于简单,不再此论述。

Action总结

Action:
  • 实现一个Action的最常用的方式:从ActionSupport继承
  • DMI动态方法调用 !xxx
  • 通配符配置 * {1} {2}
  1. *_*
  • 接收参数的方法(一般用属性和DomainModel来接收)
  • 简单的数据验证 addFieldError
  1. 一般不使用struts2的ui标签
  • 访问WEB元素
  1. Map类型(1、IoC  2、依赖struts2)
  2. 原始类型 (1、IoC 2、依赖struts2)
  • 包含文件配置(include标签)
  • 默认action处理

结果类型

result的type类型:
  1. dispatcher
  2. redirect
  3. chain
  4. redirectAction
上面四种result类型是我们常用的,下面我将配置代码编写如下:
struts.xml:
<package name="resultTypes" namespace="/r" extends="struts-default">
        <action name="r1">
            <result type="dispatcher"> /r1.jsp </result>
        </action>

        <action name="r2">
            <result type="redirect">/r2.jsp</result>
        </action>

        <action name="r3">
            <result type="chain">r1</result>
        </action>

        <action name="r4">
            <result type="redirectAction">r2</result> 
        </action>
</package>
我写了4个Action,每一个Action都对应了一个结果类型
第一个Action是默认的跳转结果类型,会直接在请求 服务器再跳转,其访问地址不变,为
http://localhost:8080/r/r1
第二个Action是 客户端之间的跳转,其地址将变为:
http://localhost:8080/r2.jsp
第三个Action的结果类型访问的是r1,而r1是跳转至r1.jsp。所以其地址为:
http://localhost:8080/r/r1
第四个Action的结果类型访问的是r2,而r2是跳转至r2.jsp,其结果地址为:
http://localhost:8080/r2.jsp

注意:使用chain时,若是需要使用另外一个包的action,那么就需要在result标签内写上param标签
如:
<action name="r3">
            <result type="chain">
                <param name="actionName">actionName</param>
                <param name="/namespace">namespace</param>
            </result>
</action>

全局结果集

定义一个可以共用的结果集,配置如下:
 <package name="user" namespace="/user" extends="struts-default">

        <global-results>
            <result name="mainpage">/main.jsp</result>
        </global-results>

        <action name="user" class="action.userAction">
            <result> /user_success.jsp </result>
            <result name="error">/user_error.jsp</result>
        </action>
 </package>
Action代码:
package action;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.inject.Scope;

/**
 * Created by Alex on 2017/5/15.
 */
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";
    }
}
那么只要是返回值为mainpage时,都会跳转至:main.jsp页面
如果其他的包要用这个全局结果集,那么就需要用到extends,代码如下:
    <package name="admin" namespace="/admin" extends="user">
        <action name="admin" class="action.adminAction">
            <result>/admin.jsp</result>
        </action>
    </package>
Action代码如下:
package action;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/15.
 */
public class adminAction extends ActionSupport {
    @Override
    public String execute(){
        return "mainpage";
    }
}
这样配置,使得我们新包继承了原来的包的全局结果集,所以可以使用该结果集。

动态结果集

使用特殊标志来动态接受结果集
struts配置如下:
 <constant name="struts.devMode" value="true" />
    <package name="user" namespace="/user" extends="struts-default">
        <action name="user" class="action.userAction">
            <result>${r}</result>
        </action>
    </package>
可以看到我们的result标签内跳转的页面名改为了${r}的形式,为何使用这种结构呢?
请看Action的代码:
package action;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.inject.Scope;

/**
 * Created by Alex on 2017/5/15.
 */
public class userAction extends ActionSupport {
    private String r;
    private int type;

    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;
    }
}


可以看到我们action代码里,定义了两个变量,生成了2个get and set方法,他们分别为type和r
type就是我们传进来的类型,r就是我们在action内定义的跳转页面名
可是为何我们能在struts.xml内接收到这个r的值呢?
原来struts访问了valueStack,值栈,我们在action内将r存进值栈了,在struts那边就可以用${r}来取到。
可以看到我们的r值就在这里面。

p.s 这种表达式名称为 OGNL表达式

带参数的结果集


使用ONGL表达式传参数,struts配置文件如下:
    <package name="user" namespace="/user" extends="struts-default">
        <action name="user" class="action.userAction">
            <result type="redirect"> /user_success.jsp?t=${type}</result>
        </action>
    </package>
可以看到在result内,我们传过来的参数使用了ONGL表达式来接收

接受jsp页面代码:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/15
  Time: 22:05
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>user success</title>
</head>
<body>
user success!! <br/>
from valueStack:<s:property value="r" /><br/>
from actionContext: <s:property value="#parameters.t"/>
<s:debug></s:debug>
</body>
</html>

效果如图


具体关系图:

可以看到传过来的参数我们并没有储存至值栈内,而是直接传到页面了,所以我们用
<s:property value="t" />
并不能取到值,而用
<s:property value="#parameters.t"/>
就能从actionContext里将t值取出
关系如下图:
在value stack内并没有t的值

但是在stack context内就能找到t的值


结果集(result)总结

Result:
  • 常用的四中类型:
  1. dispatcher(默认)
  2. redirect
  3. chain
  4. redirecAction
  • 全局结果集
  1. global-results || extends(继承另外一个包的结果集)
  • 动态结果(了解即可)
  1. 在action中保存一个属性,储存具体的结果location
  • 传递参数
  1. 客户端跳转才需要传递
  2. ${}(ONGL表达式 不是EL表达式) 从valueStack取值

OGNL

此部分我们使用一个 小项目来进行讲解
刚开始我们配置一下文件,包含两个JSP页面
index.jsp:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/16
  Time: 18:00
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>首页</title>
  </head>
  <body>
  访问属性:<br/>
  <a href="ognl.action?username=alex&password=123">ognl</a>
  </body>
</html>


ognl.jsp:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/16
  Time: 18:01
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>OGNL表达式语言学习</title>
</head>
<body>
<ol>
在此插入例子
</ol>
<s:debug></s:debug>
</body>
</html>

以及 struts.xml配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
    <constant name="struts.devMode" value="true" />
    <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    <include file="/ognl/ognl.xml" />
</struts>

和外部 ognl.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
        "http://struts.apache.org/dtds/struts-2.1.dtd">

<struts>
    <package name="ognl" extends="struts-default">
        <action name="ognl" class="ognl.ognlAction" >
            <result>/OGNL.jsp</result>
        </action>
    </package>
</struts>


以及JAVA类:

  1. ognlAction.java
    package ognl;
    
    import com.opensymphony.xwork2.ActionSupport;
    
    import java.util.*;
    
    /**
     * Created by Alex on 2017/5/16.
     */
    public class ognlAction extends ActionSupport {
        private String username;
        private String password;
        private User user;
        private Cat cat;
        private List<User> users = new ArrayList<User>();
        private Set<Dog> dogs = new HashSet<Dog>();
        private Map<String,Dog> dogMap = new HashMap<String , Dog>();
    
        public Cat getCat() {
            return cat;
        }
    
        public void setCat(Cat cat) {
            this.cat = cat;
        }
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    
        public ognlAction() {
            users.add(new User(1));
            users.add(new User(2));
            users.add(new User(3));
    
            dogs.add(new Dog("dog1"));
            dogs.add(new Dog("dog2"));
            dogs.add(new Dog("dog3"));
    
            dogMap.put("dog100",new Dog("dog100"));
            dogMap.put("dog101",new Dog("dog101"));
            dogMap.put("dog102",new Dog("dog102"));
        }
    
        public List<User> getUsers() {
            return users;
        }
    
        public void setUsers(List<User> users) {
            this.users = users;
        }
    
        public Set<Dog> getDogs() {
            return dogs;
        }
    
        public void setDogs(Set<Dog> dogs) {
            this.dogs = dogs;
        }
    
        public Map<String, Dog> getDogMap() {
            return dogMap;
        }
    
        public void setDogMap(Map<String, Dog> dogMap) {
            this.dogMap = dogMap;
        }
    
        @Override
        public String execute(){
            return SUCCESS;
        }
    
        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;
        }
    
        public String m(){
            return "hello!";
        }
    }
    

  2. User.java:
    package ognl;
    
    /**
     * Created by Alex on 2017/5/16.
     */
    public class User {
        private int age = 8;
    
        public User(){
        }
    
        public User(int age){
            super();
            this.age=age;
        }
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User:" +age ;
        }
    }
    

  3. Cat.java:
    package ognl;
    
    /**
     * Created by Alex on 2017/5/16.
     */
    public class Cat {
        private Dog friend;
    
        public Dog getFriend() {
            return friend;
        }
    
        public void setFriend(Dog friend) {
            this.friend = friend;
        }
    
        public String miaomiao(){
            return "miaomiao~";
        }
    }
    

  4. Dog.java:
    package ognl;
    
    /**
     * Created by Alex on 2017/5/16.
     */
    public class Dog {
        private String name;
        public Dog(){}
        public Dog(String name){
            super();
            this.name = name;
        };
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Dog:" + name;
        }
    }
    
    

  5. S.java:
    package ognl;
    
    /**
     * Created by Alex on 2017/5/16.
     */
    public class S {
        public static String STR="static string";
    
        public static String s(){
            return "static method";
        }
    }
    


此处我们使用迭代式开发(一点点来,步步为营,可调式,士气高涨,主流开发模式)

注意事项:
  1. OGNL全称 Object Gragh Navigation Language,对象图导航语言
  2. 1.user.xxx 只有传,才会构造(在页面内只有传了参数进去,值栈里面才会有值) 当然可以直接new对象出来,默认变量值,效果一样(domain model)。但是需要保持一个无参构造方法
ONGL如何访问对象和action的普通属性与普通方法:
    <li>访问值栈中的Action的普通属性:username = <s:property value="username"/> </li>
    <li>访问值栈中的对象的普通属性(get set方法) :<s:property value="user.age"/></li>
    <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="@ognl.S@s()" /></li>
<li>访问静态属性: <s:property value="@ognl.S@STR" /></li>
<li>访问Math类的静态方法: <s:property value="@@max(2,3)" /></li>
P.S: 第三种方法不常用。
如何访问普通类的构造方法:
<li>访问普通类的构造方法: <s:property value="new ognl.User(8)" /></li>
P.S:在此处可以直接在value属性里new对象出来
如何访问各种集合:
    <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[0].age" /> (最常用) ||  <s:property value="users.{age}[0]" /> </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>访问Map容器的大小: <s:property value="dogMap.size()" /></li>
    <li>访问Set容器的大小: <s:property value="dogs.size()" /></li>
    <li>访问List容器的大小: <s:property value="users.size()" /></li>
P.S:其中访问Set中的某个元素时,并取不到值,因 set集合中元素位置不固定所导致
投影:
    <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}" /></li>
    <li>投影:<s:property value="users.{$#this.age>1}.{age} == null" /></li>
P.S: ^为开头第一个,$为最后一个,而加判断式 == 输出true or false 

JSP页面:





struts标签

要点集合:
  •  通用标签
  1. property
  2. set (默认为action scope 会将值放入request 和 ActionContext中  ;   page,request,session,application)
  3. bean
  4. include(对中文文件有支持问题,不建议使用,如需包含,改用jsp包含)
  5. param
  6. debug
  • 控制标签
  1. if、elseif、 else
  2.  iterator(collections map  enumeration iterator array,循环遍历)
  3. subset(截取集合一部分)
  • UI标签
  1. theme(simple xhtml:默认 、 css xhtml、 ajax )
  • AJAX标签
  1. 再补充
  • $  #  %的区别
  1. $用于 i18n 和 struts配置文件
  2. #取得ActionContext的值
  3. %将原本的文本属性解析为ognl,对于本来就是ongl的属性不起作用(参考<s:property>和<s:include>)
在此我们还是使用小项目的形式来进行总结:
首先我们需要配置几个jsp页面
index.jsp:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/16
  Time: 23:15
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>首页</title>
  </head>
  <body>
  访问属性:<br/>
  <a href="tags.action?username=alex&password=123">tags</a>
  </body>
</html>
在此处我们传了两个参数(username和password)至struts.xml

tags.jsp:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/17
  Time: 5:21
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<ol>
   在此处写例子
</ol>
</body>
</html>


在配置完成后,再看看struts.xml是如何配置的:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <constant name="struts.devMode" value="true" />
    <package name="tags" extends="struts-default">
            <action name="tags" class="tags.tagsAction">
                <result>/tags.jsp</result>
            </action>
    </package>
</struts>

然后是action:

tagsAction:
package tags;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/17.
 */
public class tagsAction extends ActionSupport {
    private String username;
    private String password;

    public tagsAction(){
    }

    @Override
    public String execute(){
        this.addFieldError("fieldError.test","wrong!");
        return SUCCESS;
    }

    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;
    }
}

注意学习方法:
尽量多查阅官方DOC文档,帮助很大。


通用标签:

struts标签的property标签
官方文档:

页面代码:
    <li>property: <s:property value="username" /> </li>
    <li>property 取值为字符串: <s:property value="'username'" /> </li>
    <li>property 设定默认值: <s:property value="admin" default="管理员" /> </li>
    <li>property 设定HTML: <s:property value="'<hr/>'" escape="false" /> </li>
P.S:value的属性为object类型,都会把里面的对象解析为OGNL表达式。设定默认值意思是,当property在值栈中找不到对象,则会使用这个默认值。


struts标签的set标签
官方文档:

页面内容:
    <li>set 设定adminName值(默认为request和ActionContext):<s:set var="adminName" value="username" /> var adminName = username</li>
    <li>set 从request里取值 :<s:property value="#request.adminName" /></li>
    <li>set 从ActionContext里取值 :<s:property value="#adminName" /></li>
    <%--<li>set 设定范围:<s:set name="adminPassword" value="password" scope="page" /> name adminPassword = password (page) </li>--%>
    <%--<li>set 从相应的范围取值:<%=pageContext.getAttribute("adminPassword")%></li>--%>
    <li>set 设定var 范围为ActionContext: <s:set name="adminPassword" value="password" var="a"/> var a = password(废弃) </li>
    <li>set 使用#取值: <s:property value="#a" /> </li>
    <li>set 设定var 范围为Session: <s:set var="adminPassword" value="password" scope="session"/> var adminPassword = password(主流) </li>
    <li>set 从相应的范围取值: <s:property value="#session.adminPassword" /> </li>
P.S:这里的value的属性也为object类型,官方doc文档有bug。
 id与name属性已经废弃,可以不使用。 var使用时,默认为Action,但是用 scope可以指定特定的储存空间。


struts标签的bean标签
官方文档:

页面内容:
     <li>
        bean 定义bean,并使用param来设定新的属性值:
        <s:bean name="tags.Dog" var="myDog">
            <s:param name="name" value="'linda'"></s:param>
        </s:bean>
    </li>
    <li>bean 取值:<s:property value="#myDog.name" /> </li>
    <li>bean 查看debug:<s:debug></s:debug></li>
P.S:param子标签内部的value值我们若需要插入字符串格式,那么需要在里面加上单引号,若是需要插入OGNL表达式,则不需要单引号。
struts标签的include标签
官方文档:
页面内容:
    <li>
        include _include1.html 包含静态英文文件:
        <s:include value="_include1.html" ></s:include>
    </li>
    <li>
        include _include1.html 包含静态中文文件:
        <s:include value="_include2.html" ></s:include>
    </li>
    <li>
        include _include1.html 包含静态中文文件,说明%的用法:
        <s:set var="incPage" value="'/_include2.html'" />
        <s:include value="%{#incPage}" ></s:include>
    </li>
P.S: 老版本的中文include bug已经修复,在此提到%{}的作用,即强制将字符串定义为OGNL表达式进行传递


struts标签的fieldError标签
官方文档

页面内容:
    <li>
        fieldError simple:<s:fielderror fieldName="fieldError.test" theme="simple"></s:fielderror>
    </li>
P.S:此标签有默认样式(ul li标签)若需要去除标签样式,请转至本博客另一篇去除struts2中s:fieldError标签中的默认样式中查阅

控制标签

if、else if与else标签
官方文档:
if:

elseif:

页面内容:
    <li>
        if elseif 和 else:
        age = <s:property value="#parameters.age" /><br/>
        <s:if test="#parameters.age[0] < 0">wrong age!</s:if>
        <s:elseif test="#parameters.age[0] <= 20" >too young!</s:elseif>
        <s:else>这才对嘛</s:else>
        <s:if test="#parameters.aaa == null">null</s:if>
    </li>
简单easy,通俗易懂
P.S:不难发现这里多次用到#parameters.age[0] 我们可以使用set标签来优化语句


iterator标签
官方文档:

页面内容:
    <li>
        遍历集合:<br/>
        <s:iterator value="{1,2,3}">
            <s:property /> |
        </s:iterator>
    </li>

    <li>
        自定义变量:<br/>
        <s:iterator value="{'aaa','bbb','ccc'}" var="x" >
            <s:property value="#x.toUpperCase()" /> |
        </s:iterator>
    </li>

    <li>
        使用struts:<br/>
        <hr/>
        <s:iterator value="{'aaa','bbb','ccc'}" status="status">
            元素值:<s:property/> <br/>
            遍历过的元素总数:<s:property value="#status.count" /> <br/>
            遍历过的元素索引:<s:property value="#status.index" /> <br/>
            当前是偶数? <s:property value="#status.even"/><br/>
            当前是奇数? <s:property value="#status.odd"/><br/>
            是第一个元素吗? <s:property value="#status.first" /><br/>
            是最后一个元素吗? <s:property value="#status.last" /> <br/>
            <hr/>
        </s:iterator>
    </li>

    <li>
        遍历Map:<br/>
        <s:iterator value="#{1:'a',2:'b',3:'c'}">
            <s:property  value="key" /> | <s:property value="value"/> <br/>
        </s:iterator>
    </li>
P.S:在遍历Map时,需要在value里加入#符。


subset标签
官方文档:

页面内容:
     <s:subset source="myList" count="13" start="3">
        <s:iterator>
          <s:property />
        </s:iterator>
     </s:subset>
P.S:截取部分集合内容,了解即可,过多不再阐述


UI标签

开发中不常用,因为有不方便之处,与JavaScript结合不完美
总的 来说:
  1. 把所有的主体定义为simple
  2. fieldError特殊化处理
  3. 自己控制其他标签的展现

BBS项目

注:项目开发环境由原来的struts-2.1.6换为struts-2.3.32,IDE版本升级为2017.1.1,在此不写BBS项目开发代码,只写流程,巩固学习

项目开发事项

设计约定(编码规定):
  1. 原则:能简单就别复杂,简单就是美;
  2. 库名:项目名
  3. 表的命名:_Model名
  4. 字段命名:保持与属性名一致(尽量不要与数据库命名冲突)
  5. 用层来划分包:*.*.action: userAction studentAction   *.*.model(bean): User,Student. *.*.service(dao):userDAO,StudentDAO
  6. Action的命名:模块名+Action 驼峰命名规则
  7. JSP的命名:*-* 
  8. package的命名:action adminAction

开发过程:

1.建立界面模型
2.建立struts.xml
a).确定namespace
b).确定package
c).确定Action的名称
d).确定result
e).将界面原型页面进行修改,匹配现有配置
f).测试
3.建立数据库(或实体类)
4.建立model层
5.建立service层(之后用hibernate完善)
a).此时可以使用Junit进行测试
6.着手开发

声明式异常处理

声明抛出一个异常交给struts处理,并进行指定操作。
如:
在此处我们先抛出一个异常

然后在catch里写出throw语句

在***Action文件内,将此异常接收,发往struts.xml文件进行操作:

在struts.xml内,使用 exception-mapping接收异常并指定操作:

流程大概就是这样,理解最重要。
使用声明式异常的过程中,若需要给多个包配置异常,可使用global-exception-mapping进行配置,再使用global-result进行映射。其他包只需继承该包即可

总结:
  1. 在Action中进行异常映射
  2. 在package中进行全局异常映射
  3. 使用继承共用异常映射
  4. struts2中异常处理由拦截器实现(观察struts-default.xml) 实际上struts2的大多数功能都由拦截器实现

I18N

i18N的原理:
ResourceBundle和Locale的概念
资源文件
native2ascii
struts2的资源文件:
Action-Package-App级
PropertiesEditor插件
解压
features和plugin文件夹覆盖eclipse源目录,即可使用该插件编辑中文.
动态语言切换(request.locale = 'en_US')

拦截器

struts拦截器在开始之前,我们先介绍一段怎么样去阅读源码的教程:
我们在建立起一个项目后,在能代码能正常运行的基础上,顺着一条线读下去,
先理脉络,再读枝节
脉络图:
图解拦截过程:

类型转换

例子介绍:
test,jsp:
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/24
  Time: 23:07
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title>类型转换测试</title>
  </head>
  <body>
  name:<s:property value="name" /> <br/>
  age: <s:property value="age" />
  </body>
</html>
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>
    <package name="test" namespace="/" extends="struts-default">
        <action name="test" class="com.alex.action.testAction">
            <result>/test.jsp</result>
        </action>
    </package>
</struts>
testAction.java:
package com.alex.action;

import com.opensymphony.xwork2.ActionSupport;

/**
 * Created by Alex on 2017/5/24.
 */
public class testAction extends ActionSupport {
    private String name;
    private int age;

    @Override
    public String execute() throws Exception{
        return super.execute();
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
非常简单的一个环境,运行后是这样子的:

在此时已经发生类型转换了.所谓类型转换就是把字符串与特定类型相互转换,
接下来我们在action里添加一个日期的变量:
package com.alex.action;

import com.opensymphony.xwork2.ActionSupport;

import java.util.Date;

/**
 * Created by Alex on 2017/5/24.
 */
public class testAction extends ActionSupport {
    private String name;
    private int age;
    private Date d;

    public Date getD() {
        return d;
    }

    public void setD(Date d) {
        this.d = d;
    }

    @Override
    public String execute() throws Exception{
        return super.execute();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
在前端使用<s:date>标签对日期类型进行转换,
<%--
  Created by IntelliJ IDEA.
  User: Alex
  Date: 2017/5/24
  Time: 23:07
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title>类型转换测试</title>
  </head>
  <body>
  name:<s:property value="name" /> <br/>
  age: <s:property value="age" /><br/>
  date: <s:property value="d"/><br/>
  <s:date format="yyyy/MM/dd HH:mm:ss" name="d" />
  </body>
</html>
可以看到如下效果:

struts2总结

程度解释:
了解:可以依稀记得,遇到问题是可模糊想起完成方法,查阅资料即可想起
掌握:比较熟悉,如果忘记了,查阅资料即可想起即可.
精通:能非常熟练运用,稍有忘记,参考资料即可想起.

一、Action
  1.namespace( 掌握
2.path( 掌握)
3.DMI( 掌握)
4.wildcard( 掌握)
5.接收参数( 掌握前两种)
a)访问属性
b)Domain Model
6.访问request等( 掌握Map IoC)即 Map继承方法
7.简单数据验证( 掌握addFieldError 和 <s:fieldError>)

二、Result
1.结果类型( 掌握四种,重点两种)
a)Redirect(重点)
b)Dispacther(重点)
2.全局结果( 掌握)
3.动态结果( 了解)

三、OGNL表达式(精通)
1. # , %, $ 分别有什么作用

四、struts标签
1. 掌握常用的

五、声明式异常处理(了解)

六、i18N(了解)

七、CRUD(Create-Retrieve-Upate-Delete)的过程(最重要的是设计与规划) (精通)

八、Interceptor的原理(掌握)

九、类型转换(掌握默认,了解自定义)

===================================================================================
Struts2的学习内容到此暂告一段落,以上为本人总结梳理出来的学习心得,希望能对大家在学习Struts2上有所帮助.
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值