struts2 学习

文章目录

  • Struts
    • 基础
      • hello struts
        • 练习 访问hello跳转hello.jsp
      • 显示数据到JSP
        • 练习 访问/showTime跳转showTime.jsp并且显示当前时间
      • 提交数据到action
        • 流程
        • 练习
      • 解决struts中文问题
      • STRUTS 启动失败,出现错误ERROR FILTERSTART 应该如何调试
        • 关闭日志
      • request与response
      • session
      • 上传文件
      • 上传文件最大值
    • 标签
      • form标签
      • iterator标签
        • 为ProductAction增加list方法
      • check标签
      • radio标签
      • select 标签
      • 多重迭代
    • 调试
      • 开启调试
      • 查看action到底有没有传递数据
      • 通配符匹配链接
      • 拦截器
      • 客户端跳转
      • 客户端跳转带参数
      • 表单验证
      • xml实现表单验证
      • Action是多实例的
      • 注解
        • 其他常用注解

仓库地址: https://github.com/biuaxia/struts

Struts

基础

hello struts

  1. 创建动态web项目,idea可选Struts2,Eclipse可选择dynamic web project的方式

  2. 导入jar包

    由于我使用的是idea,我先移动lib文件夹到了web-inf下,然后配置了FileSet即可

  3. 配置web.xml

    首先经过tomcat容器,所以先配置struts的过滤器,拦截全部请求

    
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
        <filter>
            <filter-name>struts2filter-name>
            <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterfilter-class>
        filter>
        <filter-mapping>
            <filter-name>struts2filter-name>
            <url-pattern>/*url-pattern>
        filter-mapping>
    web-app>
    
  4. 配置struts.xml文件

    该文件配置了具体的请求内容,例如简单的实现访问index服务器跳转index.jsp

    
    
    
    
    <struts>
        
        <package name="basicStruts" extends="struts-default">
            <action name="index">
                <result>index.jspresult>
            action>
        package>
    
    struts>
    
  5. 设置index.jsp内容

    这里随便写点html内容即可

    <%--
      Created by IntelliJ IDEA.
      User: Administrator
      Date: 2019/2/10
      Time: 21:50
      在这里添加文件的描述
    --%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
             pageEncoding="UTF-8" isELIgnored="false" %>
    
    
    
        /index
    
    
    你好struts
    
    
    
  6. 启动tomcat并测试访问/index

  7. 基本思路
    访问/index -> 被web.xml配置的struts拦截器处理 -> 根据struts.xml的配置,访问/index将跳转到index.jsp文件 -> 显示index.jsp

练习 访问hello跳转hello.jsp

准备一个hello.jsp文件

通过配置struts.xml实现访问路径 /hello 跳转到 hello.jsp

显示数据到JSP

把Model的数据显示在视图JSP上

  1. 准备产品Product实体类

    package vip.javer.bean;
    
    import java.io.Serializable;
    
    /** * @author Administrator */
    public class Product implements Serializable {
        int id;
        String name;
        
        public int getId() {
            return id;
        }
        
        public void setId(int id) {
            this.id = id;
        }
        
        public String getName() {
            return name;
        }
        
        public void setName(String name) {
            this.name = name;
        }
    }
    
  2. 准备产品ProductAction

    package vip.javer.action;
    
    import vip.javer.bean.Product;
    
    /** * @author Administrator */
    public class ProductAction {
        private Product product;
        
        public String show() {
            product = new Product();
            product.setName("OnePlus A5000");
            return "show";
        }
        
        public Product getProduct() {
            return product;
        }
        
        public void setProduct(Product product) {
            this.product = product;
        }
    }
    
  3. 修改struts.xml配置

    <action name="showProduct" class="vip.javer.action.ProductAction" method="show">
        <result name="show">show.jspresult>
    action>
    

    这里需要说明一下代码

    中的name表示访问路径,class表示该路径被访问时会查询的类,method表示查询该类的show方法
    show.jsp中的name表示方法show的返回值为show时调用视图show.jsp

练习 访问/showTime跳转showTime.jsp并且显示当前时间

提交数据到action

  1. show.jsp添加表单控件,方便提交
<form action="addProduct">
    <input type="text" name="product.name">
    <br/>
    <input type="submit" value="submit">
form>
  1. 在ProductAction类添加add方法
public void add(){
    return "success";
}
  1. 修改struts.xml配置
<action name="addProduct" class="vip.javer.action.ProductAction" method="add">
    <result name="success">show.jspresult>
action>
  1. 测试

流程

  1. 携带输入框参数名为product.name的值访问addProduct
  2. web.xml的struts过滤器拦截
  3. 根据配置文件struts.xml, 会执行ProductAction的add方法
  4. 在add方法执行之前,Struts生成一个新的product对象,并把页面传递过来的name设置在该对象上,接着把该对象通过setProduct()方法,注入ProductAction
  5. 服务端跳转到show.jsp
  6. 在show.jsp中,访问ProductAction.getProduct() 获取注入的product,并显示其名称

练习

在页面上新增加age字段,提交到Action

为product新增加一个age属性,并提供setter和getter

最后在show.jsp上显示页面提交的age数据

解决struts中文问题

Struts的中文问题,由3部分组成

  1. jsp提交数据的时候,必须是UTF-8编码的
  2. struts拿到数据后进行UTF-8解码
  3. 服务端跳转到jsp进行显示的时候,要指定浏览器使用UTF-8进行显示
    UTF-8可以换成GBK或者GB2312,但是必须统一,不能混用

我这里由于idea的jsp模板已被我修改,所以不用配置<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%>

  1. 修改前端表单方法为post
  2. 指定struts.xml的解码方式为utf-f
<struts>
    
    <constant name="struts.i18n.encoding" value="UTF-8"/>
    ...
struts>
  1. 测试

STRUTS 启动失败,出现错误ERROR FILTERSTART 应该如何调试

在tomcat启动struts web应用的时候,如果出现了struts配置上的错误,你可能只能看到一个 Error FilterStart的提示,而看不到详细的错误原因。

这样就加大了定位和解决问题的难度

这是因为默认配置下,struts把日志输出关闭了
为了把日志输出开启便于调试,需要增加log4j.xml这个配置文件




<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %c.%M:%L - %m%n"/>
        layout>
    appender>

    
    <logger name="com.opensymphony">
        <level value="DEBUG"/>
    logger>

    <logger name="org.apache.struts2">
        <level value="DEBUG"/>
    logger>

    
    <root>
        <priority value="INFO"/>
        <appender-ref ref="STDOUT"/>
    root>

log4j:configuration>

关闭日志

有了log4j.xml日志输出可以帮助调试,但是也会导致struts的启动变慢。

关闭log4j.xml日志输出很简单,直接把log4j.xml命名成其他文件即可,以后要用再把名字改回来,很方便

request与response

想要在struts中使用request与response很简单

  1. ServletActionContext.getRequest()
  2. ServletActionContext.getResponse()

在Tomcat的控制台输出 可以看到Struts使用类StrutsRequestWrapper对HttpServletRequest进行了封装
struts2 学习_第1张图片

session

在struts中session有两个

  1. HttpSession,通过之前的request对象获取得到session对象
  2. Session,该对象为一个map集合,其中的值与httpsession同步
HttpSession session = ServletActionContext.getRequest().getSession();
Map<String, Object> sessionMap = ActionContext.getContext().getSession();

我们可以在add方法中把添加的把内容存入到session中看看效果

public String show() {
    /*测试request与response对象*/
    HttpServletRequest request = ServletActionContext.getRequest();
    HttpServletResponse response = ServletActionContext.getResponse();
    System.out.println(request);
    System.out.println(response);
    
    /*测试session对象*/
    HttpSession session = ServletActionContext.getRequest().getSession();
    Map<String, Object> sessionMap = ActionContext.getContext().getSession();
    session.setAttribute("name1", "name1");
    sessionMap.put("name2", "name2");
    
    /*四大域存放测试: application,request,session,pageContext*/
    ActionContext requestMap = ActionContext.getContext();
    Map<String, Object> application = requestMap.getApplication();
    Map<String, Object> session1 = requestMap.getSession();
    
    requestMap.put("requestField", "request域内容");
    application.put("applicationField", "application域");
    session1.put("sessionField", "session域");
    
    /*正常代码逻辑*/
    product = new Product();
    product.setName("OnePlus A5000");
    String result;
    int i = (int) (Math.random() * 50);
    product.setId(i);
    if (i % 2 == 0) {
        result = "show";
    } else {
        result = "hide";
    }
    return result;
}
<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2019/2/10
  Time: 22:28
  在这里添加文件的描述
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" isELIgnored="false" %>



    show


show.jsp

${product.id} - ${product.name} - ${product.age}
Session: ${session_id} - ${session_name} - ${session_age}
四大域测试: ${requestField} - ${applicationField} - ${sessionField}



测试session

session中的name1: ${name1}
session中的name2: ${name2}

上传文件

使用struts框架上传文件

  1. 准备upload.jsp

    <%@ taglib prefix="s" uri="/struts-tags" %>
    <%--
      Created by IntelliJ IDEA.
      User: Administrator
      Date: 2019/2/12
      Time: 8:18
      在这里添加文件的描述
    --%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
             pageEncoding="UTF-8" isELIgnored="false" %>
    
    
    
        upload
    
    
    <%----%>
    
    上传文件 :
  2. 准备对应的上传处理action类

    package vip.javer.action;
    
    import java.io.File;
    
    /** * 在upload.jsp中file字段对应的name是"doc" * 所以在action中,必须准备3个属性,分别是 * File doc; * String docFileName; * String docContentType; * 属性名字不能使用其他的,必须基于“doc" * 然后为这3个属性提供getter setter * * @author Administrator */
    public class UploadAction {
        
        File doc;
        String docFileName;
        String docContentType;
        
        /** * 实际上传方法 * 这里并不是真正的上传,而是打印了一下上传的文件信息 * 

    * 接着就为upload路径配置UploadAction,并返回success.jsp * * @return */ public String upload() { System.out.println(doc); System.out.println(docFileName); System.out.println(docContentType); return "success"; } public File getDoc() { return doc; } public void setDoc(File doc) { this.doc = doc; } public String getDocFileName() { return docFileName; } public void setDocFileName(String docFileName) { this.docFileName = docFileName; } public String getDocContentType() { return docContentType; } public void setDocContentType(String docContentType) { this.docContentType = docContentType; } }

  3. 配置struts.xml

    <struts>
        <action name="upload" class="vip.javer.action.UploadAction" method="upload">
            <result name="success">uploadSuccess.jspresult>
        action>
    struts>
    
  4. 准备uploadSuccess.jsp

    <%--
      Created by IntelliJ IDEA.
      User: Administrator
      Date: 2019/2/12
      Time: 8:23
      在这里添加文件的描述
    --%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
             pageEncoding="UTF-8" isELIgnored="false" %>
    
    
    
        uploadSuccess
    
    
    

    上传成功

    ${doc}
    ${docFileName}
    ${docContentType}
  5. 测试

    struts2 学习_第2张图片

上传文件最大值

struts上传文件的大小默认是比较小的只有2M,可以进行设置

<struts>
    
    <constant name="struts.multipart.maxSize" value="104857600"/>
struts>

标签

form标签

与jstl标准标签库类似的,struts有专属标签库
form标签用于提交数据

  1. 修改hide.jsp \ show.jsp 的表单上传框为

    <%@ taglib prefix="s" uri="/struts-tags" %>
    
    
      
      
    
    

会发现自动转换为了html的表单代码

<form id="addProduct" name="addProduct" action="/struts_war_exploded/addProduct.action" method="post">
<table class="wwFormTable">
    <tr>
    <td class="tdLabel"><label for="addProduct_product_name" class="label">product name:label>td>
    <td ><input type="text" name="product.name" value="OnePlus A5000" id="addProduct_product_name"/>td>
tr>

    <tr>
    <td colspan="2"><div align="right"><input type="submit" id="addProduct_0" value="Submit"/>
div>td>
tr>

table>form>

iterator标签

与JSTL标准标签库的c:forEach类似的,struts也提供了一个s:iterator用于遍历一个集合中的数据

为ProductAction增加list方法

  1. 为ProductAction增加一个products属性,类型是List,并提供getter setter

    private List<Product> products;
    
    public List<Product> getProducts() {
        return products;
    }
    
    public void setProducts(List<Product> products) {
        this.products = products;
    }
    
  2. 为ProductAction增加一个list()方法,为products添加3个product对象,并返回“list"

    public String list() {
        products = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            Product p = new Product();
            p.setId(i);
            p.setName("商品第" + i + "号");
            p.setAge(i * 10);
            products.add(p);
        }
        return "list";
    }
    
  3. struts.xml配置

    <action name="listProduct" class="vip.javer.action.ProductAction" method="list">
        <result name="list">listProduct.jspresult>
    action>
    
  4. listProduct.jsp

<%@ taglib prefix="s" uri="/struts-tags" %>
<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2019/2/12
  Time: 8:49

  --------------------------
    使用s:iterator标签进行遍历
    value 表示集合
    var 表示遍历出来的元素
    st 表示遍历出来的元素状态
    st.index 当前行号 基0
    st.count 当前行号 基1
    st.first 是否是第一个元素
    st.last 是否是最后一个元素
    st.odd 是否是奇数
    st.even 是否是偶数
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" isELIgnored="false" %>



    listProduct
    



        
id name st.index st.count st.first st.last st.odd st.even
${p.id} ${p.name} ${st.index} ${st.count} ${st.first} ${st.last} ${st.odd} ${st.even}
  1. 测试

struts2 学习_第3张图片

check标签

遍历3个product成为checkbox
并且第2个和第3个是默认选中的

private List<Integer> selectedProducts;

public String list() {
    selectedProducts = new ArrayList();
    
    for (int i = 0; i < 3; i++) {
        if ((i + 1) == 2 || (i + 1) == 3) {
            selectedProducts.add(i);
        }
        products.add(p);
    }
}

除了前例中准备的products
再新增一个属性ListselectedProducts,用于存放哪些产品被选中了
注意: ListselectedProducts 里放的是id,而不是对象


radio标签

遍历products成为radio,并选中第二个

在前例checkbox标签的基础上增加s:radio标签
value表示:哪项被选中
name表示:提交到服务端用的名称
list:用于遍历的集合
listValue:显示的radio的名称
listKey:radio的value


select 标签

遍历products成为select标签
默认选中第2个和第3个

使用s:select标签
name表示:提交到服务端用的名称
list:用于遍历的集合
listKey:每个option的value
listValue:显示的名称
multiple:true表示可以选中多行
size="3"表示默认显示3行
value表示:哪些被选中

注: 可以增加一个属性 theme=“simple” 使得最后生成的最简单的风格的html,否则就会有一些奇奇怪怪的tr td
或者直接在struts.xml中加一句 Servlet下载文件

注: 如果要增加class,需要使用属性: cssClass


多重迭代

有部分业务需求需要遍历list中的list
比如当前页面需要显示多个category,每个分类下又对应多个product

  1. Category除了有id和name属性外,还有List属性 表示category和product是一对多关系

    package vip.javer.bean;
    
    import java.util.List;
    
    /** * @author Administrator */
    public class Category {
        private int id;
        private String name;
        private List<Product> products;
        
        public int getId() {
            return id;
        }
        
        public void setId(int id) {
            this.id = id;
        }
        
        public String getName() {
            return name;
        }
        
        public void setName(String name) {
            this.name = name;
        }
        
        public List<Product> getProducts() {
            return products;
        }
        
        public void setProducts(List<Product> products) {
            this.products = products;
        }
    }
    
  2. 在ProductAction -> list()中准备数据 两个category,并且每个category对应3个product

    private List<Category> categories;
    public List<Category> getCategories() {
        return categories;
    }
    
    public void setCategories(List<Category> categories) {
        this.categories = categories;
    }
    public String list() {
           products = new ArrayList<>();
           selectedProducts = new ArrayList();
           categories = new ArrayList<>();
           for (int i = 0; i < 2; i++) {
               Category category = new Category();
               category.setId(i);
               category.setName("分类" + i);
               List<Product> products = new ArrayList<>();
               for (int j = 0; j < 3; j++) {
                   Product p = new Product();
                   p.setId(i);
                   p.setName("商品第" + (i + 1) + "号");
                   p.setAge(i * 10);
                   products.add(p);
               }
               category.setProducts(products);
               categories.add(category);
           }
    }
    
  3. 展示


        
id name products
${c.id} ${c.name} ${p.id} - ${p.name} - ${p.age}

调试

开启调试

导入jar包struts2-config-browser-plugin-2.2.3.1.jar

访问config-browser/actionNames即可

查看action到底有没有传递数据

有的时候jsp上不显示action传递过来的数据,有可能的原因是action并没有传递任何数据

这个时候我们就需要工具来查看action到底有没有传递数据

修改办法很简单,直接增加s:debug


通配符匹配链接

学习到目前为止,add,show, list分别需要进行配置
通过通配符匹配可以把这3个配置整合在一个配置中实现

<action name="*Product" class="vip.javer.action.ProductAction" method="{1}">
    <result name="show">show.jspresult>
    <result name="hide">hide.jspresult>
    <result name="success">show.jspresult>
    <result name="list">listProduct.jspresult>
action>

这样就能满足多个需求

解释一下xml配置

name="*Product"表示匹配所有以Product结尾的请求
method="{1}"表示以*位置的内容作为方法,比如name = "showProduct"那么就是method = "show"

<result name="show">show.jspresult>
<result name="hide">hide.jspresult>
<result name="success">show.jspresult>
<result name="list">listProduct.jspresult>

表示对应的方法返回值跳转对应页面

拦截器

拦截器可以简单地看成是Struts中的"filter"
拦截器可以拦截指定的Action,并且对Action进行相应的操作
在本例里,拦截了ProductAction,并且注入了当前时间

  1. 在ProductAction添加date属性

  2. 在显示界面追加${date}

  3. 创建拦截器

    package vip.javer.interceptor;
    
    import com.opensymphony.xwork2.ActionInvocation;
    import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
    import vip.javer.action.ProductAction;
    
    import java.util.Date;
    
    /** * 时间拦截器 * * @author Administrator */
    public class DateInterceptor extends AbstractInterceptor {
        @Override
        public String intercept(ActionInvocation actionInvocation) throws Exception {
            ProductAction productAction = (ProductAction) actionInvocation.getAction();
            productAction.setDate(new Date());
            return actionInvocation.invoke();
        }
    }
    
  4. 配置struts.xml

    <package name="basicStruts" extends="struts-default">
        
        <interceptors>
            <interceptor name="dateInterptor" class="vip.javer.interceptor.DateInterceptor"/>
        interceptors>
        <action name="*Product" class="vip.javer.action.ProductAction" method="{1}">
            
            <interceptor-ref name="dateInterptor"/>
            
            <interceptor-ref name="defaultStack"/>
        action>
    package>
    

5.测试

客户端跳转

struts默认是服务端跳转,想要实现客户端302跳转很简单

在struts.xml中的result标签加入`type="redirect"即可

客户端跳转带参数

这个也很简单,首先准备好action的属性,例如name,然后get\set,然后在方法内赋值,再在result标签的内容区域追加即可,例如index.jsp?name=${name}

String name;
get/set
public void show(){
    name = "233";
}
<result name="show" type="redirect">show.jsp?name=${name}result>

获取参数也很简单,上面的方法会直接展示url,还可以在页面获取

${param.name}

表单验证

  1. 在ProductAction中添加validate方法
public void validate() {
    if (product.getName().length() == 0) {
        addFieldError("product.name", "昵称不能为空");
    }
}
  1. 配置struts.xml
    比如我这里是表单的验证,那么就需要在addProduct的action下配置result
<result name="input">add.jspresult>

这个input可以说是内置的方法吧,只管用就好了
大概意思就是如果在addProduct提交表单时出现错误返回到add.jsp

  1. 显示错误信息

xml实现表单验证

  1. 去掉validate()方法

  2. 新建文件{ActionName}-validation.xml文件,一定要放在ActionName相同包下面

    
    <validators>
        <validator type="requiredstring">
            <param name="fieldname">product.nameparam>
            <message>使用xml方式的提示,昵称不能为空message>
        validator>
    validators>
    
  3. 重启测试

Action是多实例的

测试只需要多访问几次,然后在action的无参构造打印this就可以看到了

public ProductAction() {
    System.out.println(this);
}

struts2 学习_第4张图片

注解

以上的教程都是基于XML进行配置的,除此之外,Struts还能够基于注解进行配置

  1. 为了支持注解,需要导包

    1. 为了使struts支持注解,需要用到struts2-convention-plugin-x.x.x.jar 这个jar包,在前面的教程中是没有使用的,所以这里需要从右侧下载
    2. 下载好了之后,放在WEB-INF/lib 下
    3. 不仅如此,还要在项目导入jar,以使得eclipse能够编译通过
  2. 注释掉struts.xml的内容

  3. 在对应的Action类追加三个注解

    1. @Namespace("/")
    2. @ParentPackage(“struts-default”)
    @Results({
            @Result(name = "show", location = "/showTime.jsp")
    })
    
  4. 在对应的方法追加@Action(“showTime”)

其他常用注解

Namespace:指定命名空间。
ParentPackage:指定父包。

Result:提供了Action结果的映射。(一个结果的映射)
Results:“Result”注解列表
ResultPath:指定结果页面的基路径。

Action:指定Action的访问URL。
Actions:“Action”注解列表。

ExceptionMapping:指定异常映射。(映射一个声明异常)
ExceptionMappings:一级声明异常的数组。

InterceptorRef:拦截器引用。
InterceptorRefs:拦截器引用组。

一般说来,不是所有的注解都会用到,真正用到哪个的时候再来查一下就知道怎么回事了。

你可能感兴趣的:(全新学习)