蒋振飞的博客 - Java进阶02:Struts2教程   
正在加载蒋振飞的博客...
V3.0
蒋振飞的博客

Java进阶02:Struts2教程

发布时间: 2019年01月01日 发布人: 蒋振飞 热度: 319 ℃ 评论数: 0

一、Struts2的概述

1.什么是Struts2?

    Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个Servlet,在MVC设计模式中,Struts2作为控制器(controller)来建立模型与视图的数据交互。

2.Struts1与Struts2

    1) Struts1

        Struts1是最早基于MVC模式的轻量级Web框架,它能够合理地划分代码结构,并包含验证框架、国际化框架等多种实用工具框架。

    2) Struts2

        实质上,Struts2是以WebWork为核心,采用拦截器的机制处理用户请求,使得业务逻辑控制器能够与ServletAPI完全脱离开。

3.下载Struts2

    官网:https://struts.apache.org

4..Struts2压缩包结构

apps        :Struts2提供的应用,war文件:web项目打成war包。直接放入到tomcat可以允许。
docs        :Struts2的开发文档和API
lib         :Strtus2框架的开发的jar包
src         :Struts2的源码

5.前端控制器模型

    在传统开发中,一个请求就会对应一个Servlet,也可以抽取一个Servlet作为BaseServlet,这样使得Servlet数量过于庞大。而Struts2将所有的请求都先经过一个前端控制器,在前端控制器中实现框架的部分功能,剩下的具体操作提交到具体的Action中。其中过滤器就是很好的实现前端控制器的一种方式。

二、Structs2快速入门

1.引入jar包

    如果是进行Struts2的基本开发,可以参考apps下的struts2-blank.war下lib中有关jar的引用,其他jar需要看情况引用。

2.创建一个Jsp页面

<h1>Struts2的入门</h1>
<h3><a href="${ pageContext.request.contextPath }/hello.action">Struts2的入门</a></h3>

3.编写Action类

public class HelloAction {
 
    /**
     * 共有的返回值是String类型,在方法名为execute方法中不能传递参数
     */
    public String execute(){
        System.out.println("HelloAction执行了...");
        return "success";
    }
}

4.对Action进行配置

    在src下创建(提供)名称叫做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>
    <!-- Struts2为了管理Action的配置,通过包进行管理。 -->
    <!-- 配置Struts2的包 ================ -->
    <package name="demo1" extends="struts-default" namespace="/">
        <!-- 配置Action================ -->
        <action name="hello" class="com.jzfblog.struts.demo1.HelloAction" >
            <!-- 配置页面的跳转=========== -->
            <result name="success">/demo1/success.jsp</result>
        </action>
    </package>
</struts>

5.配置前端控制器(核心过滤器)

<!-- 配置Struts2的核心过滤器 -->
<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>

6.执行流程

    当用户访问某一个Action的时候,先经过核心过滤器,在核心过滤器中执行一组拦截器(这组拦截器实现部分功能),执行目标Action,根据Action的返回值,进行页面跳转。

三、Struts2常见配置

1.配置文件的加载顺序

    注意:后配置的常量的值会覆盖先配置的常量的值。

init_DefaultProperties()                ----加载default.properties
init_TraditionalXmlConfigurations();    ----加载struts-default.xml、struts-plugin.xml、struts.xml
init_LegacyStrutsProperties();          ----加载struts.properties
init_CustomConfigurationProviders();    ----加载配置提供类
init_FilterInitParameters() ; // [6]    ----加载web.xml中过滤器初始化参数
init_AliasStandardObjects() ; // [7]    ----加载Bean对象

2.Action的配置

    1) package相关配置

        package标签称为包,这个包与Java中的包的概念不一致。包为了更好管理action的配置。

name        :包的名称,只有在一个项目中不重名即可。
extends     :继承哪个包,通常值为struts-default。
namespace   :名称空间,与<action>标签中的name属性共同决定访问路径。
名称空间有三种写法:
    带名称的名称空间        :namespace=”/aaa” 
    根名称空间             :namespance=”/”
    默认名称空间           :namespace=””
abstract                 :抽象的,用于其他包的继承。

    2) action相关配置

name            :与namespace共同决定访问路径
class           :Action类的全路径
method          :执行Action中的哪个方法的方法名,默认值execute
converter       :用于设置类型转换器

3.常量的配置

    1) 在Struts2的框架中,提供了非常多的常量:(在default.properties)

struts.i18n.encoding=UTF-8          ----Struts2中所有的post请求的中文乱码不用处理。
struts.action.extension=action,,       ----Struts2请求的默认的扩展名。默认扩展名是.action或者什么都不写。

    2) 修改常量的值,可以有三个位置进行修正

        ①struts.xml中进行修改

<!-- 配置Struts2的常量 -->
<constant name="struts.action.extension" value="action"/>

        ②struts.properties中进行修改

struts.action.extension=action

        ③web.xml中进行修改

<!-- 配置Struts2的核心过滤器 -->
<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    <!-- 修改常量 -->
    <init-param> 
        <param-name>struts.action.extension</param-name>
        <param-value>xyz</param-value> 
    </init-param> 
</filter>

4.分模块开发的配置

    ①include的配置

<include file="com/jzfblog/struts/demo1/struts_demo1.xml"/>

四、Action的访问

1.Action的写法

    1) Action类是POJO的类

        Action可以不继承特殊的类或不实现任何特殊的接口,仅仅是一个POJO,POJO全称是Plain Oridinary Java Object(简单的java对象)。

public class ActionDemo1 {
 
    public String execute(){
        System.out.println("ActionDemo1执行了...");
        return null;
    }
}

    2) Action类实现Action的接口

        为了让用户开发的Action更规范,Struts2提供一个Action接口,用户在实现Action控制类时,可以实现Struts2提供的这个Action接口。

public class ActionDemo2 implements Action{
 
    @Override
    public String execute() throws Exception {
        System.out.println("ActionDemo2执行了...");
        return NONE;
    }
}

        Action接口中提供了5个已经定义的常量如下

SUCCESS         :success,代表成功
NONE            :none,代表页面跳转不成功
ERROR           :error,代表跳转到错误页面
INPUT           :input,数据校验的时候跳转的路径
LOGIN           :login,用来跳转到登录页面

    3) Action类继承ActionSupport类

        ActionSupport类本身实现了Action接口,是Struts2中默认的Action接口的实现类,所以继承ActionSupport就相当于Action接口。ActionSupport类还实现了Validateable、ValidationAware、TextProvider、LocaleProvider和Serializable等接口,为用户提供更多的功能。

public class ActionDemo3 extends ActionSupport{
 
    @Override
    public String execute() throws Exception {
        System.out.println("ActionDemo3执行了...");
        return NONE;
    }
}

2.Action的访问

    一个请求现在对应一个Action,那么如果请求很多对应很多个Action,为了减少代码编写,最好是让一个模块的操作提交到一个Action中。

    1) 通过method方法

        使用method方法,同一个Action类就被配置了好多次,只是修改了后面method值。

<h1>Action的访问</h1>
<h3>通过method方式</h3>
<a href="${ pageContext.request.contextPath }/userFind.action">查询用户</a><br/>
<a href="${ pageContext.request.contextPath }/userUpdate.action">修改用户</a><br/>
<a href="${ pageContext.request.contextPath }/userDelete.action">删除用户</a><br/>
<a href="${ pageContext.request.contextPath }/userSave.action">保存用户</a><br/>
 
<action name="userFind" class="com.jzfblog.struts.demo3.UserAction" method="find"></action>
<action name="userUpdate" class="com.jzfblog.struts.demo3.UserAction" method="update"></action>
<action name="userDelete" class="com.jzfblog.struts.demo3.UserAction" method="delete"></action>
<action name="userSave" class="com.jzfblog.struts.demo3.UserAction" method="save"></action>

    2) 通过通配符配置完成

        在action的name属性中使用的代表任意字符,method中的{1}代表name属性中的出现的第一个所代替的字符。

<h3>通过通配符的方式</h3>
<a href="${ pageContext.request.contextPath }/product_find.action">查询商品</a><br/>
<a href="${ pageContext.request.contextPath }/product_update.action">修改商品</a><br/>
<a href="${ pageContext.request.contextPath }/product_delete.action">删除商品</a><br/>
<a href="${ pageContext.request.contextPath }/product_save.action">保存商品</a><br/>
 
<!-- 通配符的方式 -->
<action name="product_*" class="com.jzfblog.struts.demo3.ProductAction" method="{1}"></action>

    3) 通过动态方法访问

        动态方法访问默认在Struts2中默认是不开启的,如果想要使用需要先去开启一个常量。

<!-- 开启动态方法访问 -->
<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
 
<h3>通过动态方法访问的方式</h3>
<a href="${ pageContext.request.contextPath }/customer!find.action">查询客户</a><br/>
<a href="${ pageContext.request.contextPath }/customer!update.action">修改客户</a><br/>
<a href="${ pageContext.request.contextPath }/customer!delete.action">删除客户</a><br/>
<a href="${ pageContext.request.contextPath }/customer!save.action">保存客户</a><br/>
 
<!-- 动态方法访问的方式 -->
<action name="customer" class="com.jzfblog.struts.demo3.CustomerAction"></action>

五、Servlet的API访问

1.完全解耦合方式

    注意:这种方式只能获得代表request、session、application的数据的Map集合,不能操作这些对象的本身的方法。

public class RequestDemo1 extends ActionSupport{
 
    @Override
    public String execute() throws Exception {
        // 一、接收参数:
        // 利用Struts2中的对象ActionContext对象.
        ActionContext context = ActionContext.getContext();
        // 调用ActionContext中的方法。
        // 类似于Map<String,String[]> request.getParameterMap();
        Map<String,Object> map = context.getParameters();
        for (String key : map.keySet()) {
            String[] values = (String[]) map.get(key);
            System.out.println(key+"    "+Arrays.toString(values));
        }
 
        // 二、向域对象中存入数据
        context.put("reqName", "reqValue");// 相当于request.setAttribute();
        context.getSession().put("sessName", "sessValue"); // 相当于session.setAttribute();
        context.getApplication().put("appName", "appValue"); // 相当于application.setAttribute();
 
        return SUCCESS;
    }
}

2.使用Servlet的API的原生方式

    注意:这种方式可以操作域对象的数据,同时也可以获得对象的方法。

public class RequestDemo2 extends ActionSupport {
 
    @Override
    public String execute() throws Exception {
        // 一、接收数据
        // 直接获得request对象,通过ServletActionContext
        HttpServletRequest request = ServletActionContext.getRequest();
        Map<String, String[]> map = request.getParameterMap();
        for (String key : map.keySet()) {
            String[] values = map.get(key);
            System.out.println(key+"    "+Arrays.toString(values));
        }
 
        // 二、向域对象中保存数据
        // 向request中保存数据:
        request.setAttribute("reqName", "reqValue");
        // 向session中保存数据
        request.getSession().setAttribute("sessName", "sessValue");
        // 向application中保存数据
        ServletActionContext.getServletContext().setAttribute("appName", "appValue");
        return SUCCESS;
    }
}

3.接口注入的方式

    Servlet是单例的,多个程序访问同一个Servlet只会创建一个Servlet的实例。Action是多例的,一次请求,创建一个Action的实例(不会出现线程安全的问题)。

public class RequestDemo3 extends ActionSupport implements ServletRequestAware,ServletContextAware{
 
    private HttpServletRequest request;
    private ServletContext context;
 
    public RequestDemo3() {
        super();
        System.out.println("RequestDemo3被创建了...");
    }
 
    @Override
    public String execute() throws Exception {
        // 一、接收参数
        // 通过接口注入的方式获得request对象。
        Map<String, String[]> map = request.getParameterMap();
        for (String key : map.keySet()) {
            String[] values = map.get(key);
            System.out.println(key+"    "+Arrays.toString(values));
        }
        // 二、向域对象中保存数据
        // 向request域中保存数据
        request.setAttribute("reqName", "reqValue");
        // 向session中保存数据:
        request.getSession().setAttribute("sessName", "sessValue");
        // 向application中保存数据:
        context.setAttribute("appName", "appValue");
 
        return super.execute();
    }
 
    @Override
    public void setServletRequest(HttpServletRequest request) {
        this.request = request;
    }
 
    @Override
    public void setServletContext(ServletContext context) {
        this.context = context;
    }
}

六、result标签的配置

1.result标签的配置

    result标签用于配置页面的跳转。在result标签上有两个属性:

name属性  :逻辑视图的名称。默认值:success
type属性  :页面跳转的类型。
    dispatcher      :默认值,请求转发。(Action转发JSP)
    redirect        :重定向。(Action重定向JSP)
    chain           :转发。(Action转发Action)
    redirectAction  :重定向。(Action重定向Action)
    stream          :Struts2中提供文件下载的功能。

2.结果页面的配置

    1) 全局结果页面

        在包中配置一次,其他的在这个包中的所有的action只要返回了这个值,都可以跳转到这个页面。

<!-- 全局结果页面 -->
<global-results>
    <result>/demo1/demo2.jsp</result>
</global-results>

    2) 局部结果页面

        只能在当前的action中的配置有效。

<action name="requestDemo1" class="com.jzfblog.struts2.demo1.RequestDemo1">
    <result type="redirect">/demo1/demo2.jsp</result>
</action>

七、Struts2的数据封装

1.属性驱动:提供属性set方法的方式(不常用)

    1) Jsp代码

<h3>方式一:属性驱动-提供set方法的方式</h3>
<s:fielderror/>
<form action="${ pageContext.request.contextPath }/userAction1.action" method="post">
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="password" name="password"><br/>
    年龄:<input type="text" name="age"/><br/>
    生日:<input type="text" name="birthday"/><br/>
    工资:<input type="text" name="salary"/><br/>
    <input type="submit" value="提交">
</form>

    2) Action代码

public class UserAction1 extends ActionSupport {
    // 提供了对应的属性
    private String username;
    private String password;
    private Integer age;
    private Date birthday;
    private Double salary;
    // 提供属性对应的set方法
    public void setUsername(String username) {
        this.username = username;
    }
 
    public void setPassword(String password) {
        this.password = password;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
 
    public void setSalary(Double salary) {
        this.salary = salary;
    }
 
    @Override
    public String execute() throws Exception {
        // 接收数据:
        System.out.println(username);
        System.out.println(password);
        System.out.println(age);
        System.out.println(birthday);
        System.out.println(salary);
        // 封装数据:
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        user.setAge(age);
        user.setBirthday(birthday);
        user.setSalary(salary);
        return NONE;
    }
}

2.属性驱动:页面中提供表达式方式

    1) Jsp代码

<h3>方式二:属性驱动-在页面中提供表达式方式</h3>
<form action="${ pageContext.request.contextPath }/userAction2.action" method="post">
    用户名:<input type="text" name="user.username"/><br/>
    密码:<input type="password" name="user.password"><br/>
    年龄:<input type="text" name="user.age"/><br/>
    生日:<input type="text" name="user.birthday"/><br/>
    工资:<input type="text" name="user.salary"/><br/>
         <input type="submit" value="提交">
</form>

    2) Action代码

public class UserAction2 extends ActionSupport {
 
    // 提供一个User对象:
    private User user;
    // 提供user的set和get方法:一定要提供get方法。
    // 因为拦截器完成数据封装,需要创建User对象。通过get方法可以获得同一个对象,将数据封装到同一个对象中。
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    @Override
    public String execute() throws Exception {
        System.out.println(user);
        return NONE;
    }
}

3.模型驱动:采用模型驱动方式(最常用)

    模型驱动方式最常用的方式:缺点:只能同时向一个对象中封装数据,使用第二种可以向多个对象中同时封装数据。

    1) Jsp代码

<h3>方式三:模型驱动-模型驱动方式</h3>
<form action="${ pageContext.request.contextPath }/userAction3.action" method="post">
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="password" name="password"><br/>
    年龄:<input type="text" name="age"/><br/>
    生日:<input type="text" name="birthday"/><br/>
    工资:<input type="text" name="salary"/><br/>
    <input type="submit" value="提交">
</form>

    2) Action代码

public class UserAction3 extends ActionSupport implements ModelDriven<User>{
 
    // 模型驱动使用的对象:前提必须手动提供对象的实例
    private User user = new User(); // 手动实例化User.
    @Override
    // 模型驱动需要使用的方法:
    public User getModel() {
        return user;
    }
    @Override
    public String execute() throws Exception {
        System.out.println(user);
        return NONE;
    }
}

八、Struts2的复杂类型的数据封装

1.封装到List集合中

    1) Jsp代码

<h3>封装到List集合中:批量插入商品</h3>
<form action="${ pageContext.request.contextPath }/productAction1.action" method="post">
    商品名称:<input type="text" name="products[0].name"><br/>
    商品价格:<input type="text" name="products[0].price"><br/>
    商品名称:<input type="text" name="products[1].name"><br/>
    商品价格:<input type="text" name="products[1].price"><br/>
    商品名称:<input type="text" name="products[2].name"><br/>
    商品价格:<input type="text" name="products[2].price"><br/>
    <input type="submit" value="提交">
</form>

    2) Action代码

public class ProductAction1 extends ActionSupport {
 
    private List<Product> products;
    // 提供集合的set方法:
    public void setProducts(List<Product> products) {
        this.products = products;
    }
    public List<Product> getProducts() {
        return products;
    }
 
    @Override
    public String execute() throws Exception {
        for (Product product : products) {
            System.out.println(product);
        }
        return NONE;
    }
}

2.封装到Map集合中

    1) Jsp代码

<h3>封装到Map集合中:批量插入商品</h3>
<form action="${ pageContext.request.contextPath }/productAction2.action" method="post">
    商品名称:<input type="text" name="map['one'].name"><br/>
    商品价格:<input type="text" name="map['one'].price"><br/>
    商品名称:<input type="text" name="map['two'].name"><br/>
    商品价格:<input type="text" name="map['two'].price"><br/>
    商品名称:<input type="text" name="map['three'].name"><br/>
    商品价格:<input type="text" name="map['three'].price"><br/>
    <input type="submit" value="提交">
</form>

    2) Action代码

public class ProductAction2 extends ActionSupport {
 
    private Map<String,Product> map;
 
    public Map<String, Product> getMap() {
        return map;
    }
 
    public void setMap(Map<String, Product> map) {
        this.map = map;
    }
 
    @Override
    public String execute() throws Exception {
        for (String key : map.keySet()) {
            Product product = map.get(key);
            System.out.println(key+"    "+product);
        }
        return NONE;
    }
}

九、文件上传

1.增加文件上传input框

<input type="file" name="upload" />

2.在form表单中,新增enctype属性

<form action="${pageContext.request.contextPath }/customer_save.action" method=post enctype="multipart/form-data">
</form>

3.在CustomerAction.java文件中,新增三个属性值,并设置set方法

private String uploadFileName; // 上传的文件名称,而且注意"upload"必须与表单中的name一致
private String uploadContentType; // 上传的文件类型
private File upload; // 上传的文件

4.创建UploadUtils.java,添加getUuidFileName和getPath方法

/**
 * 解决文件名重复问题
 * @param fileName
 * @return
 */
public static String getUuidFileName(String fileName) {
    // 找出符号"."的位置
    int idx = fileName.indexOf("."); // aa.txt
    // 获取后缀
    String lastName = fileName.substring(idx); //.txt
    // 返回随机生成的uuid+后缀
    return UUID.randomUUID().toString().replace("-", "") + lastName;
}
 
/**
 * 使用目录分离的方法
 * @return
 */
public static String getPath(String uuidFileName) {
    // 获取文件名的哈希值
    int hashCode1 = uuidFileName.hashCode();
    int hashCode2 = hashCode1 >>> 4;
    // 作为一级目录
    int d1 = hashCode1 & 0xf;
    // 作为二级目录
    int d2 = hashCode2 & 0xf;
    return "/" + d1 + "/" + d2;
}

5.修改CustomerAction中的save方法

/**
 * 保存客户
 * @throws IOException 
 */
public String save() throws IOException{
    // 上传图片
    if(upload != null) {
        // 设置文件上传路径
        String path = "D:/upload";
        // 为了避免上传文件名相同,所以采用随机文件名
        String uuidFileName = UploadUtils.getUuidFileName(uploadFileName);
        // 一个目录存放文件过多,目录分离
        String realPath = UploadUtils.getPath(uuidFileName);
        // 创建目录
        String dir = path + realPath;
        File file = new File(dir);
        if(!file.exists()) {
            file.mkdirs();
        }
        // 文件上传
        File destFile = new File(dir+"/"+uuidFileName);
        FileUtils.copyFile(upload, destFile);
        // 设置cust_image属性值
        customer.setCust_image(dir+"/"+uuidFileName);
    }
    // 保存客户
    customerService.save(customer);
    return SUCCESS; 
}

5.对上传的文件进行限定,在struts.xml增加拦截器

<!-- 5M的上传容量 -->
<constant name="struts.multipart.maxSize" value="5242880" />

<!-- 在action中增加如下内容,而且必须配置input视图 -->
<result name="input">/jsp/customer/add.jsp</result>
<interceptor-ref name="defaultStack">
    <param name="fileUpload.maximumSize">2097152</param>
    <param name="fileUpload.allowedExtensions">.jpg</param>
</interceptor-ref>

6.在前端页面添加错误回显

<s:actionerror/>
<s:fielderror />

十、OGNL概述

1.什么是OGNL?

    OGNL的全称是对象图导航语言(Object-Graph Navigation Language),它是一种功能强大的表达式语言。

2.OGNL的入门

    1) 支持对象方法调用,如:objName.methodName()

public void demo1() throws OgnlException {
    // 获得context
    OgnlContext context = new OgnlContext();
    // 获得根对象
    Object root = context.getRoot();
    // 执行表达式:
    Object value = Ognl.getValue("'helloworld'.length()", context, root);
    System.out.println(value );
}

    2) 支持类静态方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名|值名],如:@java.lang.Math@random()

public void demo2() throws OgnlException {
    // 获得context
    OgnlContext context = new OgnlContext();
    // 获得根对象
    Object root = context.getRoot();
    // 执行表达式:@类名@方法名
    Object value = Ognl.getValue("@java.lang.Math@random()", context, root);
    System.out.println(value);
}

3.OGNL获取数据

public void demo3() throws OgnlException{
    OgnlContext context = new OgnlContext();
 
    // 获取OgnlContext中的数据
    context.put("name","张三");
    String name = (String) Ognl.getValue("#name",context,context.getRoot());
    System.out.println(name);
 
    // 获取Root中的数据:不需要加"#"号
    User user = new User();
    user.setName("李四");
    context.setRoot(user);
    String name = (String) Ognl.getValue("name",context,context.getRoot());
    System.out.println(name);
}

4.struts2中的OGNL

    1) 在jsp中,需要引入struts的taglib

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

    2) 调用对象的方法

<s:property value="'struts'.length()"/>

    3) 调用对象的静态方法

<!-- 默认是不允许访问静态方法,需要开启一个常量 -->
<!-- struts.ognl.allowStaticMethodAccess=false -->
<s:property value="@java.lang.Math@random()"/>

十一、值栈

1.什么是值栈

    ValueStack是一个Struts的接口,字面意义为值栈,OgnlValueStack是ValueStack的实现类,客户端发起一个请求struts2架构会创建一个action实例,同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿Action的生命周期,struts2使用OGNL将请求Action的参数封装为对象存储到值栈中,并通过OGNL表达式读取值栈中的对象属性值。

2.值栈的内部结构

    ValueStack中主要分为两个区域,通常操作值栈指的是操作ValueStack中的root区域,获取context数据需要加"#号",获取root数据不需要加"#号"。

    1) context区域

        其实就是一个Map,里面一般放置web开发的常用对象数据的引用。

request
session
application
parameters
attr

    2) root区域

        其实就是一个ArrayList,里面一般放置对象。

3.获得值栈

    一个Action中获取的值栈都是相同的。

    1) 通过ActionContext对象获取值栈

ValueStack valueStack = ActionContext.getContext.getValueStack();

    2) 通过request对象获取值栈

ValueStack valueStack = ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

4.操作值栈

    1) 在Action中提供属性的get方法的方式

        默认的情况下,将Action对象压入到值栈。

        Jsp代码

<s:property value="user.username"/>
<s:property value="user.password"/>

        Action代码

public class ValueStackDemo01 extends ActionSupport {
 
    private User user;
 
    public User getUser() {
        return user;
    }
 
    @Override
    public String execute() throws Exception {
        // 向ValueStack中存值
        user = new User("java", "111");
        return SUCCESS;
    }
}

    2) 使用ValueStack中本身的一些方法的方式

        Jsp代码

<s:property value="username"/>
<s:property value="password"/>
<s:property value="name"/>

        Action代码

public class ValueStackDemo02 extends ActionSupport{
 
    @Override
    public String execute() throws Exception {
        // 获得值栈对象
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        // 使用push(object obj);set(String key, object obj);
        User user = new User("python", "222");
        // 压栈
        valueStack.push(user);
 
        valueStack.set("name","php");
 
        return SUCCESS;
    }
}

5.OGNL中特殊字符

    1) #号

        获取context的数据

<s:property value="#request.name">

        构建map集合

<s:iterator value="#{'aaa':'111','bbb':'222','ccc':'333'}" var="entery">
    <s:property value="key"/>---<s:property value="value"><br/>
    <s:property value="#entry.key"/>---<s:property value="#entry.value">
</s:iterator>
<s:radio list="#{'1':'男','2':'女'}" name="sex" label="性别"/>

    2) %号

        强制解析OGNL表达式

<s:textfield name="name" value="%{#request.name}"/>

        强制不解析OGNL表达式

<s:property value="%{'#request.name'}"/>

    3) $号

        在配置文件中使用OGNL表达式。

        属性文件

国际化:
message_zh_CN.properties
user.login=登录
user.welcome=欢迎,${#session.user.username}
message_en_US.properties
user.login=Login
user.welcome=Welcome,${#session.user.username}

        XML文件

文件下载
<action name="download" class="xxx.DownloadAction">
    <result type="stream">
        <param name="Content-Type">文件类型</param>
        <param name="Content-Disposition">attachment;filename=${文件名}</param>
    </result>
</action>

十二、Struts2拦截器

1.什么是拦截器?

    它与过滤器的区别在于过滤器是过滤客户端向服务器发送的请求,拦截器是拦截客户端对Action的访问,常指更细粒度化的拦截。

2.Struts2的执行流程

    ①客户端向服务器发送一个Action的请求,执行核心过滤器(doFilter)方法。
    ②在这个方法中,调用executeAction方法,在其内部又调用dispatcher.serviceAction()。
    ③创建了一个Action代理,最终执行的是Action代理中的execute(),在代理中执行的execute方法中调用ActionInvocation的invoke方法。
    ④接着在该方法内部递归执行一组拦截器(部分),如果没有了拦截器,就会执行目标Action,根据Action的返回结果进行页面跳转。

3.拦截器入门使用

    1) 编写拦截器类

        编写一个类实现Interceptor接口或者继承AbstractInterceptor类。

public class InterceptorDemo1 extends AbstractInterceptor{
 
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        System.out.println("InterceptorDemo1执行...");
        // invoke执行下一个拦截器
        String obj = invocation.invoke();
        System.out.println("InterceptorDemo1执行结束...");
        return obj;
    }
}

    2) 对拦截器进行配置

        定义拦截器进行配置。

<package name="demo1" extends="struts-default" namespace="/">
    <!-- 定义拦截器========== -->
    <interceptors>
        <interceptor name="interceptorDemo1" class="com.jzfblog.web.interceptor.InterceptorDemo1"/>
        <interceptor name="interceptorDemo2" class="com.jzfblog.web.interceptor.InterceptorDemo2"/>
    </interceptors> 
 
    <action name="actionDemo1" class="com.jzfblog.web.action.ActionDemo1">
        <result>/demo1/demo1.jsp</result>
 
        <interceptor-ref name="defaultStack"/>
        <interceptor-ref name="interceptorDemo1"/>
        <interceptor-ref name="interceptorDemo2"/>
    </action>
</package>

        定义一个拦截器栈的方式

<interceptors>
    <interceptor name="interceptorDemo1" class="com.jzfblog.web.interceptor.InterceptorDemo1"/>
    <interceptor name="interceptorDemo2" class="com.jzfblog.web.interceptor.InterceptorDemo2"/>
 
    <!-- 定义拦截器栈 -->
    <interceptor-stack name="myStack">
        <interceptor-ref name="defaultStack"/>
        <interceptor-ref name="interceptorDemo1"/>
        <interceptor-ref name="interceptorDemo2"/>
    </interceptor-stack>
 
</interceptors> 
<action name="actionDemo1" class="com.jzfblog.web.action.ActionDemo1">
    <result>/demo1/demo1.jsp</result>
 
    <interceptor-ref name="myStack"/>
</action>

十三、Struts2的标签库

1.标签库引用

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

2.通用标签库

    1) 判断标签

<s:set var="i" value="5" scope="request"/>
<s:if test="#request.i>3">
    i 大于 3
</s:if>
<s:elseif test="#request.i<3">
    i 小于 3
</s:elseif>
<s:else>
    i 等于 3
</s:else>

    2) 遍历标签

<s:iterator var="i" value="{'aa','bb','cc'}">
    <s:property value="#i"/>
</s:iterator>
 
<s:iterator var="entry" value="#{'aaa':'111','bbb':'222','ccc':'333'}">
    <s:property value="#entry.key"/>--<s:property value="#entry.value"/>
</s:iterator>
 
<s:iterator var="i" begin="1" end="10" step="1">
    <s:property value="#i"/>
</s:iterator>
 
<s:iterator var="i" begin="100" end="300" step="5" status="status">
    <s:if test="#status.count % 3 == 0">
        <font color="red"><s:property value="#i"/></font>
    </s:if>
    <s:else>
        <s:property value="#i"/>
    </s:else>
</s:iterator>

    3) 其他常用标签

<s:property/><s:debug/><s:date/>

3.UI标签库(方便数据回显)

    1) 传统的表单

<form action="${ pageContext.request.contextPath }/uiAction.action" method="post">
    <input type="hidden" name="id"/>
    用户名:<input type="text" name="name"/><br/>
    密码:<input type="password" name="password"><br/>
    年龄:<input type="text" name="age"><br/>
    性别:<input type="radio" name="sex" value="男">男
    <input type="radio" name="sex" value="女">女<br/>
    籍贯:<select name="city">
        <option value="">-请选择-</option>
        <option value="北京">北京</option>
        <option value="上海">上海</option>
        <option value="深圳">深圳</option>
        <option value="韩国">韩国</option>
    </select><br/>
    爱好:<input type="checkbox" name="hobby" value="basketball"/>篮球
    <input type="checkbox" name="hobby" value="football"/>足球
    <input type="checkbox" name="hobby" value="volleyball"/>排球
    <input type="checkbox" name="hobby" value="pingpang"/>乒乓球<br/>
    介绍:<textarea name="info" cols="8" rows="2"></textarea><br/>
    <input type="submit" value="提交"/>
</form>

    2) UI标签的表单

<s:form action="uiAction" namespace="/" method="post" theme="simple" >
    <s:hidden name="id" />
    <s:textfield name="name" label="用户名"/>
    <s:password name="password" label="密码" showPassword="true"/>
    <s:textfield name="age" label="年龄"/>
    <s:radio list="{'男','女'}" name="sex" label="性别"/>
    <s:select list="{'北京','上海','深圳','韩国'}" name="city" label="籍贯" headerKey="" headerValue="-请选择-"/>
    <s:checkboxlist list="#{'basketball':'篮球','football':'足球','volleyball':'排球','pingpang':'乒乓球'}" name="hobby" label="爱好"/>
    <s:textarea name="info" cols="8" rows="2" label="介绍" ></s:textarea>
    <s:submit value="提交"/>
</s:form>

打赏 蒋振飞

取消

感谢您的支持,我会继续努力的!

扫码支持
一分也是爱     一块不嫌多

点击 支付宝 或 微信 打赏蒋振飞

打开支付宝扫一扫,即可进行扫码打赏哦

评论列表