java框架复习——struts2

目录

概述

Struts 2是Struts的下一代产品,是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架。其全新的Struts 2的体系结构与Struts 1的体系结构差别巨大。Struts2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以Struts 2可以理解为WebWork的更新产品。

虽然从Struts 1到Struts 2有着太大的变化,但是相对于WebWork,Struts 2的变化很小。

Struts:框架,就是一个半成品。

WebWork:设计非常先进。

Struts2是一个位于表现层的非常优秀的MVC框架。它的核心是WebWork。

struts2需要的jar包

  • struts2-core-2.3.1.1.jar:Struts 2框架的核心类库
  • xwork-core-2.3.1.1.jar:Command模式框架,WebWork和Struts2都基于xwork
  • ognl-3.0.3.jar:对象图导航语言(Object Graph Navigation Language), struts2框架通过其读写对象的属性
  • freemarker-2.3.18.jar:Struts 2的UI标签的模板使用FreeMarker编写
  • commons-logging-1.1.x.jar:ASF出品的日志包,Struts 2框架使用这个日志包来支持Log4J和JDK 1.4+的日志记录。
  • commons-fileupload-1.2.2.jar: 文件上传组件,2.1.6版本后需要加入此文件
  • commons-io-2.0.1.jar:传文件依赖的jar包
  • commons-lang-2.5.jar:对java.lang包的增强
  • javassist-3.11.0.GA.jar是一个开源的分析、编辑和创建Java字节码的类库

struts2的运行流程

  1. 启动服务,加载web.xml实例化StrutsPrepareAndExecuteFilter过滤器
  2. 在实例化StrutsPrepareAndExecuteFilter的时候会执行过滤器中的init方法加载struts.xml
  3. 浏览器发起请求,会被StrutsPrepareAndExecuteFilter拦截到,根据请求的uri(hello)找到相应的Action类,并且创建Action对象,执行相应的hello方法。
  4. 返回视图标志,展示success.jsp
    在这里插入图片描述

struts2的配置文件

配置文件的名称位置存储的内容说明
Default.propertiesStruts2-core-2.3.jar/org/apache/struts2/default.properties通过属性的形式来配置struts2的参数不能直接修改(可以间接修改)
Struts-default.xmlStruts2-core-2.3.jar/ struts-default.xml是struts的核心配置文件不能直接修改(可以间接修改)
Struts-plugin.xml***-plugin.jar中插件相关配置不能直接修改
Struts.xml放在classpath中应用配置文件开发人员使用,可以修改
Struts.properties放在classpath中应用配置文件(较少使用)开发人员使用,可以修改

以上的配置文件服务器启动时会被加载,按着配置文件的加载的顺序,后面文件和前面文件相同的配置,后面的会把前面的文件的值覆盖。

default.properties

参数名称参数默认值说明
struts.i18n.encodingUTF-8框架使用的编码
struts.action.extensionaction请求的后缀名
struts.serve.static.browserCacheTrue是否开启浏览器的静态资源的缓存
struts.configuration.xml.reload=falseFalse每当struts.xml被修改的时候我们是否要热部署,是否需要重启服务器
struts.devModeFalse是否是开发者模式
<!-- 
		通过constant配置来间接修改default.properties的属性,修改请求的后缀
	 -->
<constant name="struts.action.extension" value="do,,"></constant>

struts-default.xml

该文件是struts的核心文件,里面提供了结果的返回类型和拦截器还有业务bean

Result-type
在这里插入图片描述
拦截器
在这里插入图片描述

struts.xml

这个文件是开发人员要编写的文件

package

package是我们按着项目的模块来划分的一种单元,我们可以在开发中一个模块一个package。

其中包含的属性:

  • Name:必须的属性,包的名称
  • Extends:包的继承,默认情况下我们必须要继承struts-default,否-则我们无法使用struts框架
  • Abstract:抽象包,在该包中不能有Action,其他的都可以有
  • Namespace:包的命名空间,值必须要有/,namespace用于请求访问时指定某一个包的路径,目的区分不同包的相同的Action名字

Action

Action是每次请求所访问的方法

其中包含的属性:

  • Name:必须要有的http://localhost:8080/struts2_02/person/hello.action,后缀不需要指定,我们访问方法时就是根据name来访问
  • Class:要访问的方法的所在类
  • Method:方法名

Result

Result就是要跳转的视图

其中包含的属性:

  • Name:result的名称,是唯一的,到底Action返回哪个视图是由Action的方法的返回值来决定,返回值和result视图的name属性值做匹配,从而返回相应的视图。
  • Type:跳转视图的方式。默认情况跳转方式是请求转发,地址栏不变。

struts动作类(Action)

创建动作类

使用普通方式javaBean作为Action动作类,不需要继承任何父类,也不需要实现接口。

  • 方法一定是public公用的,
  • 返回值是字符串用来决定跳转到哪个视图
  • 不需要参数
  • 方法名自定义,如果不自定义的话,有个默认的方法名execute
public class HelloAction {
	
	public String hello(){
		System.out.println("讲师");
		return "success";
	}
	/**
	 * 当前请求没有绑定方法的时候自动找execute默认的方法
	 * @return
	 */
	public String execute(){
		System.out.println("一个好讲师");
		return "success";
	}
}

创建动作类实现接口com.opensymphony.xwork2.Action

public class ActionDemo implements Action {
    
    @Override
    public String execute() throws Exception {
        return null;
    }
}

Action接口中提供了一些常量

常量说明
SUCCESSSuccess返回成功页面
NONENone不返回任何页面
ERRORError返回错误提示页面
INPUTInput当提交表单时发生错误,就跳回表单页面
LOGINLogin返回登录页面
public interface Action {
    String SUCCESS = "success";
    String NONE = "none";
    String ERROR = "error";
    String INPUT = "input";
    String LOGIN = "login";
    String execute() throws Exception;
}

创建动作类继承父类com.opensymphony.xwork2.ActionSupport(推荐使用)

public class ActionDemo extends ActionSupport {
    @Override
    public String execute() throws Exception {
        return null;
    }
}

配置动作类

<!-- 
name:必须指定在一个包中唯一
class:当前action所在的动作类
method:当前action要访问的方法
-->
<action name="as" class="com.rl.action.HelloAction2" method="helloAS">
	<result name="success">/success.jsp</result>
</action>

action动作类的生命周期

创建:Action动作类每次请求的时候都会创建一个实例对象

销毁:当前action动作类的请求响应完后就消失了

跟javaweb中的HttpServletRequest的生命周期是一样的,struts2是多例的,线程安全的

action动作类的访问

通配符(常用)

原则:约定优于配置

在action中每一个方法有一定的规则:都以User结尾

public class UserAction extends ActionSupport{
	public String saveUser(){
		System.out.println("啊..我被保存了");
		return super.SUCCESS;
	}
	public String updateUser(){
		System.out.println("啊..我被修改了");
		return super.SUCCESS;
	}
}

在这里插入图片描述

public class PersonAction extends ActionSupport{

	public String save_Person(){
		System.out.println("啊..我被保存了");
		return super.SUCCESS;
	}
	
	public String update_Person(){
		System.out.println("啊..我被修改了");
		return super.SUCCESS;
	}
}

在这里插入图片描述
注意:同一个包中不能有多个通配符的action,如果有多个通配符action,请求就不知道找哪个类了,如上图所示,如果不注释掉就会报错

动态方法调用

首先要开启动态方法调用的开关

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

这是我们的action的动作类不需要再指定方法method属性了,而是在url上指定。

语法:!方法名[.后缀名]

http://localhost:8080/struts2_05/person/renliang!hello3.do

结果视图的配置

在这里插入图片描述

<result name="success">/success.jsp</result>

Result表示要返回的视图

包含有的属性:

  • Name: action动作类要返回的值,如果返回的值匹配上了当前的
  • name:值就跳转到相应的页面
  • Type:跳转方式

Type的跳转方式有以下几种:

  1. Dispatcher
    请求转发,相当于javaweb中的forward,地址栏不变,type默认是dispatcher
  2. Redirect
    页面重定向,地址栏变化
  3. Chain
    请求转发到一个action动作类,地址栏不发生变化
    在这里插入图片描述
  4. redirectAction
    重定向到一个action的动作类,地址栏变化
    同包内的重定向
    在这里插入图片描述
    不同包的重定向,result需要通过param来指定namespace和actionName
<package name="hello" extends="struts-default" namespace="/person">
		<action name="renliang" class="com.rl.action.HelloAction" method="hello">
			<result name="success" type="redirectAction">
				<!-- 
					指定命名空间
				 -->
				<param name="namespace">/person1</param>
				<!-- 
					重定向的action的name
				 -->
				<param name="actionName">rl</param>
			</result>
		</action>
		
	</package>
	<package name="hello1" extends="struts-default" namespace="/person1">
		<action name="rl" class="com.rl.action.HelloAction1" method="hello1">
			<result name="success">/success1.jsp</result>
		</action>
	</package>
  1. stream
    文件的上传和下载

局部结果视图和全局的结果视图

在一个action中配置的result是局部结果视图,外部的action是不能使用这个result

包内的全局的结果视图配置对于包内的每一个action动作类都有效

<package name="hello1" extends="struts-default" namespace="/person1">
		<!-- 
			包级别的全局结果视图
		 -->
		<global-results>
			<result name="error">/error.jsp</result>
		</global-results>
		<action name="rl" class="com.rl.action.HelloAction1" method="hello1">
			<result name="success">/success1.jsp</result>
			<!-- <result name="error">/error.jsp</result> -->
		</action>
		<action name="rl1" class="com.rl.action.HelloAction2" method="hello2">
			<result name="success">/success1.jsp</result>
			<!-- <result name="error">/error.jsp</result> -->
		</action>
	</package>

在整个系统中的全局的结果视图,通过继承的方式来实现

<!-- 定义一个公用的包,继承struts-default -->
	<package name="basePackage" extends="struts-default" abstract="true">
		<global-results>
			<result name="error">/error.jsp</result>
		</global-results>
	</package>
	
	<package name="hello" extends="basePackage" namespace="/person">
		<action name="renliang" class="com.rl.action.HelloAction" method="hello">
			<result name="success">/success.jsp</result>
		</action>
	</package>
	<package name="hello1" extends="basePackage" namespace="/person1">
		<action name="rl" class="com.rl.action.HelloAction1" method="hello1">
			<result name="success">/success1.jsp</result>
		</action>
		<action name="rl1" class="com.rl.action.HelloAction2" method="hello2">
			<result name="success">/success1.jsp</result>
		</action>
	</package>

设置了abstract="true"的包内不能有action,当可以被其他的包继承

struts动作类获得ServletAPI

使用ServletActionContext获得servletAPI

public String hello(){
		//获得page域对象获得不到(很少使用)
		PageContext page = ServletActionContext.getPageContext();
		//获得request
		HttpServletRequest request = ServletActionContext.getRequest();
		//获得response
		HttpServletResponse response = ServletActionContext.getResponse();
		//获得session
		HttpSession session = request.getSession();
		//获得application
		ServletContext sc = ServletActionContext.getServletContext();
		System.out.println(page);
		System.out.println(request);
		System.out.println(response);
		System.out.println(session);
		System.out.println(sc);
		return super.SUCCESS;
	}

通过实现接口的方式来获得servletAPI

实现
org.apache.struts2.interceptor.ServletRequestAware;
org.apache.struts2.interceptor.ServletResponseAware;
org.apache.struts2.interceptor.SessionAware;
org.apache.struts2.util.ServletContextAware;

public class HelloAction1 extends ActionSupport implements 
ServletRequestAware, 
ServletResponseAware, ServletContextAware{

	HttpServletRequest request;
	
	HttpServletResponse response;
	
	ServletContext sc;
	
	public String hello(){
		System.out.println(request);
		String result = request.getParameter("name");
		System.out.println(result);
		System.out.println(request.getSession());
		System.out.println(response);
		System.out.println(sc);
		return super.SUCCESS;
	}

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

	@Override
	public void setServletResponse(HttpServletResponse response) {
		this.response = response;
	}

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

}

参数的封装

静态封装

在运行器不发生变化的数据,或者一些配置相关的数据可以做静态封装

Action动作类

public class PersonAction extends ActionSupport{
	
	/**
	 * 用于接收静态的参数,必须提供set和get方法
	 */
	private String name = "任亮";
	
	private String desc;
	
	public String getDesc() {
		return desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String hello(){
		System.out.println(name);
		System.out.println(desc);
		return super.SUCCESS;
	}
}

Struts.xml配置

<package name="person" extends="struts-default" namespace="/person">
		<action name="renliang" class="com.rl.action.PersonAction" method="hello">
			<!-- 
				param的name在Action中必须要有相应的属性
			 -->
			<param name="name">任亮老师</param>
			<param name="desc">讲javaee的互联网的技术</param>
			<result name="success">/success.jsp</result>
		</action>
	</package>

动态参数的封装

系统云期间用户提交表单,ajax,url访问

动作类充当模型对象

动作类和模型和为一体,我们可以在动作类中定义要接收的属性的值,对每个属性必须要提供set和get方法,动作类model中的属性的名字和表单中的name一定要一致,否则无法注入。

Jsp页面

<form action="${pageContext.request.contextPath }/person/renliang1" method="post">
		<table>
			<tr>
				<td>ID</td>
				<td><input type="text" name="id"></td>
			</tr>
			<tr>
				<td>姓名</td>
				<td><input type="text" name="name"></td>
			</tr>
			<tr>
				<td>性别</td>
				<td>
					<input type="radio" name="gender" value="1" checked="checked"><input type="radio" name="gender" value="2"></td>
			</tr>
			<tr>
				<td>地址</td>
				<td><input type="text" name="address"></td>
			</tr>
		</table>
		<input type="submit" value="提交">
	</form>

动作类model

public class PersonAction1 extends ActionSupport{
	
	/**
	 * 在Action的内部定义model的属性
	 * @return
	 */
	private int id;
	
	private String name;
	
	private int gender;
	
	private String address;
	
	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 int getGender() {
		return gender;
	}

	public void setGender(int gender) {
		this.gender = gender;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String hello(){
		System.out.println(id);
		System.out.println(name);
		System.out.println(gender);
		System.out.println(address);
		return super.SUCCESS;
	}
}
动作类和模型对象分离封装参数(推荐使用)

动作类充当模型对象Action既是是C又是M,可读性差,重用性差,很难维护,不推荐使用第一种。

这种方式接受参数对提交的表单中的name有要求,name需要使用Action中的model属性的名字加点再加上要接收的属性名:如 person.name

<form action="${pageContext.request.contextPath }/person/renliang2" method="post">
		<table>
			<tr>
				<td>ID</td>
				<td><input type="text" name="person.id"></td>
			</tr>
			<tr>
				<td>姓名</td>
				<td><input type="text" name="person.name"></td>
			</tr>
			<tr>
				<td>性别</td>
				<td>
					<input type="radio" name="person.gender" value="1" checked="checked"><input type="radio" name="person.gender" value="2"></td>
			</tr>
			<tr>
				<td>地址</td>
				<td><input type="text" name="person.address"></td>
			</tr>
		</table>
		<input type="submit" value="提交">
	</form>

动作类里面需要定义model的属性并且提供set和get

package com.rl.action;
import com.opensymphony.xwork2.ActionSupport;
import com.rl.model.Person;
/**
 * action动作类充当模型对象
 * @author renliang
 *
 */
public class PersonAction2 extends ActionSupport{
	
	/**
	 * 定义model类的对象用于接收参数
	 * 必须提供set和get
	 */
	private Person person;

	public Person getPerson() {
		return person;
	}

	public void setPerson(Person person) {
		this.person = person;
	}

	public String hello(){
		System.out.println(person);
		return super.SUCCESS;
	}
}
模型驱动方式封装参数(推荐使用)

第二种方式对页面上的文本域的name有要求必须要用model的属性的名称.属性名,这样的话页面和Action动作类有侵入性,模型驱动方式解决了这个问题,我们需要实现一个ModelDriven接口指定要接收的model的类型提供getModel的方法,Action类中的model对象必须要手动的创建,否则无法注入属性值。
在这里插入图片描述

<form action="${pageContext.request.contextPath }/person/renliang3" method="post">
		<table>
			<tr>
				<td>ID</td>
				<td><input type="text" name="id"></td>
			</tr>
			<tr>
				<td>姓名</td>
				<td><input type="text" name="name"></td>
			</tr>
			<tr>
				<td>性别</td>
				<td>
					<input type="radio" name="gender" value="1" checked="checked"><input type="radio" name="gender" value="2"></td>
			</tr>
			<tr>
				<td>地址</td>
				<td><input type="text" name="address"></td>
			</tr>
		</table>
		<input type="submit" value="提交">
	</form>

Action类

public class PersonAction3 extends ActionSupport implements ModelDriven<Person>{
	
	/**
	 * 定义model类的对象用于接收参数
	 * 必须提供set和get,model的对象必须手动的创建,否则框架无法注入属性
	 */
	private Person person = new Person();

	

	public Person getPerson() {
		return person;
	}
	public void setPerson(Person person) {
		this.person = person;
	}
	public String hello(){
		System.out.println(person);
		return super.SUCCESS;
	}

	@Override
	public Person getModel() {
		return person;
	}
}

类型转换

页面所提交过来的数据都是字符串的类型,而model里面的数据类型并不都是字符串,有各种各样的类型int ,Integer, float, String,date…,我们使用servlet来接收数据时都是自己手动转换,struts2可以为我们自动转换,转换的前提是前台提交的字符串和model中的相应的数据类型可以转换。

提交表单时:字符串-------→其他类型
页面展示:其他类型--------→字符串

表单提交时数据类的转换struts给我做了绝大多数,但是时间类型往往需要根据我们自己的需求来转换的。

自定义转换数据类型

在这里插入图片描述
日期转换:
默认情况下,struts2解析的时间的格式:“yyyy-MM-dd”

通过自定义类型转换器继承StrutsTypeConverter做日期转换器
public class MyDateConverter extends StrutsTypeConverter {

	@Override
	public Object convertFromString(Map context, String[] values, Class toClass) {
		Date date = null;
		if(values != null && values.length > 0){
			if(toClass == Date.class){
				try {
					date = new SimpleDateFormat("yyyy/MM/dd").parse(values[0]);
				} catch (ParseException e) {
					e.printStackTrace();
				}
			}
		}
		return date;
	}
	@Override
	public String convertToString(Map context, Object o) {
		String dateStr = null;
		if(o != null && o.getClass() == Date.class){
			dateStr = new SimpleDateFormat("yyyy/MM/dd").format((Date)o);
		}	
		return dateStr;
	}
}
注册日期类型转换器

局部注册
第一步:在要转换的model类的同级包中建立一个属性文件,命名要求:model类名-conversion.properties,在文件的内部key是要转换的属性名,value是转换器的全路径
在这里插入图片描述
全局注册
如果有很多的model类都有时间类型,那么使用局部类型转换器就需要创建很多注册文件,这样不合理。需要全局注册

方法:在classpath下一个属性文件:xwork-conversion.properties
在这里插入图片描述

把错误提示信息转换成中文
  1. 在Action类的同级包下创建一个属性文件, 规则:和Action类同名.properties
  2. Key: invalid.fieldvlaue.[要转换的表单中文本域的name:birthday],value是中文的提示信息,中文在properties文件中以Unicode的编码的方式

在这里插入图片描述

struts2数据验证

验证的方式

1.客户端校验:使用js结合正则表达式来校验,不和服务器打交道,开发简单,安全性差
2.服务器端校验:请求web服务器,开发量比较大,安全性好,如果需要和数据库到交到的就必须要使用服务器端校验
实际开发中,客户端校验比较多,最好是客户端和服务端都校验。

Struts2的校验属于服务器端校验

struts2编程式验证

编程校验
重写父类的validate方法,在这个方法中对每一个字段来做校验,但是校验的前提是提交过来的参数是能相互转换的,如果转换不了的话会由param,modelDriven拦截器来负责。如果参数不符合正则,我们可以添加提示信息addFieldError(“id”, “id必须是1到5位的数字”)

@Override
	public void validate() {	
		if(person.getId() != 0 && !(person.getId()+"").matches("\\d{1,3}")){
			addFieldError("id", "id必须是1到5位的数字");
		}
		if(!person.getName().matches("[a-zA-Z]{3,8}")){
			//添加提示信息,第一个参数表单中文本域的name,第二个是提示信息
			addFieldError("name", "姓名只能是3到8位大小字母");
		}
	}

跳出校验
在编程式校验中validate会对当前的Action中的每一个方法做校验,那么有一些方法时不需要校验的,我们可以通过@skipValidation来跳过校验。

@SkipValidation//跳出校验
	public String delete(){
		System.out.println(person);
		return super.SUCCESS;
	}

我们在Action中定义一个验证方法,命名规则是validate+要验证的方法名,那么这个方法就会被校验如validateSave,save方法就会被校验,其余的方法都不会被校验

public void validateSave() {
		if(person.getId() != 0 && !(person.getId()+"").matches("\\d{1,3}")){
			addFieldError("id", "id必须是1到5位的数字");
		}
		if(!person.getName().matches("[a-zA-Z]{3,8}")){
			//添加提示信息,第一个参数表单中文本域的name,第二个是提示信息
			addFieldError("name", "姓名只能是3到8位大小字母");
		}
	}

struts2声明式验证

为了解决Action和验证逻辑的高耦合,我们可以使用声明式验证,我们要把验证规则配置在xml中。

在Action的同级包下创建一个xml,名称规范:Action名称-validation.xml

打开xwork-validator1.0.3.dtd,把头信息拷贝到我们的xml中
在这里插入图片描述
我们对验证做配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
  		"-//Apache Struts//XWork Validator 1.0.3//EN"
  		"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
  		
<validators>
	<!-- 
		field:表单中要提交的文本域
		name:文本域的name
	 -->
	<field name="name">
		<!-- 
			文本域的验证器
		 -->
		<field-validator type="requiredstring">
			<message>姓名不能为空</message>
		</field-validator>
	</field>
</validators>

Struts2框架给我们提供了很多验证器,这些验证器的位置在xwork-core.2.3.15.jar/com/opensymphony/xwork2/validator/validators/default.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
        "-//Apache Struts//XWork Validator Definition 1.0//EN"
        "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">

<!-- START SNIPPET: validators-default -->
<validators>
    <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
    <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
    <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
    <validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
    <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
    <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
    <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
    <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
    <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
    <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
    <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
    <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
    <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
    <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
    <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
    <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
</validators>

系统提供的校验器如下:

  • required (必填校验器,要求被校验的属性值不能为null)
  • requiredstring (必填字符串校验器,要求被校验的属性值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
    -stringlength(字符串长度校验器,要求被校验的属性值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)
  • regex(正则表达式校验器,检查被校验的属性值是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指
<field name="name">
		<!-- 
			文本域的验证器
		 -->
		<field-validator type="requiredstring">
			<message>姓名不能为空</message>
		</field-validator>
		<field-validator type="regex">
			<param name="regex">[a-zA-Z]{3,8}</param>
			<message>姓名必须是3到8位的字符</message>
		</field-validator>
	</field>

第二种验证配置方式

<validator type="regex">
		<!-- 配置验证的字段 -->
		<param name="fieldName">password</param>
		<!-- 验证的正则表达式 -->
		<param name="regex">\d{6,8}</param>
		<message>请输入6到8位数字的密码</message>
</validator>
  • int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
  • double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
  • fieldexpression(字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
  • email(邮件地址校验器,要求如果被校验的属性值非空,则必须是合法的邮件地址)
  • url(网址校验器,要求如果被校验的属性值非空,则必须是合法的url地址)
  • date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
  • conversion(转换校验器,指定在类型转换失败时,提示的错误信息)
  • visitor(用于校验action中复合类型的属性,它指定一个校验文件用于校验复合类型属性中的属性)
  • expression(OGNL表达式校验器,它是一个非字段校验器, expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中)

struts2国际化

1.先来建立独立的包的资源文件

命名的规范:以msg开头后面加上国家的语言和国家名称的简写
msg_en_US.properties
msg_zh_CN.properties
在这里插入图片描述
2.配置国际化资源文件的加载

<!-- 
		加载资源文件做国际化,value指的是资源文件的包的路径后面加上msg,用/分隔
	 -->
	<constant name="struts.custom.i18n.resources" value="com/rl/resource/msg"></constant>

3.在页面上使用国际化的资源<s:text name=’属性文件的key’>

<form action="${pageContext.request.contextPath }/person/renliang" method="post">
		<table>
			<tr>
				<td>ID</td>
				<td><input type="text" name="id"></td>
				<td><s:fielderror fieldName="id"></s:fielderror></td>
			</tr>
			<tr>
				<td><s:text name="pname"/></td>
				<td><input type="text" name="name"></td>
				<td><s:fielderror fieldName="name"></s:fielderror></td>
			</tr>
			<tr>
				<td><s:text name="pgender"/></td>
				<td>
					<input type="radio" name="gender" value="1" checked="checked"><input type="radio" name="gender" value="2"></td>
				<td></td>
			</tr>
			<tr>
				<td><s:text name="paddr"/></td>
				<td><input type="text" name="address"></td>
				<td><s:fielderror fieldName="address"></s:fielderror></td>
			</tr>
			
			<tr>
				<td><s:text name="pbirth"/></td>
				<td><input type="text" name="birthday"></td>
				<td><s:fielderror fieldName="birthday"></s:fielderror></td>
			</tr>
		</table>
		<input type="submit" value="<s:text name='submit'/>">
	</form>

这样会通过浏览器的语言环境生成不同的文本
在这里插入图片描述
可以通过设置语言首选项来验证

sruts2拦截器

Struts2的核心就是拦截器,param,modelDriven,validation,servletAPI等等这些都是拦截器完成的功能。
在这里插入图片描述
在这里插入图片描述
服务器启动时实例化StrustPrepareAndExecuteFilter,读取struts所有的配置文件,把struts.xml中的每一个Action实例化。

当一个请求访问的时候,StrustPrepareAndExecuteFilter会把请求拦截下来,匹配一个ActionMapper,然后ActionMapper创建ActionProxy。

然后去执行默认拦截器栈,再执行Action,然后返回结果,然后默认拦截器再反向执行(做一些运行期的监控和清理工作,不是每一个拦截器都执行后置拦截),最后响应结果返回给页面。

自定义拦截器

在这里插入图片描述
创建拦截器类

public class MyInterceptor extends AbstractInterceptor {

	@Override
	public String intercept(ActionInvocation invocation) throws Exception {
		System.out.println("前置拦截执行....");
		//让拦截器向下走并且返回结果代码
		String result = invocation.invoke();
		System.out.println("后置拦截执行...");
		return result;
	}
}

拦截器的配置

<package name="person" extends="struts-default" namespace="/person">
		<!-- 拦截器配置 -->
		<interceptors>
			<interceptor name="myInterceptor" class="com.rl.interceptor.MyInterceptor"></interceptor>
		</interceptors>
		<action name="renliang" class="com.rl.action.PersonAction" method="save">
			<result name="success">/success.jsp</result>
			<result name="input">/form.jsp</result>
		</action>
		<action name="delete" class="com.rl.action.PersonAction" method="delete">
			<!-- 引用拦截器 -->
			<interceptor-ref name="myInterceptor"></interceptor-ref>
			<result name="success">/success.jsp</result>
			<result name="input">/form.jsp</result>
		</action>
	</package>

我们从结果上可以看懂执行的顺序:
前置拦截–→action—→页面执行–→后置拦截
在这里插入图片描述

拦截器和拦截器栈的配置

<package name="myStruts-Default" extends="struts-default" abstract="true">
		<interceptors>
			<!-- 
				配置我们自己的拦截器
			 -->
			<interceptor name="myInterceptor" class="com.rl.interceptor.MyInterceptor"></interceptor>
			<!-- 
				拦截器栈:是把多个拦截器集中到一起统一被引用
			 -->
			<interceptor-stack name="rlStack">
				<!-- 
					拦截器栈中要引用已经配置好的拦截器或者拦截器栈
				 -->
				<interceptor-ref name="defaultStack"></interceptor-ref>
				<interceptor-ref name="myInterceptor"></interceptor-ref>
			</interceptor-stack>
		</interceptors>
		<!-- 
			把rlStack作为默认拦截器,只要继承了myStruts-Default,执行Action时就会自动先执行rlStack中的所有拦截器
		 -->
		<default-interceptor-ref name="rlStack"/>
		
	</package>
	<package name="person" extends="myStruts-Default" namespace="/person">
		
		<!-- 拦截器配置 -->
		<action name="renliang" class="com.rl.action.PersonAction" method="save">
			<result name="success">/success.jsp</result>
			<result name="input">/form.jsp</result>
		</action>
		<action name="delete" class="com.rl.action.PersonAction" method="delete">
			<result name="success">/success.jsp</result>
			<result name="input">/form.jsp</result>
		</action>
	</package>
	<package name="person1" extends="myStruts-Default" namespace="/person1">
		
		<!-- 拦截器配置 -->
		<action name="renliang1" class="com.rl.action.PersonAction1" method="save">
			<result name="success">/success.jsp</result>
			<result name="input">/form.jsp</result>
		</action>
	</package>

排除Action的拦截

要想排除被拦截的Action的执行方法,拦截器需要继承MethodFilterInterceptor

public class MyInterceptor1 extends MethodFilterInterceptor {

	@Override
	protected String doIntercept(ActionInvocation invocation) throws Exception {
		System.out.println("前置拦截执行1....");
		//让拦截器向下走并且返回结果代码
		String result = invocation.invoke();
		System.out.println("后置拦截执行1...");
		return result;
	}
}

配置Action的排除

<package name="myStruts-Default" extends="struts-default" abstract="true">
		<interceptors>
			<!-- 
				配置我们自己的拦截器
			 -->
			<interceptor name="myInterceptor" class="com.rl.interceptor.MyInterceptor"></interceptor>
			<interceptor name="myInterceptor1" class="com.rl.interceptor.MyInterceptor1"></interceptor>
			<!-- 
				拦截器栈:是把多个拦截器集中到一起统一被引用
			 -->
			<interceptor-stack name="rlStack">
				<!-- 
					拦截器栈中要引用已经配置好的拦截器或者拦截器栈
				 -->
				<interceptor-ref name="defaultStack"></interceptor-ref>
				<interceptor-ref name="myInterceptor"></interceptor-ref>
				<interceptor-ref name="myInterceptor1"></interceptor-ref>
			</interceptor-stack>
		</interceptors>
		<!-- 
			把rlStack作为默认拦截器,只要继承了myStruts-Default,执行Action时就会自动先执行rlStack中的所有拦截器
		 -->
		<default-interceptor-ref name="rlStack"/>
		
	</package>
	<package name="person" extends="myStruts-Default" namespace="/person">

		<action name="renliang" class="com.rl.action.PersonAction" method="save">
			<result name="success">/success.jsp</result>
			<result name="input">/form.jsp</result>
		</action>
		<action name="delete" class="com.rl.action.PersonAction" method="delete">
			<!-- 当前Action需要主动管理默认拦截器栈 -->
			<interceptor-ref name="rlStack">
				<!-- 
					指定要排除的拦截器
					name:要排除的拦截器的名字.exludeMethods
					元素:要排除的方法
				 -->
				<param name="myInterceptor1.excludeMethods">delete</param>
			</interceptor-ref>
			<result name="success">/success.jsp</result>
			<result name="input">/form.jsp</result>
		</action>
	</package>

struts2的上传和下载

单文件上传

上传文件对表单的要求:

  • 表单要使用post方式提交
  • 表单的enctype是multipart/form-data
  • 表单中要有file类型的input文本域

Struts上传也是基于拦截器,底层还是使用commons-fileupload组件

Struts上传的步骤

1.建立表单

<form action="${pageContext.request.contextPath }/upload/renliang" method="post" enctype="multipart/form-data">
	姓名:<input type="text" name="username"><br>
	文件:<input type="file" name="upload"><br>
	<input type="submit">
</form>

2.创建Action
注意:
接收的文件属性:和表单中文本域file类型input的name一致
接收的文件名字:file的name+FileName固定写法
接收的文件MIME类型:file的name+ContentType

public class UploadAction extends ActionSupport{
	

	private String username;
	
	/**
	 * 要接收的文件,命名需要和表单中的file类型的input的name一致
	 */
	private File upload;
	
	/**
	 * 文件名的接收 File属性名FileName:固定写法
	 */
	private String uploadFileName;
	
	/**
	 * 获得上传文件的MIME类型,File属性名字ContentType:固定写法
	 */
	private String uploadContentType;

	public String getUsername() {
		return username;
	}

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

	public File getUpload() {
		return upload;
	}

	public void setUpload(File upload) {
		this.upload = upload;
	}

	public String getUploadFileName() {
		return uploadFileName;
	}

	public void setUploadFileName(String uploadFileName) {
		this.uploadFileName = uploadFileName;
	}

	public String getUploadContentType() {
		return uploadContentType;
	}

	public void setUploadContentType(String uploadContentType) {
		this.uploadContentType = uploadContentType;
	}
	
	
	public String upload() throws Exception{
		//获得servletContext
		ServletContext sc = ServletActionContext.getServletContext();
		//获得服务的绝对路径
		String realPath = sc.getRealPath("/");
		realPath = realPath + "upload\\"+uploadFileName;
		//定义输入输出流
		InputStream in = new FileInputStream(upload);
		OutputStream out = new FileOutputStream(realPath);
		int len = -1;
		byte[] bs = new byte[1024];
		while((len = in.read(bs)) != -1){
			out.write(bs, 0, len);
		}
		out.close();
		in.close();
		return super.SUCCESS;
	}
}

文件上传类型的验证

<package name="upload" extends="struts-default" namespace="/upload">
		
		<!-- 拦截器配置 -->
		<action name="renliang" class="com.rl.action.UploadAction" method="upload">
			<!-- 
				主动引用默认拦截器栈
			 -->
			<interceptor-ref name="defaultStack">
				<!-- 设置上传拦截器fileUpload.allowedExtensions,  不要使用allowedExtensionsSet -->
				<param name="fileUpload.allowedExtensions">.png,.txt</param>
			</interceptor-ref>
			<result name="success">/success.jsp</result>
			<result name="input">/form.jsp</result>
		</action>
	</package>

文件上传的大小验证

Struts2默认的上传文件的配置在default.properties的文件中,由struts.multipart.maxSize=2097152控制

我们发现展示并不是中文,我们使用国际化来处理

文件超过最大值的提示信息是在struts_message.propertise中,我们只要通过国际化文件覆盖它即可
在这里插入图片描述
我们创建国际化文件
在这里插入图片描述
在这里插入图片描述

多文件的上传

多文件上传Action中把File属性以及和file属性相关的上传文件都变成数组,在execute中循环上传即可

public class UploadsAction extends ActionSupport{
	

	private String username;
	
	/**
	 * 要接收的文件,命名需要和表单中的file类型的input的name一致
	 */
	private File[] upload;
	
	/**
	 * 文件名的接收 File属性名FileName:固定写法
	 */
	private String[] uploadFileName;
	
	/**
	 * 获得上传文件的MIME类型,File属性名字ContentType:固定写法
	 */
	private String[] uploadContentType;
	
	public String getUsername() {
		return username;
	}

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

	public File[] getUpload() {
		return upload;
	}

	public void setUpload(File[] upload) {
		this.upload = upload;
	}

	public String[] getUploadFileName() {
		return uploadFileName;
	}

	public void setUploadFileName(String[] uploadFileName) {
		this.uploadFileName = uploadFileName;
	}

	public String[] getUploadContentType() {
		return uploadContentType;
	}

	public void setUploadContentType(String[] uploadContentType) {
		this.uploadContentType = uploadContentType;
	}
	
	public String upload() throws Exception{
		//获得servletContext
		ServletContext sc = ServletActionContext.getServletContext();
		//获得服务的绝对路径
		String realPath = sc.getRealPath("/");
		for(int i = 0; i < upload.length; i++){
			realPath = realPath + "upload\\"+uploadFileName[i];
			//定义输入输出流
			FileUtils.copyFile(upload[i], new File(realPath));
		}
		return super.SUCCESS;
	}	
}

文件下载

Struts文件下载对动作类Action有要求,在Action之中必须提供三个属性:

  • 提供一个输入流的属性,名字叫inputStream固定
    private InputStream inputStream;
  • 定义文件的大小
    private int filelength;
  • 定义文件名
    private String fileName;

public class DownAction extends ActionSupport{
	
	//提供一个输入流的属性,名字叫inputStream固定
	private InputStream inputStream;
	
	//定义文件的大小
	private int filelength;
	
	//定义文件名
	private String fileName;

	public InputStream getInputStream() {
		return inputStream;
	}

	public void setInputStream(InputStream inputStream) {
		this.inputStream = inputStream;
	}

	public int getFilelength() {
		return filelength;
	}

	public void setFilelength(int filelength) {
		this.filelength = filelength;
	}

	public String getFileName() {
		return fileName;
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}
	
	public String down() throws Exception{
		ServletContext sc = ServletActionContext.getServletContext();
		String realPath = sc.getRealPath("/upload/1.png");
		//实例化输入流
		inputStream = new FileInputStream(realPath);
		//给fileName赋值
		fileName = FilenameUtils.getName(realPath);
		//对文件名做编码
		fileName = URLEncoder.encode(fileName, "UTF-8");
		//给文件大小赋值
		filelength = inputStream.available();
		return super.SUCCESS;
	}
}

Action的配置

<action name="renliang" class="com.rl.action.DownAction" method="down">
			<result name="success" type="stream">
				<!-- 指定Action中输入流变量 -->
				<param name="inputName">inputStream</param>
				<!-- 设置响应的消息头 Content-Disposition -->
				<param name="contentDisposition">attachment;filename=${fileName}</param>
				<!-- 使用下载的方式来返回结果 -->
				<param name="contentType">application/octet-stream</param>
				<!-- 配置文件的大小 -->
				<param name="contentLength">${filelength}</param>
			</result>
</action>

${fileName}${filelength}是取得action中的属性值

ognl表达式

OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。

作用:取值,获取javaBean中的属性,获取List或者数组元素,获得map的键值对,还可以执行逻辑运算。

要求:我们必须把ognl表达式写在struts的标签中。

ognl对普通方法的调用

<%-- 	在<s:properties >的value属性中""内部是ognl表达式,如果要输出字符串要加''
 --%>
<s:property value="'renliang'"/>
<%-- 	在<s:properties >的value属性中""内部是ognl表达式,可以使用java的api
 --%>
<s:property value="'renliang'.toUpperCase()"/>

ognl对静态变量和静态方法的调用

静态变量和静态方法的调用都要使用@类的全路径@[静态变量或静态方法],但是如果是静态方法的调用必须要先开启

<!-- 开启ognl对静态方法的调用 -->
	<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>

调用

<s:property value="@java.lang.Integer@MAX_VALUE"/>
<s:property value="@java.lang.Math@abs(-100)"/>

ActionContext

context map:是每次请求访问时存储数据的对象,每一次请求都会创建context map,我们可以把数据来存放到map中。
在这里插入图片描述

KeyValue说明
Value stackList集合以栈的方式来存储数据
RequestMap<String,Object>结构以键值对的方式存储请求范围的数据
sessionMap<String,Object>结构以键值对的方式存储会话范围的数据
ApplicationMap<String,Object>结构以键值对的方式存储应用范围的数据
ActionObject类型当前访问的Action动作类对象
ParametersMap<String,Object>结构存储请求的参数
AttrMap<String,Object>结构根据key从page,request,session,application范围依次查找属性的值

每一个请求访问时都会创建一个contextMap的对象,ValueStack和contextmap的数据是关联的可以相互转化

我们的数据采用两个存储的结构来存储一个valueStack(set(map)),contextMap(map)

在context map中存取数据(常用)

在请求范围内存储数据

@Override
	public String execute() throws Exception {
		System.out.println(username);
		//获得动作类的上下文ActionContext包含了contextmap和valuestack
		ActionContext ac = ServletActionContext.getContext();
		//在contextmap中存储数据,默认的相当于request,因为ActionContext的生命周期和request一样都是一次请求
		ac.put("name", "renliang");
		return super.execute();
	}

在contextmap中取数据要使用#

<s:property value="#name"/>
	<s:debug></s:debug>

在这里插入图片描述
在会话范围存储数据

@Override
	public String execute() throws Exception {
		System.out.println(username);
		//获得动作类的上下文ActionContext包含了contextmap和valuestack
		ActionContext ac = ServletActionContext.getContext();
		//在contextmap中存储数据,默认的相当于request,因为ActionContext的生命周期和request一样都是一次请求
		ac.put("name", "renliang");
		//把数据存储在会话范围
		ac.getSession().put("user", "任亮我是一个javaee的互联网A级讲师");
		return super.execute();
	}

取值时语法

<s:property value="#session.user"/>
<s:debug></s:debug>

在这里插入图片描述
在应用级别的存储数据

@Override
	public String execute() throws Exception {
		System.out.println(username);
		//获得动作类的上下文ActionContext包含了contextmap和valuestack
		ActionContext ac = ServletActionContext.getContext();
		//在contextmap中存储数据,默认的相当于request,因为ActionContext的生命周期和request一样都是一次请求
		ac.put("name", "renliang");
		//把数据存储在会话范围
		ac.getSession().put("user", "任亮我是一个javaee的互联网A级讲师");
		//在应用范围内存数据
		ac.getApplication().put("pv", 12345);
		return super.execute();
	}

取数据

<s:property value="#application.pv"/>
	<s:debug></s:debug>

在这里插入图片描述

在值栈中存取数据

把请求传递的参数存储在值栈中,同时把Action对象也压入栈中,属性对象在上面Action在下面,对象的值既可以从栈顶取,也可以从Action的person属性中来取
在这里插入图片描述
在这里插入图片描述
从值栈中取数据ognl表达式不需要#

<s:property value="username"/>
	<s:property value="person.personId"/>
	<s:property value="person.personName"/>
	<s:property value="person.gender"/>

手动通过栈放入的数据也会放入栈顶,但是在Action对象中不会存储在相应属性数据
在这里插入图片描述

使用注意(重要)

在方法中传过去的值在取的时候要加#号,在action类中定义的属性值取的时候不需要加#号

struts2的标签库(重要)

首先要引入struts2的标签库

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

分支判断

If elseif else使用 test内部是ognl表达式取值

<s:if test="#age < 16">
		<s:property value="'小孩'"/>
	</s:if>
	<s:elseif test="#age >= 16 && #age < 18">
		<s:property value="'未成年'"/>
	</s:elseif>
	<s:else>
		<s:property value="'成年人'"/>
</s:else>

循环

使用<s:iterator>标签

属性:

  • Value:是从ActionContext中获取的集合key,不需要#
  • Var:是每次从集合中取值赋值的变量

使用的时候需要加#,例如#str

<h3>循环数组</h3>
	<s:iterator value="arrStr" var="str">
		<s:property value="#str"/>
	</s:iterator>
	<hr>
	<h3>循环list</h3>
	<s:iterator value="list" var="str">
		<s:property value="#str"/>
	</s:iterator>
	<hr>
	<h3>循环map</h3>
	<s:iterator value="map" var="mapObj">
		<s:property value="#mapObj.key"/>------><s:property value="#mapObj.value"/><br>
	</s:iterator>

对于对象集合循环

Status属性可以给循环设置参数:

  • Index:当前循环的索引号,从0开始
  • Count:当前循环的顺序号,从1开始
  • First:是否是第一行
  • List:是否是最后一行
  • odd:是否是奇数
  • even:是否是偶数
  • begin:从数字几开始
  • end:到数字几结束
  • step:步长
<table border="1">
		<tr>
			<th>id</th>
			<th>姓名</th>
			<th>性别</th>
			<th>索引</th>
			<th>序号</th>
			<th>是否首行</th>
			<th>是否尾行</th>
			<th>奇数</th>
			<th>偶数</th>
		</tr>
		<s:iterator value="personList" var="person" status="status">
		<tr bgcolor='<s:property value="#status.odd?'#c3f3c3':'#f3c3f3'"/>'>
			<td><s:property value="#person.personId"/></td>
			<td><s:property value="#person.personName"/></td>
			<td><s:property value="#person.gender == 1?'':''"/></td>
			<td><s:property value="#status.index"/></td>
			<td><s:property value="#status.count"/></td>
			<td><s:property value="#status.first"/></td>
			<td><s:property value="#status.last"/></td>
			<td><s:property value="#status.odd"/></td>
			<td><s:property value="#status.even"/></td>			
		</tr>
		</s:iterator>
</table>

在这里插入图片描述

输出标签(重要)参考上面的ognl表达式的ActionContext

<S:property>

属性:

  • Value:用于通过ognl表达式来取值
  • Default:如果value值是空就给一个默认值
  • EscapeHtml:是否被浏览器解析,默认是true不解析,false 是解析
<s:property value="#name"/>
<s:property value="#name1" default="空值"/>
<s:property value="'<a href>任亮</a>'" escapeHtml="false"/>

日期输出标签(重要)

<s:date>

属性:

  • Name:取日期的ognl表达式的值
  • Format:要展示的日期的格式
<h3>日期输出标签</h3>
<s:date name="#ctime" format="yyyy-MM-dd HH:mm:ss"/>

页面动态包含

<s:action>

属性:

  • Name:要请求的Action
  • ExecuteResult:是否展示Action的执行结果,true是展示,false不展示
<h3>页面包含</h3>
<s:action name="hello1" executeResult="true"></s:action>

超链接标签

<s:a>

属性:

  • Action:要链接的动作类的名称,标签会在Action的值后面自动的加上后缀
  • <s:param>是<s:a>内部的元素,param主要是给a链接赋予参数的,可以自动的对中文编码
<h3>超链接</h3>
<s:a action="hello">我是a链接
	<s:param name="username" value="'我是谁'"></s:param>
	<s:param name="job" value="'teacher'"></s:param>
</s:a>

那么请求的链接如下

http://localhost:8080/struts2_18/hello.action?username=%E4%BB%BB%E4%BA%

struts2对el表达式的支持

${name}:调用pageContext.findAttribute(“name”),从页面,请求,会话,应用范围去查找key=name对应的值

<table border="1">
		<tr>
			<th>id</th>
			<th>姓名</th>
			<th>性别</th>
			<th>索引</th>
			<th>序号</th>
			<th>是否首行</th>
			<th>是否尾行</th>
			<th>奇数</th>
			<th>偶数</th>
		</tr>
		<s:iterator value="personList" var="p" status="status">
		<tr bgcolor="${status.odd?'#c3f3c3':'#f3c3f3'}">
			<td>${p.personId }</td>
			<td>${p.personName }</td>
			<td>${p.gender == 1?'男':'女' }</td>
			<td>${status.index}</td>
			<td>${status.count}</td>
			<td>${status.first}</td>
			<td>${status.last}</td>
			<td>${status.odd}</td>
			<td>${status.even }</td>
		</tr>
		</s:iterator>
	</table>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值