struts2基础

 

注意:

1.action对象是多例的,每次请求都创建一个对象,线程安全

2.struts2默认拦截没有后缀,或者.action的请求

3.struts2注解依赖的jar包:struts2-convention-plugin-2.3.15.3.jar,asm...jar[三个]

4.jar包:

 

 

1.struts2的配置文件

default.properties:注意配置常量  【不可改,在jar包中】

struts-default.xml :常量,bean,struts-default包【结果视图,拦截器】,默认动作类,动作方法【不可改,jar包中】

struts-plugin.xml  【不可改】

struts.xml  【自己创建】

struts.properties  【自己创建】

web.xml

 

加载顺序:default.propeties ->struts-default.xml->struts-plugin.xml->struts.properties->web.xml

 

 2.struts2核心思想

 

结果视图:

  以ServletDispatcherResult为例:继承StrutsResultSupport类,

public class ServletDispatcherResult extends StrutsResultSupport {
    private static final long serialVersionUID = -1970659272360685627L;
    private static final Logger LOG = LoggerFactory.getLogger(ServletDispatcherResult.class);
    private UrlHelper urlHelper;

    public ServletDispatcherResult() {
    }

    public ServletDispatcherResult(String location) {
        super(location);
    }

    @Inject
    public void setUrlHelper(UrlHelper urlHelper) {
        this.urlHelper = urlHelper;
    }

    public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Forwarding to location " + finalLocation, new String[0]);
        }

        PageContext pageContext = ServletActionContext.getPageContext();
        if (pageContext != null) {
            pageContext.include(finalLocation);
        } else {
            HttpServletRequest request = ServletActionContext.getRequest();
            HttpServletResponse response = ServletActionContext.getResponse();
            RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);
            if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf("?") > 0) {
                String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1);
                Map<String, Object> parameters = this.getParameters(invocation);
                Map<String, Object> queryParams = this.urlHelper.parseQueryString(queryString, true);
                if (queryParams != null && !queryParams.isEmpty()) {
                    parameters.putAll(queryParams);
                }
            }

            if (dispatcher == null) {
                response.sendError(404, "result '" + finalLocation + "' not found");
                return;
            }

            Boolean insideActionTag = (Boolean)ObjectUtils.defaultIfNull(request.getAttribute("struts.actiontag.invocation"), Boolean.FALSE);
            if (!insideActionTag.booleanValue() && !response.isCommitted() && request.getAttribute("javax.servlet.include.servlet_path") == null) {
                request.setAttribute("struts.view_uri", finalLocation);
                request.setAttribute("struts.request_uri", request.getRequestURI());
                dispatcher.forward(request, response);
            } else {
                dispatcher.include(request, response);
            }
        }

    }

    private Map<String, Object> getParameters(ActionInvocation invocation) {
        return (Map)invocation.getInvocationContext().getContextMap().get("parameters");
    }
}
ServletDispatcherResult

  

  最终执行的是request.getRequestDispatcher(location).forward(req, resp)方法,相当于一个处理请求的最终servlet

 

  因此,可以自定义结果视图类型,灵活的完成各种输出:

下面以输出验证码为例:

jar包:ValidateCode.jar

package cn.getword.result;

import cn.dsna.util.images.ValidateCode;
import com.opensymphony.xwork2.ActionInvocation;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsResultSupport;

import javax.servlet.http.HttpServletResponse;

/**
 * 自定义结果视图类型
 */
public class CaptchaResult extends StrutsResultSupport {
    //配置图像的大小
    private int width;
    private int height;


    @Override
    protected void doExecute(String finalLocation, ActionInvocation invocation)
            throws Exception {
        // TODO Auto-generated method stub
        /***
         * 1.导入第三方验证码生成包
         * 2.创建ValidateCode对象
         * 3.获取相应对象
         * 4.输出验证码
         */
        ValidateCode code=new ValidateCode(width,height,4,150);
        HttpServletResponse response= ServletActionContext.getResponse();
//        response.getWriter().print("hello world");
        code.write(response.getOutputStream());

    }

    public int getWidth() {
        return width;
    }


    public void setWidth(int width) {
        this.width = width;
    }


    public int getHeight() {
        return height;
    }


    public void setHeight(int height) {
        this.height = height;
    }

}
CaptchaResult

 

 

在struts.xml中配置使用自定义结果视图类型:

    <!-- 验证码 -->
    <package name="validateCode" namespace="/validate" extends="struts-default">

        <!-- 自定义结果视图类型  局部 -->
        <result-types>
            <result-type name="catpcha" class="cn.getword.result.CaptchaResult"></result-type>
        </result-types>
        <!--
            使用默认动作类ActionSupport来处理,默认返回success,返回的结果视图类型为自定义结果视图catpcha(类似Servlet类),
         -->
        <action name="captcha">
            <!-- 视图结果处理类类似于一个Servlet类,不同的类型完成不同的任务,如请求转发、重定向、自定义处理类型 -->
            <result name="success" type="catpcha">
                <!-- 配置图像的大小,依赖注入 -->
                <param name="width">200</param>
                <param name="height">50</param>
            </result>
        </action>
    </package>
struts.xml

 

   

运行结果:

 

 

3.package标签

package:

  name:包名,唯一

  extends:继承父包,默认继承struts-default,在struts-default.xml中配置

  abstract:是否为抽象包,抽象包中不允许出现action动作

  namespace:命名空间,模块化管理,必须以 / 开头,第一个字符必须是字母,

    默认值:""

 

 4.action标签

  name:

  class:

  method:

action 的三种访问方式:

  • 全匹配方式:
  • 通配符:

基本用法:

<package name="user" extends="struts-default" namespace="/user">
    <!-- {1}:表示第一个匹配到的* -->
    <action name="*User" class="cn.getword.action.UserAction" method="{1}User">
        <result name="success" type="dispatcher">/index.jsp</result>
    </action>
</package>

 

 

更彻底的方式:

<package name="user" extends="struts-default" namespace="/user">
    <!-- {1}:表示第一个匹配到的* -->
    <action name="*_*" class="cn.getword.action.{2}Action" method="{1}{2}">
        <result name="success" type="dispatcher">/index.jsp</result>
    </action>
</package>

 

 

  • 动态方法调用
<constant name="struts.enable.DynamicMethodInvocation" value="true" />

 

<action name="user" class="cn.getword.action.UserAction">
    <result name="success" type="dispatcher">/index.jsp</result>
</action>

 

<a href="/user?addUser"> :  user对应action的name属性,addUser对应动作类的方法

 

 

 

 5.动作类的创建方式

(1)无侵入低耦合的方式 POJO  【基本不用】

 

(2)实现Action接口,实现excute方法【默认动作方法】

package cn.getword.action;

import com.opensymphony.xwork2.Action;


public class MainAction implements Action{

    @Override
    public String execute() throws Exception {
        return null;
    }
}
MainAction

 

 

(3)继承ActionSupport类

  默认的动作类为:ActionSupport

  默认动作方法:excute方法

 

 6.result结果视图

  常用逻辑结果视图:

chain:转发到action【同包】

dispatcher:只能转发到物理视图

redirect:url原封不动的返回给客户端,客户端再次使用这个地址发起请求。

redirectAction:重定向到action【同包或不同包】,默认会自动加上后缀名

 

 

全局结果视图:

 

 

    <package name="mydefault" extends="struts-default" abstract="true">
        <global-results>
            <result name="error">/WEB-INF/views/error.jsp</result>
        </global-results>
    </package>

    <package name="p2" extends="mydefault">
        <action name="index" class="cn.getword.action.MainAction" method="index">
            <result name="success">/index.jsp</result>
        </action>
        <action name="login">
            <result type="dispatcher">/WEB-INF/views/login.jsp</result>
        </action>
    </package>
View Code

 

 

JSONResult结果视图:

(1)jar包:struts2-json-plugin...jar

(2)struts.xml

   <package name="p1" extends="json-default">
        <action name="getUsers" class="cn.getword.action.UserAction" method="allUsers">
            <result type="json">
                <param name="noCache">true</param>
                <param name="contentType">application/json</param>
            </result>
        </action>
    </package>

 

(3)UserAction

package cn.getword.action;

import cn.getword.domain.User;
import com.opensymphony.xwork2.ActionSupport;

import java.util.ArrayList;
import java.util.List;

public class UserAction extends ActionSupport {
    private List<User> users;
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public String allUsers(){
        user = new User(){{
            setId(1);
            setUsername("admin");
            setAge(12);
        }};
        users = new ArrayList<User>();
        users.add(new User(){{
            setId(1);
            setUsername("admin");
            setAge(12);
        }});
        users.add(new User(){{
            setId(2);
            setUsername("张三");
            setAge(21);
        }});
        return SUCCESS;
    }
}
UserAction.java

 

(4)结果

 

 

7.访问Servlet API

方式一:使用工具类:ServletActionContext【常用】

public class MainAction extends ActionSupport {
    public String index(){
        ServletActionContext.getRequest();  //struts2提供的包装类
        ServletActionContext.getRequest();
        ServletActionContext.getRequest().getSession();
        ServletActionContext.getServletContext();
        return ERROR;
    }
}

 

 

方式二:通过实现接口的方式。拦截器ServletConfig封装参数

public class MainAction extends ActionSupport implements ServletRequestAware{
    private HttpServletRequest request;
    public String index(){
        System.out.println(request);
        return ERROR;
    }

    @Override
    public void setServletRequest(HttpServletRequest request) {
        this.request = request;
    }
}

 

 

第三种方式:上面两种方式的底层都是通过ActionContext的get(key)获取的

  ActionContext

 

 8.请求参数的封装

  ParameterInterceptor拦截器做了数据类型转换。【注意,请求参数封装到动作类实例的成员变量中,是拦截器干的事情

方式一:属性驱动-没有实体类

  注意:动作类中定义对应的成员变量,必须要有set和get 方法

  对于多个name相同的值转化成数组

public class User1Action extends ActionSupport {
    private String username;
    private int age;
    private Date birthday;
    private String[] hobby;

    public String register(){
        System.out.println(username+","+age+","+birthday+","+ Arrays.toString(hobby));
        return "register";
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String[] getHobby() {
        return hobby;
    }

    public void setHobby(String[] hobby) {
        this.hobby = hobby;
    }
}
UserAction

 

 表单:

  <form method="post" action="${pageContext.request.contextPath}/user/register">
    username:<input type="text" name="username"><br>
    age:<input type="text" name="age"><br>
    birthday:<input type="text" name="birthday"><br>
    hobby:<input type="checkbox" name="hobby" value="篮球">篮球
    <input type="checkbox" name="hobby" value="足球">足球
    <input type="checkbox" name="hobby" value="排球">排球<br>
    <input type="submit" value="提交" />
  </form>
View Code

 

 

  使用场景:不需要实体类的情况,如分页、查询等,不需要存储到数据库的表单数据。

 

方式二:属性驱动:有实体类  【不常用】

  在动作类中定义一个实体类成员变量,提供get、set方法。

Action:

public class User2Action extends ActionSupport {
    private User user;
    public String register(){
        System.out.println(user);
        return SUCCESS;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
View Code

 

  表单的name为user.xxx【OGNL表达式】

Form:

  <form method="get" action="${pageContext.request.contextPath}/user/register">
    username:<input type="text" name="user.username"><br>
    age:<input type="text" name="user.age"><br>
    birthday:<input type="text" name="user.birthday"><br>
    hobby:<input type="checkbox" name="user.hobby" value="篮球">篮球
    <input type="checkbox" name="user.hobby" value="足球">足球
    <input type="checkbox" name="user.hobby" value="排球">排球<br>
    <input type="submit" value="提交" />
  </form>
View Code

 

 

 

 方式三:模型驱动,实现ModelDriven<>接口 【常用】

  规范:实现ModelDriver接口,模型属性必须new。

public class User3Action extends ActionSupport implements ModelDriven<User> {
    private User user = new User();

    public String register(){
        System.out.println(user);
        return SUCCESS;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public User getModel() {
        return user;
    }
}
View Code

 

 

集合类型的参数封装:

  集合类型的参数只能通过属性驱动来封装。【OGNL】

(1)list集合   name: users[0].xxx

action:

public class User4Action extends ActionSupport {
    private List<User> users;

    public String register(){
        System.out.println(users);
        return SUCCESS;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }
}
View Code

 

form:

<form method="get" action="${pageContext.request.contextPath}/user/register">
    username:<input type="text" name="users[0].username"><br>
    age:<input type="text" name="users[0].age"><br>
    birthday:<input type="text" name="users[0].birthday"><br>
    hobby:<input type="checkbox" name="users[0].hobby" value="篮球">篮球
    <input type="checkbox" name="users[0].hobby" value="足球">足球
    <input type="checkbox" name="users[0].hobby" value="排球">排球<br>
    <input type="submit" value="提交" />
  </form>
View Code

 

 

 

 (2)Map对象

  OGNL: name : users['key'].xxx

action:

public class User5Action extends ActionSupport {
    private Map<String, User> users;

    public String register(){
        for (String k :
                users.keySet()) {
            System.out.println("key:"+k+", value:"+ users.get(k));
        }
        return SUCCESS;
    }

    public Map<String, User> getUsers() {
        return users;
    }

    public void setUsers(Map<String, User> users) {
        this.users = users;
    }
}
View Code

 

 

form:

  <form method="get" action="${pageContext.request.contextPath}/user/register">
    username:<input type="text" name="users['user1'].username"><br>
    age:<input type="text" name="users['user1'].age"><br>
    birthday:<input type="text" name="users['user1'].birthday"><br>
    hobby:<input type="checkbox" name="users['user1'].hobby" value="篮球">篮球
    <input type="checkbox" name="users['user1'].hobby" value="足球">足球
    <input type="checkbox" name="users['user1'].hobby" value="排球">排球<br>
    <input type="submit" value="提交" />
  </form>
View Code

 

 

9.请求封装异常处理【封装失败,如日期转换失败,数字转换等】,数据回显(界面太丑,基本不用)

  适用于模型驱动,有实体类的属性驱动

 

  当ParameterInterceptor做类型转化时发生错误,将返回input逻辑视图,因此只要在action中配置input结果视图即可

struts.xml:

   <package name="p3" extends="struts-default" namespace="/user">
        <action name="register" class="cn.getword.action.User3Action" method="register">
            <result name="success">/WEB-INF/views/success.jsp</result>
            <result name="input" type="redirect">/user/register</result>
        </action>
    </package>
struts.xml

 

 action:

public class User3Action extends ActionSupport implements ModelDriven<User> {
    private User user = new User();

    public String register(){
        System.out.println(user);
        return SUCCESS;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public User getModel() {
        return user;
    }
}
View Code

 

 

form:

register.jsp

 

  或者使用struts提供的form表单: 

  <%--自动补齐项目前缀--%>
  <s:form action="user/register">
    <s:textfield name="username" label="姓名" />
    <s:textfield name="age" label="年龄" />
    <s:textfield name="birthday" label="生日" />
    <s:checkbox name="hobby" value="篮球" fieldValue="篮球" label="篮球" />
    <s:checkbox name="hobby" value="足球" fieldValue="足球" label="足球" />
    <s:checkbox name="hobby" value="排球" fieldValue="排球" label="排球" />

    <s:submit value="提交" />
  </s:form>
View Code

 

 

将提示信息改为中文:在模型类的同目录下创建User.properties属性文件。

invalid.fieldvalue.birthday=日期格式不对

 

 

  在实际项目开发中,需要在前端进行表单验证,因此传到后台的数据基本合法,如何不合法(转换失败),需要提供input结果视图,以防止出现尴尬的界面(struts的报错界面)

   表单数据被保存在request域中,因此可以使用EL表达式进行数据回显。

  <form method="post" action="${pageContext.request.contextPath}/user/register">
    username:<input type="text" name="username" value="${username}"><br>
    age:<input type="text" name="age" value="${age}"><br>
    birthday:<input type="text" name="birthday" value="${birthday}" ><br>
    hobby:<input type="checkbox" name="hobby" value="篮球">篮球
    <input type="checkbox" name="hobby" value="足球">足球
    <input type="checkbox" name="hobby" value="排球">排球<br>
    <input type="submit" value="提交" />
  </form>
View Code

 

  使用此方法,可以解决struts2的标签带来的bug,例如:封装失败后,第一次返回input结果视图,第二次返回error结果视图

   此外,还发现一个问题,struts2对于post和get请求没有过滤区分?

 

 

 

end

posted @ 2018-09-25 16:48  fight139  阅读(153)  评论(0编辑  收藏  举报