Struts2

Struts2

    • Struts2的概述
    • Struts2的入门
        • 下载Struts2的开发环境
        • 创建Struts2框架的项目
    • Struts2的常见配置
        • package标签的相关配置
        • action相关配置
        • Struts2的常量配置
        • Struts2的分模块开发
    • Action
        • Action的写法
        • Action的访问
    • Struts2的Servlet的API的访问
        • 完全解耦的方式
        • 使用Servlet的API的原生方式
        • 接口注入的方式
    • Struts2的结果页面的配置
        • Result标签的配置(重定向和请求转发以及文件下载)
    • Struts2的数据的封装
        • 属性驱动:提供属性set方法的方式(不常用)
        • 属性驱动:页面提供表达式
        • 采用模型驱动的方式
    • INPUT逻辑视图的配置
    • 复杂类型的数据封装
        • 封装数据到List集合中
        • 封装数据到map集合中
    • Struts2的拦截器
        • 拦截器的概述
        • 过滤器和拦截器的区别:
        • 自定义拦截器
        • demo:实现用户登录认证
    • Sturts2的执行流程
    • Struts2的标签库
        • 通用标签库
        • UI标签库(方便数据回显)

Struts2的概述

Struts2是一个基于MVC设计模式的WEB层框架,具有web层框架的特点,基于前端控制器实现的

Struts2的入门

下载Struts2的开发环境

Struts2的开发环境下载地址,2.3.24
解压,四个目录
在这里插入图片描述

  • apps:Struts2提供的应用,war文件,放入tomcat中可以启动
  • docs:Struts2的开发文档和API
  • lib:Struts2框架开发的jar包
  • src:Struts2的源码

创建Struts2框架的项目

  1. 创建web项目

  2. 导入jar包
    将app目录下面的struts2-blank.war包放到tomcat下面,启动tomcat解压war包可以得到struts2框架的基本包
    Struts2_第1张图片
    在项目的WEB-INF下创建lib文件夹,将jar包导入lib中

  3. 在src目录下创建struts.xml的配置文件,其约束在下面的图片所示
    Struts2_第2张图片

  4. 在web.xml中配置struts2的前端控制器
    Struts2_第3张图片

  5. 创建web层(controller层)类

    public class HelloAction {
    
        public String execute(){
            System.out.println("HelloAction执行了....");
            return "success";
        }
    }
    
  6. 配置struts.xml(注意:struts.xml必须配置src的根目录下面,否则不起作用

    
    
    <struts>
    	
        <package name="demo1" extends="struts-default" namespace="/">
        
            <action name="hello" class="com.struts2.action.HelloAction">
            	
                <result name="success">/demo1/success.jspresult>
            action>
        package>
    struts>
    

    目录结构:
    Struts2_第4张图片

  7. 前端跳转路径

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

Struts2的常见配置

package标签的相关配置

package标签称为包,这个包与java中的包概念不一样,包是为了更好管理action

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

action相关配置

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

Struts2的常量配置

在Struts2的框架中,提供了非常多的常量配置,在Struts2源码struts2-core-2.3.24.jar的jar包下面的org.apache.struts2.default.properties配置文件中

  • struts.i18n.encoding=UTF-8 :设置字符集编码,在Struts2中所有的post请求的中文乱码不用处理
  • struts.action.extension=action,,:Struts2请求的默认扩展名,扩展名是action,或者什么都不写

default.properties已经是底层了,被人家打成了jar包,所以我们想要修改常量可以到struts.xml或者web.xml中修改

例如:在struts.xml中修改Struts2请求的默认扩展名

<constant name="struts.action.extension" value="action">constant>

在web.xml中修改Struts2请求的默认扩展名

 <filter>
        <filter-name>strutsfilter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterfilter-class>
        
        <init-param>
            <param-name>struts.action.extensionparam-name>
            <param-value>actionparam-value>
        init-param>
    filter>

Struts2的分模块开发

在团队开发的时候,个人负责个人的模块,都会来修改同一个配置文件,这样在整合的时候会比较困难,所以,一般都是个人写个人的配置文件,然后有一个总的配置文件来管理这些个人写的配置文件

	<include file="com/struts2/action/structs_demno1.xml">include>
    <include file="com/struts2/action/structs_demno2.xml">include>

Action

Action的写法

  • Action类是简单类
  • Action类实现一个Action的接口
  • Action类继承ActionSupport类(推荐使用)

Action类实现接口和继承类,他们的父类都给他们提供了五个常量:

  • SUCCESS:成功
  • ERROR:失败
  • LOGIN:登录出错页面跳转
  • INPUT:表单校验的时候出错
  • NONE:不跳转

但是继承类还提供了数据校验等一系列操作的功能,所以推荐使用继承ActionSupport

Action的访问

  1. 通过method设置
    访问路径:

    	<a href="${pageContext.request.contextPath}/userFind.action">查询用户a>
        <a href="${pageContext.request.contextPath}/userUpdate.action">修改用户a>
        <a href="${pageContext.request.contextPath}/userDelete.action">删除用户a>
        <a href="${pageContext.request.contextPath}/userSave.action">保存用户a>
    

    配置struts.xml

    	<package name="demo2" extends="struts-default" namespace="/">
            <action name="userFind" class="com.struts2.action.demo1.UserAction" method="find">action>
            <action name="userUpdate" class="com.struts2.action.demo1.UserAction" method="update">action>
            <action name="userDelete" class="com.struts2.action.demo1.UserAction" method="delete">action>
            <action name="userSave" class="com.struts2.action.demo1.UserAction" method="save">action>
        package>
    
  2. 通过通配符的方式进行设置(要求你访问的路径中包含你要执行的方法)
    访问路劲

    	<a href="${pageContext.request.contextPath}/user_find.action">查询用户a>
        <a href="${pageContext.request.contextPath}/user_update.action">修改用户a>
        <a href="${pageContext.request.contextPath}/user_delete.action">删除用户a>
        <a href="${pageContext.request.contextPath}/user_save.action">保存用户a>
    

    配置struts.xml

    	<package name="demo2" extends="struts-default" namespace="/">
            <action name="user_*" class="com.struts2.action.demo1.UserAction" method="{1}">action>
        package>
    

    通配符更加抽象的写法:

    	<package name="demo2" extends="struts-default" namespace="/">
            <action name="*_*" class="com.struts2.action.demo1.{1}" method="{2}">action>
        package>
    
  3. 动态方法访问

    访问路径:

    	<a href="${pageContext.request.contextPath}/user!find.action">查询用户a>
        <a href="${pageContext.request.contextPath}/user!update.action">修改用户a>
        <a href="${pageContext.request.contextPath}/user!delete.action">删除用户a>
        <a href="${pageContext.request.contextPath}/user!save.action">保存用户a>
    

    配置struts.xml

    	
        <constant name="struts.enable.DynamicMethodInvocation" value="true">constant>
        <package name="demo2" extends="struts-default" namespace="/">
            <action name="user" class="com.struts2.action.demo1.UserAction">action>
        package>
    

Struts2的Servlet的API的访问

在使用Struts2的框架的过程中,发现Struts2和Servlet的API是解耦合的。在实际开发中,经常会使用到Servlet的API,比如登录,将用户信息保存到session中等

完全解耦的方式

  1. 获取页面传递的数据:

    	  ActionContext context = ActionContext.getContext();
    	  //map集合的值是一个数组
          Map<String, Object> map = context.getParameters();
          for (String key:map.keySet()){
              String[] values = (String[])map.get(key);
              System.out.println(key+"        "+ Arrays.toString(values));
          }
    
  2. 将参数传到前端页面

    		context.put("reqName","李四");//相当于request.setAttribute();
            context.getSession().put("age",25);//相当于session.setAttribute();
            context.getApplication().put("tel","10086");//相当于application.setAttribute();
    

这种方式可以操作域对象的数据,但不能获得对象的方法

使用Servlet的API的原生方式

  1. 获取页面传递的数据

     		javax.servlet.http.HttpServletRequest request = ServletActionContext.getRequest();
            Map<String, String[]> map = request.getParameterMap();
            for (String key:map.keySet()){
                System.out.println(Arrays.toString(map.get(key)));
            }
    
  2. 将参数传递到前端

            //向request中保存数据
            request.setAttribute("reqName","王者荣耀");
            //向session中保存数据
            request.getSession().setAttribute("age","23");
            //向application中保存数据
            ServletActionContext.getServletContext().setAttribute("tel","10010");
    

这种方式可以操作域对象的数据,也可以获得对象的方法

接口注入的方式

import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.util.ServletContextAware;
public class RequestDemo3Action extends ActionSupport implements ServletRequestAware, ServletContextAware {
	private HttpServletRequest request;
    private ServletContext context;
	
	@Override
    public String execute() throws Exception {
        Map<String, String[]> map = request.getParameterMap();
        for (String key:map.keySet()){
            System.out.println(Arrays.toString(map.get(key)));
        }
        //向request中保存数据
        request.setAttribute("reqName","王者荣耀");
        //向session中保存数据
        request.getSession().setAttribute("age","23");
        //向application中保存数据
        context.setAttribute("tel","10010");
        return SUCCESS;
    }

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

    @Override
    public void setServletContext(ServletContext servletContext) {
        this.context = servletContext;
    }
}

一般情况下,接口注入会写一个基本的BaseAction,然后让其他的Action去继承这个BaseAction
Servlet是单例的,多个程序访问同一个Servlet只会创建一个Servlet实例,Action是多例的,一次请求,创建一个Action实例(不会出现线程安全的问题)

Struts2的结果页面的配置

  1. 全局结果页面
    全局结果页面对于当前包下面的action都有效
  2. 局部结果页面
    局部结果页面对于对应的action有效
	<package name="demo3" extends="struts-default" namespace="/">
        <global-results>
            
            <result name="success">/demo1/demo2.jsp<result>
        global-results>
        <action name="requestDemo1" class="com.struts2.action.demo1.RequestDemo1Action">
            
            <result name="success">/demo1/demo2.jsp<result>
        action>
        <action name="requestDemo2" class="com.struts2.action.demo1.RequestDemo1Action">
        action>
        <action name="requestDemo3" class="com.struts2.action.demo1.RequestDemo1Action">
        action>
    package>

Result标签的配置(重定向和请求转发以及文件下载)

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

demo
重定向(Action → Action)

  1. 不在同一个名称空间下的重定向

    	  <result name="saveSuccess" type="redirectAction">
              <param name="namespace">/param>
              <param name="actionName">customer_find.actionparam>
          result>
    
  2. 处于同一个名称空间下的重定向

    	<result name="saveSuccess" type="redirectAction">customer_find.actionresult>
    

Struts2的数据的封装

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

在action提供前台所传数据的set方法,即可获取到数据

	private String username;
    private String password;
    private Integer age;
    private Date birthday;

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

    @Override
    public String execute() throws Exception {
        System.out.println(username);
        System.out.println(password);
        System.out.println(age);
        System.out.println(birthday);
        return NONE;
    }

属性驱动:页面提供表达式

前端:注意看name属性

	<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="submit" value="注册">
    form>

后端:必须生成对象的get和set方法,不能省略get方法

	private User user;

    public User getUser() {
        return user;
    }

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

    @Override
    public String execute() throws Exception {
        System.out.println(user.toString());
        return NONE;
    }

相较于提供属性set方法的方式的属性驱动而言,属性驱动:页面提供表达式的方式将你获得的数据直接封装到了对象之中。

采用模型驱动的方式

public class UserAction3 extends ActionSupport implements ModelDriven<User> {

    private User user = new User();
    @Override
    public String execute() throws Exception {
        System.out.println(user);
        return NONE;
    }

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

缺点:一个Action只能向一个对象进行封装
优点:相比于第二种数据封装方法而言,不需要修改页面

INPUT逻辑视图的配置

在页面显示错误信息:
页面配置:

  1. 导包

    <%@ taglib uri="/struts-tags" prefix="s"%>
    
  2. 提供错误信息显示的地方

    <s:fielderror>s:fielderror>
    

后端配置

		<global-results>
			
            <result name="input">/demo1/demo3.jspresult>
        global-results>

复杂类型的数据封装

封装数据到List集合中

页面写法:

	<form action="${pageContext.request.contextPath}/productAction1.action" method="post">
        商品名称:<input type="text" name="products[0].productName"> <br />
        商品价格:<input type="text" name="products[0].price"> <br />
        商品名称:<input type="text" name="products[1].productName"> <br />
        商品价格:<input type="text" name="products[1].price"> <br />
        商品名称:<input type="text" name="products[2].productName"> <br />
        商品价格:<input type="text" name="products[2].price"> <br />
        <input type="submit" value="提交">
    form>

action写法:

	private List<Product> products;

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    }

    @Override
    public String execute() throws Exception {

        System.out.println(products.toString());
        return NONE;
    }

封装数据到map集合中

页面写法:

	<form action="${pageContext.request.contextPath}/productAction2.action" method="post">
        商品名称:<input type="text" name="map['one'].productName"> <br />
        商品价格:<input type="text" name="map['one'].price"> <br />
        商品名称:<input type="text" name="map['two'].productName"> <br />
        商品价格:<input type="text" name="map['two'].price"> <br />
        商品名称:<input type="text" name="map['three'].productName"> <br />
        商品价格:<input type="text" name="map['three'].price"> <br />
        <input type="submit" value="提交">
    form>

action接收:

	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()){
            System.out.println(map.get(key));
        }
        return NONE;
    }

Struts2的拦截器

拦截器的概述

Interceptor:Struts2拦截器,起到拦截Action的作用

过滤器和拦截器的区别:

  • filter:过滤器,过滤从客户端向服务器发送的请求,可以拦截所有
  • Interceptor:拦截器,拦截的是客户端对Action的访问,不能拦截jsp,html。更细粒度化的拦截,可以拦截到Action中的具体方法,Struts2框架的核心功能都是依赖拦截器实现的

自定义拦截器

  1. 创建一个自定义拦截器类,并继承AbstractInterceptor抽象类,重写AbstractInterceptor的抽象方法

    import com.opensymphony.xwork2.ActionInvocation;
    import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
    public class InterceptorDemo1 extends AbstractInterceptor {
        @Override
        public String intercept(ActionInvocation actionInvocation) throws Exception {
    
            System.out.println("InterceptorDemo1执行了");
            String invoke = actionInvocation.invoke();
            System.out.println("InterceptorDemo1执行结束了");
            return invoke;
        }
    }
    
  2. 在需要拦截的方法所配置的struts中配置拦截器,注意配置自定义拦截器后,struts2默认拦截器栈的拦截器就不会执行了,需要自己手动引入

    <package name="demo1" extends="struts-default" namespace="/">
    	
        <interceptors>
            <interceptor name="interceptorDemo1" class="com.ognl.interceptor.InterceptorDemo1">interceptor>
        interceptors>
    
        <action name="bbb" class="com.ognl.action.ActionDemo1">
            <result name="actionDemo1">/demo1/demo1.jspresult>
            
            <interceptor-ref name="defaultStack">interceptor-ref>
            
            <interceptor-ref name="interceptorDemo1">interceptor-ref>
        action>
    package>
    

当拦截器有多个时,一个个引入是很烦的,我们可以定义一个拦截器栈,将需要引用的拦截器放入拦截器栈中,然后引用拦截器栈即可

		<interceptors>
            <interceptor name="interceptorDemo1" class="com.ognl.interceptor.InterceptorDemo1">interceptor>
            <interceptor name="interceptorDemo2" class="com.ognl.interceptor.InterceptorDemo2">interceptor>
            
            <interceptor-stack name="mystack">
                <interceptor-ref name="defaultStack">interceptor-ref>
                <interceptor-ref name="interceptorDemo1">interceptor-ref>
                <interceptor-ref name="interceptorDemo2">interceptor-ref>
            interceptor-stack>
        interceptors>
        <action name="aaa" class="com.ognl.action.ValueStackDemo1">
            <result name="success">/demo1/success.jspresult>
        action>
        <action name="bbb" class="com.ognl.action.ActionDemo1">
            <result name="actionDemo1">/demo1/demo1.jspresult>
            
            <interceptor-ref name="mystack">interceptor-ref>

        action>

拦截器的执行顺序:
Struts2_第5张图片

demo:实现用户登录认证

前端登录页面:

<form id=form1 name=form1 action="${pageContext.request.contextPath}/user_login.action" method=post target="_parent">
	 
	 <s:actionerror>s:actionerror>
     <table cellSpacing=0 cellPadding=2 border=0>
     	<tbody>
        	<tr>
            	<td style="height: 28px" width=80>登 录 名:td>
                <td style="height: 28px" width=150>
                	<input id=txtName style="WIDTH: 130px" name="user_code">
                td>			        
            tr>
            <tr>
            	<td style="height: 28px">登录密码:td>
                <td style="height: 28px">
                	<input id=txtPwd style="width: 130px" type=password name="user_password">
                td>
            tr>
 		tbody>
 	table>
FORM>

后端action:

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import org.apache.struts2.ServletActionContext;

public class UserAction extends ActionSupport implements ModelDriven<User> {

    private User user = new User();
    private UserService userService = new UserServiceImpl();
    public String login(){
        List<User> login = userService.login(user);
        if(login.size() > 0){
        	//登录成功
        	//将用户名保存到session
            ServletActionContext.getRequest().getSession().setAttribute("username",login.get(0).getUser_code());
            return SUCCESS;
        }else{
        	//登录失败,返回错误信息
            this.addActionError("用户名或密码错误");
            return LOGIN;
        }
    }

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

拦截器:

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import org.apache.struts2.ServletActionContext;

public class PrivilegeInterceptor extends MethodFilterInterceptor {

    @Override
    protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
    	//获取session中的用户名
        Object username = ServletActionContext.getRequest().getSession().getAttribute("username");
        if(username == null){
			//没有登录
            ActionSupport action = (ActionSupport) actionInvocation.getAction();
            //返回错误信息
            action.addActionError("没有登录!请先登录");
            //打回登录页面
            return ActionSupport.LOGIN;
        }else{
            //登录,放行
            return actionInvocation.invoke();
        }
    }
}

xml配置:

 <package name="customer" extends="struts-default" namespace="/">
		
        <interceptors>
            <interceptor name="privilegeInterceptor" class="com.xuhx.web.interceptor.PrivilegeInterceptor">interceptor>
        interceptors>
        
        <global-results>
            <result name="login">/login.jspresult>
        global-results>
        <action name="customer_*" class="com.xuhx.web.action.CustomerAction" method="{1}">
            <result name="findSuccess">/jsp/customer/list.jspresult>
            
            <interceptor-ref name="privilegeInterceptor">
            	
                <param name="excludeMethods">loginparam>
            interceptor-ref>
            <interceptor-ref name="defaultStack">interceptor-ref>
        action>
        <action name="user_*" class="com.xuhx.web.action.UserAction" method="{1}">
            <result name="success">/index.jspresult>
            
            <interceptor-ref name="privilegeInterceptor">
                <param name="excludeMethods">loginparam>
            interceptor-ref>
            <interceptor-ref name="defaultStack">interceptor-ref>
        action>
    package>

Sturts2的执行流程

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

Struts2的标签库

导包:

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

通用标签库

  1. 条件判断

    	<s:set var="i" value="3" scope="request">s:set>
        <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. 循环
    遍历List集合:

    	<s:iterator var="i" value="{'aa','bb','cc'}">
            <s:property value="#i" />
        s:iterator>
    

    遍历map集合:

    	<s:iterator var="i" value="#{'aaa':'111','bbb':'222','ccc':'333'}">
            <s:property value="#i.key">s:property>--<s:property value="#i.value">s:property>
        s:iterator>
    

    遍历1-10的奇数

    	<s:iterator var="i" begin="1" end="10" step="2" status="status">
            <s:property value="#i" />
            
            <s:property value="#status.count" /><br />
        s:iterator>
    
  3. 调试用的标签

  4. 日期格式化

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

  1. 表单
    	<s:form action="" method="post">
            <s:textfield name="name" label="用户名" />
            <s:password name="password" 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="20" rows="5" label="介绍" />
            <s:submit value="提交" />
    	s:form>
    
    Struts2_第6张图片

你可能感兴趣的:(框架)