JavaWeb开发知识总结(六)-(struts2_ognl_valuestack_interceptor)

来源:互联网 发布:mac 用appleid 登陆 编辑:程序博客网 时间:2024/06/05 21:53

JavaWeb开发知识总结(struts2-ognl_valueStack_interceptor)

1. ognl表达式

​ OGNL是Object-Graph Navigation Language(对象图导航语言)的缩写,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。 类似EL表达式,但功能更强大。Ognl导航类似于Java中的链式编程,ognl表达式的导航本质是使用get和set方法进行获取/设置数据

ognl表达式的作用:

  1. 支持对象的操作,调用对象的方法;
  2. 支持静态成员访问;
  3. 支持赋值操作与表达串联;
  4. 访问OGNL上下文,访问OgnlContext对象;
  5. 操作集合对象。

ognl在struts2中的作用:

# ognl是表达式,和el表达式类似,el表达式用在jsp中获取域对象里面的值1. ognl也是一种表达式,比el表达式功能更加强大;2. 使用ognl主要作用:在struts2里面获取值栈中存储的数据。# ognl本身不是struts2一部分,而是单独项目,经常和struts2一起使用* 要使用ognl首先导入jar包,在struts2里面支持ognl,默认提供ognl的jar包。# 在struts2里面获取值栈数据* ognl在struts2里面和struts2标签一起使用,来获取值栈中的数据。

ognl作用案例:项目中需要导入ognl的jar包ognl-3.0.6.jar

// ognl表达式的作用:public class StrutsOgnlDemo1 {    @Test    /**     * 功能1: ognl操纵对象     * ognl表达式的三要素:表达式,ognlContext对象,root对象     */    public void demo1() throws OgnlException {        // 1.创建ognlContext上下文对象        OgnlContext context = new OgnlContext();        // 2.操作对象        // Object value = Ognl.getValue("'somnus'", context, new Object());        // 从root中获取的值是somnus字符串        // Object value = Ognl.getValue("'somnus'.length()", context, new        // Object()); // root中获取的值是somnus字符串的长度        // Object value = Ognl.getValue("#somnus", context, new Object());         // 从Context中获取的值是somnus属性,不存在返回null        // Object value = Ognl.getValue("somnus", context, new Object());         // 从root中获取的值是somnus属性,不存在时报错        // 在Context中有对root的引用        Object root = context.getRoot();        Object value = Ognl.getValue("somnus", context, root);        // 报错        System.out.println(value);    }    /**     * 功能2: ognl访问静态常量和静态方法     */    @Test    public void demo2() throws OgnlException {        // 1.获取Context对象        OgnlContext context = new OgnlContext();        // 2.获取root对象        Object root = context.getRoot();        // 3.执行表达式,访问静态常量        Object value = Ognl.getValue("@java.lang.Math@PI", context, root);        System.out.println(value);        // 访问静态方法        Object value2 = Ognl.getValue("@java.lang.Math@random()", context, root);        System.out.println(value2);    }    /**     * 功能3:ognl的Context对象     * Context对象本质是Map集合     * 访问Context中的数据需要加#     */    @Test    public void demo3() throws OgnlException{        // 1.获取Context对象        OgnlContext context = new OgnlContext();        // 2.获取root对象        Object root = context.getRoot();        // 3.向Context中存储数据        context.put("somnus", "sunmos");        // 4.通过表达式取出数据        Object value = Ognl.getValue("#somnus", context, root);        System.out.println(value);    }    /**     * 功能3:ognl的root对象     * root对象本质是list集合     * 访问Context中的数据不需要加#     */    @Test    public void demo4() throws OgnlException{        // 1.获取Context对象        OgnlContext context = new OgnlContext();        // 2.获取root对象,获取root对象,需要在context设置root后才能获取        // Object root = context.getRoot();        // 3.向Context中存储Map集合数据        Map<String, String> map = new HashMap<String, String>();        map.put("username", "somnus");        context.put("username", "sunmos");        // 将map设为root        context.setRoot(map);        Object root = context.getRoot();        // 4.通过表达式取出数据,取出root中的数据,不能加#        Object value = Ognl.getValue("username", context, root); // 值是somnus        // 4.通过表达式取出数据,取出context中的数据,不能加#        // Object value = Ognl.getValue("#username", context, root); // 值是sunmos        System.out.println(value); // 输出时somnus    }    /**     * 功能4:操作集合     */    @Test    public void demo5() throws OgnlException {        // 1.获取Context对象        OgnlContext context = new OgnlContext();        // 2.获取root        Object root = context.getRoot();        // 3.操作集合        Object value = Ognl.getValue("{'hello','good','well'}", context,root);        System.out.println(value.getClass()); // class java.util.ArrayList        // list集合的数据可以存储在root中        context.setRoot(value);        // 取出root中的数据        Object value2 = Ognl.getValue("[0]", context, root);// 取出List集合中索引为0的值        //相当于创建了一个Map集合,和上述创建List集合不能同时存在        // Object value3 = Ognl.getValue("#{'username':'tom','age':20}", context,context.getRoot());         // System.out.println(value3.getClass()); // class java.util.LinkedHashMap        // System.out.println(value3); // {username=tom, age=20}    }    /**     * 功能5:赋值及表达式串联     */    @Test    public void demo6() throws OgnlException {        // 1.获取上下文对象OgnlContext        OgnlContext context = new OgnlContext(); // 它就是一个java.util.Map        //2.操作ognl表达式        //相当于创建了一个Map集合        Object value = Ognl.getValue("#{'username':'tom','age':20}", context,context.getRoot());         // 将值设为root        context.setRoot(value);        // 取出root中的值及为root中值赋值        // Object value2 = Ognl.getValue("username,age=45", context,context.getRoot()); // 值为45        // Object value3 = Ognl.getValue("username", context,context.getRoot()); // 值为tom        Object value3 = Ognl.getValue("username='somnus'", context,context.getRoot()); //值为somnus        System.out.println(value3);    }}

struts2中使用ognl表达式:

​ 在struts2框架中我们使用ognl表达式的作用是从valueStack中获取数据。我们在struts2框架中可以使用ognl+valueStack达到在页面(jsp)上来获取相关的数据。在jsp页面上使用ognl表达式,就需要结合struts2框架的标签需要导入strutss2的标签库,struts2-tags,简写是s,在jsp页面使用< s:property value=”表达式”>来使用。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib uri="/struts-tags" prefix="s" %><html><head><title>ognl在struts2中的使用</title></head><body><!-- 获取somnus字符串的长度 --><s:property value="'somnus'.length()"/><!-- 获取Math类的常量值PI --><s:property value="@java.lang.Math@PI"/><!-- 获取Math类的随机数 --><s:property value="@java.lang.Math@random()"/></body></html>

2. valueStack值栈

​ 在Servlet中是通过域对象(3个域对象)向前端页面传递数据,将数据存储到域中,在前端页面jsp中通过EL表达式获取域对象中的数据。

​ 在struts2中action处理完数据后需要携带数据到前端页面上,struts2中使用valueStack值栈方式存储数据。valueStack本质是一个数据容器。valueStack类似Servlet的域对象,valueStack是存储在每一个action中,在前端页面jsp中可以通过ognl表达式(也可以是EL表达式)获取值栈中存储的数据。

2.1 Servlet与action的区别:(重点)

​ Servlet默认是在第一次被请求访问时创建对象,只创建一次,单实例对象,每次请求访问Servlet都会重新开启一个线程去执行service方法进行处理请求。

​ Action会在每次请求访问时都会重新创建一个对象,多实例对象

2.2 action的生命周期:(个人理解)

​ valueStack被设计成是一个接口com.opensymphony.xwork2.util.ValueStack,struts2基于ognl表达式提供了实现类com.opensymphony.xwork2.ognl.OgnlValueStack,根据查看action执行过程可知:action的生命周期

当客户端向发送一个请求,服务器就会创建一个Action来处理请求,每一次请求都会有一个新的action对应(多实例,不存在线程安全问题);Struts2会根据每一次的http请求来创建对应的ActionContext(与当前线程绑定),每一次请求对应一个线程来处理request请求,同时为action实例创建一个单独的valueStack对象并将其存储到request域中;valueStack值栈和ActionContext对象都是随着Action实例的创建而存在,随着Action的销毁而消失,valueStack是存储在request域中,则valueStack的生命周期和request的生命周期相同,则Action生命周期和request生命周期相同,request的生命周期是一次请求一次响应范围。
rquest---->ActionContext---->Action---->ValueStack是一一对应的关系,其生命周期都和request相同,是一次请求一次响应范围。

2.3 valueStack值栈内部结构

​ 通过查看值栈的源码可知,值栈由两部分组成root和Context。并且root本质是ArrayList集合,Context本质是linkedHashMap集合。一般存储的数据都是存储在root中,Context中存储的是对象的引用。与action相关的数据存储到root中,与Servlet的API相关的数据存储到Context中。root模拟的是栈结构形式的存取数据。在jsp页面使用struts的<s:dubeg> 标签可以查看值栈的结构。同时Context中存储了root的引用。

root中存储数据类型:

​ root中存储的是action相关的数据,action每次被访问时创建实例对象,struts2会将当前action的实例存储到root的栈底,当action中有模型对象时会将模型对象也存储到root栈中,同时在action中声明的属性都会存储到root中栈中。如果action声明任何属性或模型时,action实例对象是位于root中的栈顶。

Context中存储的数据类型:本质是Map集合,存储的是对象引用,key值是固定的

Map集合的key Map集合的value request 本次请求HttpServletRequest对象的引用 session 本次会话HttpSession对象引用 application web应用的ServletContext对象引用 parameters 本次请求的所有参数的封装引用 attr 获取Servlet三个域中存储的值,当存储的名称相同时,获取是域范围最小的中存储的值 对象(会变) 当jsp页面使用< s:iterator value=”集合” var=”p”> 声明var变量时,会将p对象引用存储在Context中

2.4 valueStack值栈对象的获取

​ 值栈是存储在每次请求的request域中,则可以先获取request对象,再从request中获取valueStack对象;每次action被访问时创建ActionContext对象,并向其中存储valueStack对象,则可以通过ActionContext对象获取valueStack对象。值栈对象针对每一个action实例只有一个实例对象。

// 在action中任意一个方法中均可以获取:// 第一种方式:通过ServletActionContext获取request对象,再获取request域中存储的valueStack对象ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);// 第二种方式:通过actionContext对象获取valueStack对象ValueStack valueStack2 = ActionContext.getContext().getValueStack();

2.5 向valueStack中存储数据

​ 向值栈中存储数据,主要是都是存储在root中的;struts2框架会自动向root中存储数据(两类);在action中向valueStack中存储数据有两种方式。

struts2框架自动向root中存储数据:

​ 通过查看源码可知,struts2框架会向值栈的root中存储数据(两类):第一类是将当前的action对象存储到root的栈底;第二类是将action的模型驱动封装的模型(需要action实现ModelDriven接口)或成员属性(属性需要提供get方法)存储到root中。前者是默认自动存储root中栈底,属性(属性需要提供get方法)或模型存储到root中的顺序和声明时顺序有关,越晚声明的属性(属性需要提供get方法)或模型越靠近root的栈顶。

手动向valueStack中存储数据:两种方式

​ 第一种方式是调用值栈的push方法:本质是将值直接存储到root中(List集合中);

​ 第二种方式是调用值栈的set方法:底层调用的实际是map集合的put方法,此方式将键值对数据存储到Map集合中,然后再将这个Map集合存储号root的中(存储到List集合中)。

# 第一种方式:调用值栈的push方法valueStack对象.push(Object obj);# 第二种方式:调用值栈的set方法valueStack对象.set(Object key, Object value);

案例:

//在action中向valueStack中存储数据public class ValueStackDemo extends ActionSupport {    private static final long serialVersionUID = 1L;    // 声明属性,字符串    private String username = "somnus";    // 提供字符串属性的get方法    public String getUsername() {        return username;    }    // 向值栈中存储对象数据    private User user = new User();    // 提供对象属性的get方法    public User getUser() {        return user;    }    // 向值栈存储List集合数据    List<User> list = new ArrayList<>();    // 提供get方法    public List<User> getList() {        return list;    }    @Override    public String execute() throws Exception {        // 方式1:字符串属性方式存储到valueStack中,自动存储到值栈中        // 修改属性的值        username = "sunmos";        // 方式1:javabean对象存储到值栈中,自动存储        // 设置对象的值        user.setUsername("username");        user.setPassword("sunmos");        // 方式1:list集合存储到值栈中,自动存储        // 设置集合的值        User user2 = new User();        user2.setUsername("username2");        user2.setPassword("sunmos2");        list.add(user2);        User user3 = new User();        user3.setUsername("username3");        user3.setPassword("sunmos3");        list.add(user3);        // 获取值栈对象        ValueStack valueStack = ActionContext.getContext().getValueStack();        // 方式2:调用值栈的set方法,将数据存储到valueStack中,在jsp页面的dubeg中显示Map集合的方式        valueStack.set("username", username);        // 方式3:调用值栈的push方法,将数据保存到valueStack中        valueStack.push("somnus");        return SUCCESS;    }}

2.6 从valueStack中获取数据

​ 一般在jsp页面中通过struts2标签和ognl表达式获取valueStack中存储的数据。获取数据的方式:<s:property value="ognl表达式" />注意:ognl表达式中从root中直接获取数据时,不要加#,从Context中获取数据时,需要加#。一下案例中,通过Context中存储的root的引用也进行了获取值栈中的数据。

值栈push方法和set方法存储的数据获取方式:

// 1. action中存储的数据// 获取valueStack对象ValueStack valueStack = ActionContext.getContext().getValueStack();// 方式1:使用valuestack的push方法valueStack.push("somnus");// 方式2:使用valuestack的set方法valueStack.set("somnus", "java");// 2. 从值栈中获取数据,需要导入struts2的标签库<%@ taglib uri="/struts-tags" prefix="s" %><!-- 从root中获取数据 --><s:property value="somnus"/><br><s:property value="[1].top"/><br><!-- 从Context的root中获取数据 --><s:property value="#root.somnus"/><br> <s:property value="#root[1].top"/><br><s:debug />

action属性驱动方式存储的请求数据获取方式:

// 请求的url是:http://localhost:8080/day06_struts2/demo?username=somnus&password=java// 1. action中使用属性驱动获取请求的参数,并将参数存储到值栈中public class ValueStackDemo2 extends ActionSupport {    // 定义属性    private String username;    // 定义属性    private String password;    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 demo() {        // 打印请求的参数        System.out.println(username+"  "+password);        return "success";    }}// 2. jsp页面获取值栈中数据<!-- 属性驱动获取action中的数据 --><!-- 获取root中action中属性数据 --><s:property value="username"/><br><s:property value="password"/><br><!-- 从Context的root中获取数据 --><s:property value="#root.username"/><br> <s:property value="#root.password"/><br><s:debug />

action的模型驱动封装的请求参数数据的获取:

// 请求地址:http://localhost:8080/day06_struts2/demo3?username=somnus&password=java// 1. action中封装模型驱动数据public class ValueStackDemo3 implements ModelDriven<User>{    // 定义属性    private User user = new User();    public String demo() {        System.out.println(user.getUsername()+"  "+user.getPassword());        // 将模型重新赋值,当改变模型时,在页面中需要使用model.模型属性名获取模型的属性值        user = new User();        user.setUsername("heima");        user.setPassword("python");        return "success";    }    @Override    public User getModel() {        return user;    }}// 前端页面jsp页面获取数据<!-- 模型驱动获取action中的数据 --><!-- 获取root中action中属性数据 --><s:property value="username"/><br><s:property value="password"/><br><!-- 在action中对模型对象重新创建一个模型后进行赋值,则需要通过model对象获取重新设置的值 --><s:property value="model.username"/><br><s:property value="model.password"/><br><!-- 从Context的root中获取数据 --><s:property value="#root.username"/><br><s:property value="#root.password"/><br><!-- 在action中对模型对象重新创建一个模型后进行赋值,则需要通过model对象获取重新设置的值 --><s:property value="#root.model.username"/><br><s:property value="#root.model.password"/><br><!-- 使用EL表达式获取valuestack中存储的值 -->${model.username}<br>${username}<!-- 显示valuestack中的详细信息 --><s:debug />

获取值栈中的javabean对象数据:

// 1. action中存储数据//在action中向valueStack中存储数据public class ValueStackDemo extends ActionSupport {    // 向值栈中存储对象数据    private User user = new User();    // 提供对象属性的get方法    public User getUser() {        return user;    }    @Override    public String execute() throws Exception {        // 方式1:javabean对象存储到值栈中,自动存储        // 设置对象的值        user.setUsername("username");        user.setPassword("sunmos");        return SUCCESS;    }}// 2. 前端页面获取值栈中对象的数据,注意javabean对象必须提供属性的get方法<s:property value="user.username" /><s:property value="user.password" />

获取值栈中的List集合数据:(重点,三种方式获取数据)

// 1. action中向值栈中存储数据public class ValueStackDemo extends ActionSupport {    // 向值栈存储List集合数据    List<User> list = new ArrayList<>();    // 提供get方法    public List<User> getList() {        return list;    }    @Override    public String execute() throws Exception {        // 方式1:list集合存储到值栈中,自动存储        // 设置集合的值        User user2 = new User();        user2.setUsername("username2");        user2.setPassword("sunmos2");        list.add(user2);        User user3 = new User();        user3.setUsername("username3");        user3.setPassword("sunmos3");        list.add(user3);        return SUCCESS;    }}// 2. 在前端页面中获取值栈中数据// 方式1:<s:property value="list[0].username" /><s:property value="list[0].password" /><s:property value="list[1].username" /><s:property value="list[1].password" />// 方式2:遍历集合,使用的是<s:iterator>标签<s:iterator value="lsit" >    <!--遍历得到的是list集合中每一个user,和JSTL的foreach标签有区别-->    <s:property value="username" />    <s:property value="password" /></s:iterator>// 方式3:遍历集合,使用的是<s:iterator>标签,指定遍历的变量<s:iterator value="lsit" var="user" status="status">    <!--遍历得到的是list集合中每一个user        struts2的机制:操作的数据在值栈root中,如果声明了var迭代变量,        list每次迭代的变量user对象是存储在Context中,则使用Context中的对象,需要加#进行访问-->    <s:property value="#status.count" /> <!-- 需要不能省略# -->    <s:property value="#user.username" />    <s:property value="#user.password" /></s:iterator>

2.7 EL表达式获取值栈中的数据(面试)

​ EL表达式原本在jsp页面中不能获取值栈中存储的数据,但是通过查看源码可知,struts2框架通过继承HttpServletRequest的父接口的另一个实现类,使用继承的方式增强了原始的request对象,对request中getAttribute方法进行增强,当使用getAttribute方法从request域中获取数据时,会先从原始的request域中进行查找,如果没有查找到,则会获取值栈valueStack对象,再在值栈中查找是否有,如果有就将值存储到request域中,并将查找的结果返回。则EL表达式从request域中获取数据时,从request中获取不到,则会从值栈中获取数据。

2.8 ognl中特殊的字符

​ ognl中有三个特殊的字符#%$。# 号代表的是从Context中获取数据;%代表的是是否强制解析ognl表达式;$代表从值栈中获取参数并作跳转路径的参数进行传递。

<!--#号和%的使用--><%    request.setAttribute("somnus", "java");    request.getSession().setAttribute("sunmos", "python");%><h3>ognl的特殊符号</h3><h4>#号:从非root中获取数据</h4><s:property value="#request.somnus" /> <!-- 只能此方式才能获取request中的值 --><s:property value="#session.sunmos"/><s:property value="#parameters.username"/> <!-- 获取请求参数中的数据 --><h4>%号:是否强制解析ognl</h4><s:property value="%{#session.sunmos}"/> <!-- 会将字符串解析成ognl表达式 --><s:property value="%{'#session.sunmos'}"/> <!-- 不会解析为ognl表达式,直接数据字符串 #session.sunmos--><!-- 显示valuestack中的详细信息 --><s:debug /><!--$的使用   在struts.xml中的action中配置--><!-- 测试在页面获取action对象中封装的请求参数的数据,模型驱动 --><action name="demo3" class="com.web.valuestack.ValueStackDemo3" method="demo">  <!-- $符号传递参数 -->  <result name="success">/valuestack3.jsp?username=${model.username}</result></action>

2.9 向登陆页面传递提示信息

​ 在struts2中向登陆页面显示提示信息有3种方式:

  1. 通过request域中存储提示信息,在jsp页面中通过EL表达式获取,并显示;
  2. 通过将错误信息存储在值栈中,在jsp页面中使用ognl表达式获取,并显示;
  3. 通过自定义action继承ActionSupport类,使用其中的addActionError/addFieldError/addActionMessage方法设置提示信息,在jsp页面中通过ognl表达式进行获取,并显示。

    ​在Servlet中是通过向request域中存储提示信息,然后再登陆的jsp页面中进行显示;在struts2也可以使用Servlet的方式进行错误信息数据的传递,也可以通过ActionSupport类中提供的方法设置错误提示信息。案例如下:

public class LoginAction extends ActionSupport implements ModelDriven<User>{    private User user = new User();    // 登陆的方法    public String login() {        // 判断用户名和密码是否正确        if("admin".equals(user.getUsername()) && "admin".equals(user.getPassword())) {            // 登陆成功,跳转到成功页面            // 将用户信息保存在session中            ServletActionContext.getRequest().getSession().setAttribute("user", user);            return "success";        } else {            // 登陆失败,跳转到登陆页面,并进行错误信息提示            // 1.方式1:将错误信息存储在valueStack中,在jsp页面中通过ognl表达式获取            // 获取valueStack对象            ValueStack valueStack = ActionContext.getContext().getValueStack();            // 将错误信息保存在valueStack中            valueStack.set("msg", "用户名或密码错误");            // 方式2:action实现actionSupport类            // 调用actionsupport中的方法存储错误信息            this.addActionError("用户名或密码错误"); // 此方法一般用于action执行过程中出现错误的提示信息            this.addFieldError("username", "用户名或密码错误"); // 此方法一般用户提示表单中指定字段校验失败的提示信息            this.addActionMessage("用户名或密码错误"); // 此方法就是一个简单的提示信息            return "failer";        }    }    @Override    public User getModel() {        return user;    }}// 前端页面:<!-- 显示错误提示,方式1:valuestack存储错误信息   --><s:property value="msg"/><!-- 显示错误提示信息,方式2:使用action继承actionsupport类的提示方法 --><s:actionerror/> <!-- 显示action执行出现错误的提示信息,对应的是addActionError设置的信息 --><s:fielderror/> <!-- 显示表单字段校验失败的提示信息,对应的是addFieldError设置的信息 --><s:actionmessage/> <!-- 一般的提示信息,对应的是addActionMessage设置的信息 --><form action="${ pageContext.request.contextPath }/login">    username:<input type="text" name="username"/>    password:<input type="text" name="password"/>    <input type="submit" value="登陆" /></form>

3. interceptor拦截器

​ 拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。struts2中的拦截器,主要是用来实现一些默认的功能(请求参数的封装等)。struts2中有很多的预定义好的拦截器,但是只会执行一些默认的拦截器(在struts-default.xml中< interceptor-stack name=”defaultStack”>中定义的拦截器)。

拦截器的执行时间:在action的对象创建后,在action中的方法执行前执行拦截器。

3.1 拦截器的原理

​ struts2中的拦截器实现的底层原理主要用到AOP(面向切面编程)和责任链设计模式。AOP底层是通过动态代理方式实现类的某些方法的增强;责任链和过滤链类似,就是一组拦截器组成的链式的拦截,每个拦截器做一系列的操作后需要放行到下一个拦截器再做其他的操作,最后一系列的拦截器实现一组的功能。

3.2 拦截器的执行流程

  1. 拦截器在action对象创建之后,action里面的方法执行之前执行;
  2. 在struts2里面执行默认的拦截器,在action不需要调用拦截器的方法,使用配置方式执行(AOP思想的一种实现);
  3. 执行很多的拦截器,比如有三个拦截器,首先执行拦截器1,做放行,执行拦截器2,之后放行,执行拦截器3,放行之后,之后action里面的方法。

3.3 拦截器和过滤器的区别:

  1. 拦截器是基于Java的反射机制的,而过滤器是基于函数回调;
  2. 过滤器依赖与servlet容器,而拦截器不依赖与servlet容器;

  3. 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用(过滤jsp,html,servlet,图片路径等);

  4. 拦截器可以访问action上下文、值栈里的对象,而过滤器不能;

  5. 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。

​ 拦截器 :是在面向切面编程的就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。

​ 过滤器:是在javaweb中,将request中的某些信息进行提前过滤,或者提前设置一些参数(统一字符集编码设置解决post或get方式提交中文数据乱码),然后再传入servlet或者struts的action进行业务逻辑,比如过滤掉非法url(不是login.jsp的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者struts的action前统一设置字符集,或者去除掉一些非法字符。

3.4 自定义interceptor拦截器

​ 在struts2中可以通过拦截器可以在action中的方法执行前和执行后进行一些逻辑的处理,可以实现权限控制等功能。实现自定义拦截器有3种方式:

  1. 通过实现Interceptor接口,并实现接口中的所有方法,该接口是所有的拦截器都必须要实现的接口,该接口是过滤action中所有方法;
  2. 通过继承AbstractInterceptor抽象类,并实现其中的方法,该抽象类也是继承至Interceptor接口,该接口是过滤action中所有方法;
  3. 通过继承MethodFilterInterceptor类,并重写doIntercept方法实现逻辑的操作,该类是AbstractInterceptor的子类,该接口可以自定义过滤的action的方法;

通过自定义拦截器实现逻辑的操作步骤:

  1. 自定义Action实现自定义拦截器,并重写其中过滤的方法,实现逻辑操作控制;
  2. 将自定义的拦截器和需要被拦截的action通过配置文件进行关联。

自定义的拦截器和Action关联的配置方式:拦截action中的所有方法

  1. 配置自定义的拦截器信息,拦截器需要实现Interceptor接口或实现AbstractInterceptor类;
  2. 将自定义拦截通过配置关联action(两种方式:单个拦截器关联action或配置拦截器栈后与action进行关联);

配置模板:在struts的核心配置文件struts.xml文件中配置

<!--配置单个拦截器方式:--><!--步骤1: 配置defaultStack和自定义拦截器,方式1:配置单个拦截器方式 --><interceptors>  <!-- 实现Interceptor接口的拦截器是拦截action中的所有的方法 -->  <interceptor name="MyInterceptor" class="com.web.interceptor.MyInterceptor" /></interceptors><!-- 步骤2:将自定义拦截器和action关联 --><action name="showProduct" class="com.web.action.ProductAction" method="showProduct">  <result name="success">/product.jsp</result>  <!-- 权限校验,失败时跳转到login.jsp -->  <result name="failer">/login.jsp</result>  <!-- 配置defaultStack和自定义拦截器,方式1:配置两个interceptor-ref -->  <interceptor-ref name="MyInterceptor" />  <!-- 定义自定义的拦截器会将struts默认的defaultStack覆盖,则需手动配置上 -->  <interceptor-ref name="defaultStack" /></action><!--配置拦截器栈方式:--><!--步骤1: 配置defaultStack和自定义拦截器,方式2:配置拦截器栈方式 --><interceptors>  <!-- 配置defaultStack和自定义拦截器,方式2:配置拦截器栈方式 -->  <interceptor-stack name="myStack">    <!-- 自定义拦截器配置 -->    <interceptor-ref name="MyInterceptor" />    <!-- 定义自定义的拦截器会将struts默认的defaultStack覆盖,则需手动配置上 -->    <interceptor-ref name="defaultStack" />  </interceptor-stack></interceptors><!-- 步骤2:将自定义拦截器和action关联 --><action name="showProduct" class="com.web.action.ProductAction" method="showProduct">  <result name="success">/product.jsp</result>  <!-- 权限校验,失败时跳转到login.jsp -->  <result name="failer">/login.jsp</result>  <!-- 配置自定义拦截器栈,方式2:配置拦截器栈方式 -->  <interceptor-ref name="myStack" /></action>

自定义的拦截器和Action关联的配置方式:拦截action中的部分方法

  1. 配置自定义的拦截器信息,拦截器需要继承MethodFilterInterceptor类;
  2. 将自定义拦截通过配置关联action(两种方式:单个拦截器关联action或配置拦截器栈后与action进行关联);

配置模板:

<!--配置单个拦截器方式:--><!--步骤1: 配置defaultStack和自定义拦截器,方式1:配置单个拦截器方式 --><interceptors>  <!-- 实现Interceptor接口的拦截器是拦截action中的所有的方法 -->  <interceptor name="MyInterceptor2" class="com.web.interceptor.MyInterceptor2" /></interceptors><!-- 步骤2:将自定义拦截器和action关联 --><action name="showProduct" class="com.web.action.ProductAction" method="showProduct">  <result name="success">/product.jsp</result>  <!-- 权限校验,失败时跳转到login.jsp -->  <result name="failer">/login.jsp</result>  <!-- 配置defaultStack和自定义拦截器,方式1:配置两个interceptor-ref -->  <interceptor-ref name="MyInterceptor2">      <!-- 配置要拦截action中的方法,多个方法时用逗号隔开 -->      <param name="includeMethods">showProduct</param>      <!-- 配置要放行action中的方法,多个方法时用逗号隔开 -->      <param name="excludeMethods">addProduct</param>  </interceptor-ref>  <!-- 定义自定义的拦截器会将struts默认的defaultStack覆盖,则需手动配置上 -->  <interceptor-ref name="defaultStack" /></action><!--配置拦截器栈方式:--><!--步骤1: 配置defaultStack和自定义拦截器,方式2:配置拦截器栈方式 --><interceptors>  <!-- 配置defaultStack和自定义拦截器,方式2:配置拦截器栈方式 -->  <interceptor-stack name="myStack">    <!-- 自定义拦截器配置 -->    <interceptor-ref name="MyInterceptor2">        <!-- 配置要拦截action中的方法,多个方法时用逗号隔开 -->        <param name="includeMethods">showProduct</param>        <!-- 配置要放行action中的方法,多个方法时用逗号隔开 -->        <param name="excludeMethods">addProduct</param>    </interceptor-ref>    <!-- 定义自定义的拦截器会将struts默认的defaultStack覆盖,则需手动配置上 -->    <interceptor-ref name="defaultStack" />  </interceptor-stack></interceptors><!-- 步骤2:将自定义拦截器和action关联 --><action name="showProduct" class="com.web.action.ProductAction" method="showProduct">  <result name="success">/product.jsp</result>  <!-- 权限校验,失败时跳转到login.jsp -->  <result name="failer">/login.jsp</result>  <!-- 配置自定义拦截器栈,方式2:配置拦截器栈方式 -->  <interceptor-ref name="myStack" /></action>

注意实现:当配置自定义的拦截器后,默认的defaultStack拦截器栈中拦截器就不会执行,则需要手动将默认的拦截器栈配置上。默认的拦截器中实现了较多的实用的功能。拦截器的执行逻辑后如果要放行需要调用return invocation.invoke() 方法。

案例:用户没有登陆时,点击查看商品详细数据时,进行登陆的校验–使用拦截所有的方法的拦截器

/** * 自定义实现拦截器 * 实现Interceptor接口,拦截的是所有的方法 * 实现MethodFilterInterceptor则可以过滤指定的方法 */public class MyInterceptor implements Interceptor {    private static final long serialVersionUID = 1L;    @Override    public void destroy() {    }    @Override    public void init() {    }    @Override    public String intercept(ActionInvocation invocation) throws Exception {        // 自定义拦截逻辑        // 获取session中是否有用户信息        User user = (User) ServletActionContext.getRequest().getSession().getAttribute("user");        if(user == null) {            // 用户没有登陆,则返回failer,跳转到登陆页面            // 设置错误提示信息方式1:            // 获取valuestack对象            ValueStack stack = invocation.getStack();            // 将错误信息存储到valuestack中            stack.set("msg", "您没有登陆,无权限查询数据");            // 设置错误提示信息方式1:只能转换为本身的action对象,不能转换为actionsupport对象            ActionSupport action = (ActionSupport) invocation.getAction();            action.addActionError("您没有登陆,无权限查询数据");            return "failer";        } else {            // 用户登陆,直接放行            return invocation.invoke(); // 继续调用下一级拦截器        }    }}
<!--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>    <constant name="struts.devMode" value="true"></constant>    <package name="default" namespace="/" extends="struts-default">        <!-- 注意事项:拦截器的定义必须在action配置的前面 -->        <!-- 定义自定义的拦截器 -->        <!-- 定义自定义的拦截器会将struts默认的defaultStack覆盖,则需手动配置上 -->        <!-- 配置defaultStack和自定义拦截器,方式1: -->        <interceptors>            <!-- 实现Interceptor接口的拦截器是拦截action中的所有的方法 -->            <interceptor name="MyInterceptor" class="com.itheima.web.interceptor.MyInterceptor" />            <!-- 配置defaultStack和自定义拦截器,方式2: -->            <!--<interceptor-stack name="myStack">                <interceptor-ref name="MyInterceptor" />                <interceptor-ref name="defaultStack" />            </interceptor-stack>-->        </interceptors>        <!-- 配置全局的结果页面 -->        <global-results>            <result name="success" type="dispatcher">/success.jsp</result>        </global-results>        <!-- 登陆案例 -->        <action name="login" class="com.itheima.web.action.LoginAction" method="login">            <result name="success">/success.jsp</result>            <result name="failer">/login.jsp</result>        </action>        <!-- 商品显示 -->        <action name="showProduct" class="com.itheima.web.action.ProductAction" method="showProduct">            <result name="success">/product.jsp</result>            <!-- 权限校验,失败时跳转到login.jsp -->            <result name="failer">/login.jsp</result>            <!-- 配置defaultStack和自定义拦截器,方式1:配置两个interceptor-ref -->            <interceptor-ref name="MyInterceptor" />            <interceptor-ref name="defaultStack" />            <!-- 配置defaultStack和自定义拦截器,方式2: -->            <!-- <interceptor-ref name="myStack"/> -->        </action>    </package></struts>

案例:用户没有登陆时,点击查看商品详细数据时,进行登陆的校验–使用拦截部分方法的拦截器

/** * 自定义实现拦截器 * 实现MethodFilterInterceptor则可以过滤指定的方法 */public class MyInterceptor2 extends MethodFilterInterceptor {    private static final long serialVersionUID = 1L;    @Override    protected String doIntercept(ActionInvocation invocation) throws Exception {        // 自定义拦截逻辑        // 获取session中是否有用户信息        User user = (User) ServletActionContext.getRequest().getSession().getAttribute("user");        if(user == null) {            // 用户没有登陆,则返回failer,跳转到登陆页面            // 设置错误提示信息方式1:            // 获取valuestack对象            ValueStack stack = invocation.getStack();            // 将错误信息存储到valuestack中            stack.set("msg", "您没有登陆,无权限查询数据");            // 设置错误提示信息方式1:只能转换为本身的action对象,不能转换为actionsupport对象            ActionSupport action = (ActionSupport) invocation.getAction();            action.addActionError("您没有登陆,无权限查询数据");            return "failer";        } else {            // 用户登陆,直接放行            return invocation.invoke(); // 继续调用下一级拦截器        }    }}
<!--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>    <constant name="struts.devMode" value="true"></constant>    <package name="default" namespace="/" extends="struts-default">        <!-- 注意事项:拦截器的定义必须在action配置的前面 -->        <!-- 定义自定义的拦截器 -->        <!-- 定义自定义的拦截器会将struts默认的defaultStack覆盖,则需手动配置上 -->        <!-- 配置defaultStack和自定义拦截器,方式1: -->        <interceptors>            <!-- 继承MethodFilterInterceptor类的方式的拦截器可以指定拦截action中某些方法 -->            <interceptor name="MyInterceptor2" class="com.itheima.web.interceptor.MyInterceptor2" />            <!-- 配置defaultStack和自定义拦截器,方式2:配置拦截器栈 -->            <!--<interceptor-stack name="myStack">                <interceptor-ref name="MyInterceptor2">-->                    <!-- 配置要拦截action中的方法,多个方法时用逗号隔开 -->                    <!-- <param name="includeMethods">showProduct</param> -->                    <!-- 配置要放行action中的方法,多个方法时用逗号隔开 -->                    <!--<param name="excludeMethods">addProduct</param>                </interceptor-ref>                <interceptor-ref name="defaultStack" />            </interceptor-stack> -->        </interceptors>        <!-- 配置全局的结果页面 -->        <global-results>            <result name="success" type="dispatcher">/success.jsp</result>        </global-results>        <!-- 登陆案例 -->        <action name="login" class="com.itheima.web.action.LoginAction" method="login">            <result name="success">/success.jsp</result>            <result name="failer">/login.jsp</result>        </action>        <!-- 商品显示 -->        <action name="showProduct" class="com.web.action.ProductAction" method="showProduct">            <result name="success">/product.jsp</result>            <!-- 权限校验,失败时跳转到login.jsp -->            <result name="failer">/login.jsp</result>            <!-- 配置defaultStack和自定义拦截器,方式1:配置两个interceptor-ref -->            <interceptor-ref name="MyInterceptor2">                <!-- 配置要拦截action中的方法,多个方法时用逗号隔开 -->                <param name="includeMethods">showProduct</param>                <!-- 配置要放行action中的方法,多个方法时用逗号隔开 -->                <param name="excludeMethods">addProduct</param>            </interceptor-ref>            <interceptor-ref name="defaultStack" />            <!-- 配置拦截部分方法的拦截器,方式2:配置拦截器栈方式 -->            <!--<interceptor-ref name="MyInterceptor">-->        </action>    </package></struts>

4. struts2的标签

struts2常用标签:

# <s:debug>标签:查看值栈存储结构# <s:property value=”ognl表达式”>: 在jsp中获取值栈数据# <s:iterator>标签: 遍历得到值栈集合内容# 注意事项:struts2中标签的使用需要引入struts2的标签库< %@ taglib uri="/struts-tags" prefix="s"%>

struts2的表单标签:struts2表单标签和html中表单标签大多是一一对应的

<!-- 1 form标签 --><s:form action="customer_add.action" method="post">    <!-- 2 普通输入项 -->    <s:textfield name="username" label="用户名"></s:textfield>    <!-- 3 密码输入项 -->    <s:password name="password" label="密码"></s:password>    <!-- 单选输入项         第一种情况: value值和显示值一样的,构建list集合        第二种情况: value值和显示值不一样的,构建map集合    -->    <s:radio list="{'女','男'}" name="sex" label="性别"></s:radio>    <s:radio list="#{'nv':'女','nan':'男' }"  name="sex1" label="性别"></s:radio>    <!-- 复选输入项 -->    <s:checkboxlist list="{'吃饭','睡觉','购物'}" name="love" label="爱好"></s:checkboxlist>    <!-- 下拉输入框 -->    <s:select list="{'幼儿园','博士后','教授'}" name="college" label="学历"></s:select>    <!-- 隐藏输入项 -->    <s:hidden name="hid" value="abcd"></s:hidden>    <!-- 文件上传项 -->    <s:file name="file" label="选择文件"></s:file>    <!-- 文本域 -->    <s:textarea rows="3" cols="10" label="简历" name="resume"></s:textarea>    <!-- 提交按钮 -->    <s:submit value="提交"></s:submit>    <!-- 重置按钮 -->    <s:reset value="重置"></s:reset></s:form>

struts2标签示例结果

原创粉丝点击