struts2学习笔记

用于学习与交流,视频课件原作者为黑马程序员,侵立删

struts2概述

1、struts2框架应用javaee三层结构中的web框架
2、struts2框架在struts1和webwork基础之上
3、struts2使用过滤器拦截请求,在根据不同情况执行不同的action
4、struts2版本
5、web层框架:struts、springMVC

struts框架入门:

1、导入jar包,建web项目。不能把lib文件夹中jar的全部导入,到apps目录中示例程序,从示例程序中复制相关的jar包。解压war包,找出里面的jar包
2、创建action。
2.1、访问servlet时先执行service方法再执行doGet等方法。继承HttpServlet方法,重写类里面的方法,在web.xml中配置servlet的访问路径。
2.2、访问action时,每次访问action时,默认执行execute方法。类似于servlet,也要配置action的访问路径。
3、配置action的访问路径
3.1、创建struts的核心配置文件,核心配置文件名称和位置是固定的,即src/struts.xml
3.2 引入dtd约束,可以在struts的示例程序中找到struts.xml文件,复制dtd约束。
3.3 创建struts标签。

//类文件
package cn.itcast.action;
public class HelloAction{
	public string execute(){
		return "ok";
	}
}
//struts.xml文件
<struts>
	<package name="hellodemo" extends="struts-default" namespace="/">
		<!-- name:访问名称 -->
		<action name="hello" class="cn.itcast.action.HelloAction">
			<!-- 配置方法的返回值到页面 -->
			<result name="ok">/hello.jsp</result>
		</action>
	</package>
</struts>
//hello.jsp
//...
<body>
	<h1>hello struts...</h1>
</body>
//...

4.配置过滤器
在web.xml中配置过滤器,可以在struts示例程序中找到web.xml,复制其中的以及标签
使用idea配置strut2时,要注意删除struts2-rest-plugin-2.5.20.jar包,否则就无法执行action,还要注意修改web.xml和struts.xml时要注意版本号要匹配。

struts执行的基本过程

在浏览器地址栏输入一个地址并回车之后,客户端会向服务端发送一个http请求,该请求首先被送达过滤器。
在过滤器中所做的处理:
第一步,获得请求的路径,获得路径中包含的action名。
第二步,在src目录下找struts.xml文件并使用DOM4j对其进行解析,找到其中的action标签name属性值,与路径中的action名称进行匹配。
第三步,如果匹配成功,再通过action标签的class属性得到action的全路径,利用反射可以实现功能。

//反射的代码:
Class clazz = Class.forName("Action的全路径");//得到类对应的Class对象
Method method = clazz.getMethod("execute");//得到类对应方法的Method对象
Object obj = method.invoke();//使用invoke方法执行类的方法

第四步,得到action中execute的返回值,根据返回值在struts.xml中action标签下result标签的name属性,如果匹配成功就跳转到该页面。
查看源代码:
StrutsPrepareAndExecuteFilter是过滤器,实现了StrutsStatics和Filter接口,过滤器中有三个方法,分别是init、doFilter和destroy方法。
过滤器在服务器启动的时候创建,创建过滤器时会执行里面的init方法。init方法加载主要配置文件,包含自定义和struts自带的配置文件
主要关注struts.xml和web.xml文件

Struts核心配置文件

名称和位置是固定的,名称为struts.xml,位置为src下。
主要有三个标签package、action、result。

package标签:

1、package用来区分不同action,类似与Java代码中的包,在package里面才能进行action的配置。
2、package标签的属性:
2.1、name属性:
name属性值跟功能本身无关,起标识package的作用,name属性值是唯一的。
2.2、extends属性:
extends属性表示继承,固定值为struts-default,写了这个属性后,在package中配置的类才具有了action的功能。
2.3、namespace属性
namespace属性指名称空间,namespace属性值与action中的name属性值一起构成访问的路径,默认为“/”,可以不写。

action标签:

1、action标签的作用是配置action的访问路径
2、action标签属性:
2.1、name属性
package的namespace属性值与action标签的name属性值一起构成访问路径,一个package可以有很多action标签,但name属性值是唯一的。
2.2、class属性
action类的全路径,包名+类名
2.3、method属性
action默认执行execute方法,使用method方法可以配置action执行的方法,以便让action执行多个方法。

result标签:

1、作用:根据action中方法的返回值,跳转到其他路径(页面或其他action)
2、result标签的属性:
2.1、name属性
和方法的返回值一样,表示跳转到的路径,如/hello.jsp
2.2、type属性
配置如何到路径中去,可以转发或重定向,type属性的默认值是转发操作。转发不改变url,重定向发起新的页面请求。

常量配置:

1、struts2框架,帮我们实现了一部分功能,struts2中有常量,在常量中封装了一部分功能
2、struts默认的常量位置:
default.properties
3、修改struts2默认常量值
3.1 常用的方式:
在struts.xml中配置。

<constant name="struts.i18n.encoding" value="UTF-8"></constant>

name和value表示常量的名称-值对
3.2、其他方式:创建struts.properties文件并配置,在web.xml中进行配置,一般用的较少。
4、最常用的常量
struts.i18n.encoding=UTF-8
4.1、表单提交数据到action里面,在action可以获取表单提交数据
4.2、表单提交数据有中文,有乱码问题

  • post提交直接设置编码
  • get提交做编码转换
    4.3、如果在action获取表单通过post方式提交中文,中文乱码问题已经解决了,不需要自己处理,但通过get方式提交还是需要自己做编码转换。

分模块的开发:

应用场景:协同开发,如果开发团队规模较大,修改同一个struts.xml就很乱。
单独写一个配置文件,所有人都把自己的配置文件引入到核心的配置文件如struts.xml。

<inlcude file="cn/itcast/action/hello.xml"></inlcude>

action的编写方式:

1、action编写有三种方式
第一种,创建普通类,不继承类也不实现任何接口。

public class HelloAction{
	//...
}

第二种,创建一个类,实现接口Action,注意是在xwork下的Action。第二种方式用的并不是很多,因为接口中的方法都要实现。

public class UserAction implements Action{
	@Override
	public String execute() throws Exception{
		//...
		return "success";//返回的页面
		//Action中提供了一些常量,可以作为直接返回值,如SUCCESS、NONE等
	}
}

第三种,创建一个类,继承ActionSupport类,注意是xwork下的ActionSupport,ActionSupport本身也实现了Action接口。

public class Person extends ActionSupport{
	@Override
	public String execute() throws Exception{
		//...
		return "success";
	}
}

访问action中的方法:

1、有三种方式实现action中方法的访问
第一种,使用action标签的method属性指定方法。缺点是如果访问的方法比较多,每个方法都需要进行配置,比较麻烦。
第二种,使用通配符方式实现
第三种,使用动态方法访问(几乎不用)
2、演示错误:
如果action中execute的返回值在struts.xml中没有配置,就会报404错误。
execute的方法如果有返回值类型必须是String类型,也可以没有返回值,那么在result中就不需要配置,做法:返回值类型设为void(不建议)或返回NONE或"none"。

//创建action,定义多个方法:
package cn.itcast.method;
import com.opensymphony.xwork2.ActionSupport;
public class BookAction extends ActionSupport{
	public String add(){
		System.out.println("add........);
		return NONE;
	}
	
	public String update(){
		System.out.println("update.........)
		return NONE;
	}
}
<!-- 普通方法 -->
<package name="methoddemo" extends="struts-default" namespace="/">
	<action name="addAction" class="cn.itcast.method.BookAction" method="add"></action>
	<action name="updateAction" class="cn.itcast.method.BookAction" method="update"></method>
</package>
<!-- 第二种方式,使用通配符。
	在action标签的name属性中使用*符号,*号的意思是可以匹配任意的内容。
-->
<package name="hellodemo" extends="struts-default" namespace="/">
	<!-- 
		name属性里面写符号*
		执行action里面的add方法,访问book_add,
		执行action里面的update方法,访问book_update,
		book_add,book_update都能够使用book_*匹配到。
		在class属性中使用占位符{n}取得第n个*号替代的内容。
	-->
	<action name="book_*" class="cn.itcast.method.BookAction" method="{1}"></action>
</package>

原帖作者:CRitsu 链接:https://www.jianshu.com/p/543c75ab7a2e
struts2.5版本引入了新的安全限制(据说是2.3版本开始,没去确认)新版本通配符需要配置才能使用
两种配置方法:
1、关闭严格方法调用
<package name = “default” namespace="/" extends=“struts-default” strict-method-invocation=“false”>
放弃新引进的DMI机制,一劳永逸,但有风险(不过想想原来都是这样用的……)
**2、设定方法白名单。**第二种方式需要维护方法白名单,是官方推荐的做法
//设定全局允许通行的方法
<global-allowed-methods>method1,method2</global-allowed-methods>
//每个action单独设定允许通行的方法
<action name=“test_*” method="{1}" class=“com.test.TestClass”>
    <result name=“success”>{1}page.jsp</result>
    <allowed-methods>method1,metho2</allowed-methods>
</action>
//注解加在action类上
@AllowedMethods(“method”)
public class TestAction extends ActionSupport {
    //…
}

<!-- 第三种方式:动态方法访问
动态方法访问在Struts2中默认是不开启的,如果想要使用必须先开启一个常量 -->
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<!-- 页面路径写法 -->
<a href="${pageContext.request.contextPath}/userAction!save.action">添加用户</a><!-- 注意!的用法-->

结果页面的配置:

全局结果页面

result标签用来配置action方法的返回值到不同路径中去。
创建两个action,执行默认的方法execute,让两个action的execute方法都返回success,返回success之后,配置到同一个页面中去。

//BookAction.java
public class BookAction extends ActionSupport{
	@Override
	public String execute(){
		return SUCCESS;
	}
}
//OrdersAction.java
public class OrdersAction extends ActionSupport{
	@Override
	public String execute(){
		return SUCCESS;
	}
}
<!-- struts.xml -->
<package name="demo1" extends="struts-default" namespace="/">
	<action name="book" class="cn.itcast.action.BookAction>
		<result name="success">/hello.jsp</result>
	</action>
	<action name="order" class="cn.itcast.action.OrdersAction>
		<result name="success">/hello.jsp</result>
	</action>
</package>

可以看出result标签重复写了两遍,如果action个数较多这一问题会更加明显,使用全局结果页面可以克服这一问题,减少配置代码的冗余。

<!-- struts.xml -->
<package name="demo1" extends="struts-default" namespace="/">
	<global-results>
		<result name="success">/hello.jsp</result>
	</global-results>
	<action name="book" class="cn.itcast.action.BookAction>
		<!-- <result name="success">/hello.jsp</result> -->
	</action>
	<action name="order" class="cn.itcast.action.OrdersAction>
		<!-- <result name="success">/hello.jsp</result> -->
	</action>
</package>

局部结果页面

在action标签中添加的result标签中的页面就是局部结果页面。
既配置了全局结果页面,又配置了同名的局部结果页面,那么以局部结果页面为准。

<action name="order" class="cn.itcast.action.OrdersAction>
	<result name="success">/hello.jsp</result>
</action>

result标签的type属性

type属性的作用是配置跳转到路径中的方式,包括dispatcher、redirect、chain、redirectAction。其中dispatcher和redirect针对的是页面中的配置,如果要配置到其他的action中去,使用chain和redirectAction。

  • dispatcher是默认值,表示转发。转发不会导致发起新的请求,地址栏url不会改变。
  • redirect表示重定向。重定向会导致发起新的请求,地址栏的url发生改变。
  • chain的作用是转发到action中,因为缓存问题,一般并不使用。
  • redirectAction的作用是重定向到action。
<result name="book" type="dispatcher">hello.jsp</result>
<result name="book" type="redirect">hello.jsp</result>
<result name="book" type="chain">xxx.action</result>
<result name="book" type="redirectAction">xxx.action</result>

转发或重定向到action时要注意result标签的name属性值为action的访问路径。

<action name="book" class="cn.itcast.action.BookAction>
	<result name="redirectAction">orders</result>
	<!-- 注意,这里要写action的访问路径,而不是项目的文件路径 -->
</action>
<action name="orders" class="cn.itcast.action.OrdersAction>
	<result name="success">/hello.jsp</result>
</action>

在action中获取表单提交数据

【回忆】提交表单到servlet里面,在servlet里面使用request对象里面的方法获取,getParameter、getParameterMap。现在提交表单到action中,但action中没有request对象,不能直接使用request获取表单数据。
action中获取request对象的方式有三种:ActionContext类、ServletActionContext类、接口注入,这些类和接口都是Struts2封装好的,但底层都使用了Servlet的Request对象。

使用ActionContext类

static ActionContext getContext():获得ActionContext对象。
HttpParameters getParameters():获得表单数据集合。该方法不是静态方法,需要创建ActionContext对象。要注意不使用new操作符创建对象,而是调用getContext()静态方法。

视频课件中的Map<String,Object>应该更改为HttpParameters

具体步骤:创建表单,并提交表单到action中,在action中使用ActionContext对象获取表单数据

<!-- form.html -->
<form action="${pageContext.request.contextPath}/form1.action" method="post">
	username:<input type="text" name="username" /><br/>
	password:<input type="text" name="password" /><br/>
	address:<input type="text" name="address" /><br/>
	<input type="submit" value="提交" />
</form>
//Form1DemoAction.java
public class Form1DemoAction extends ActionSupport{
	@Override
	public String execute() throws Exception{
		//第一种方式 使用ActionContext类获取
		//1.获取ActionContext对象
		ActionContext context = ActionContext.getContext();
		//2.key是表单输入项name属性值,value是表单输入值
		HttpParameters params = context.getParameters();//旧版本中返回值类型为Map<String,Object>
		Set<String> keys = map.KeySet();
		for(String key:keys){
			//根据key得到value,返回值为Parameter类型,旧版本中为Object[]。
			Parameter param = params.get(key);
			System.out.println(key + " = " + param);
		}
		return NONE;
	}
}
<!-- struts.xml -->
<!-- 获取表单数据 -->
<package name="demo2" extends="struts-default" namesapce="/">
	<action name="form1" class="cn.itcast.form.Form1DemoAction"></action>
</package>

使用ServletActionContext类获取

  • static HttpServletRequest getRequest():获取Web应用的HttpServletRequest对象。
  • static HttpServletResponse getResponse():获取Web应用的HttpServletResponse对象。
  • static ServletContext getServletContext():获取web应用的ServletContext对象。
  • static PageContext getPageContext():获取web应用的PageContext对象。
    调用类里面的静态方法得到Request对象。
//...
public String execute(){
	//第二种方式 使用ServletActionContext类获取表单数据
	//1.使用ServletActionContext获取request对象
	HttpServletRequest request=ServletActionContext.getRequest();
	//2.调用request里面的方法获得表单数据
	String username = request.getParameter("username");
	String password = request.getParameter("password");
	String address = request.getParameter("address");
	System.out.println(username+" "+password+" "+address);
	//注意,因为已经在struts.xml中声明了常量,所以Struts2已经帮我们处理了post提交方式的中文乱码问题。
	return NONE;
}

使用接口注入方式

让action实现接口,来获得request对象

/** 
 * 通过实现ServletRequestAware接口获取request对象
 */
public class Form3DemoAction extends ActionSupport implements ServletRequestAware{
	private HttpServletRequest request;
	@Override
	public void setServletRequest(HttpServletRequest request){
		this.request = request;
	}
	@Override
	public String execute() throws Exception{
		String username = request.getParameter("username");
		System.out.println("username = "+username);
	}
}

在action中操作域对象:

1、在servlet中可用的域对象:request、session、servletContext
2、使用ServletActionContext类操作域对象

public String execute() throws Exception{
	HttpServletRequest request = ServletActionContext.getRequest();
	request.setAttribute("req","reqValue");
	
	HttpSession session = request.getSession();
	session.setAttribute("sess","sessValue);
	
	ServletContext context = ServletActionContext.getServletContext();
	context.setAttribute("contextname","contextValue");
}

Struts2提供的获取表单数据方式

Struts2提供的获取表单数据方式:属性驱动、模型驱动

使用最原始的方式获取表单数据并封装到实体类对象

//User.java
/**
 * 实体类对象
 */
public class User{
	private String username;
	private String password;
	private String address;
	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 getAddress() {return address;}
	public void setAddress(String address) {this.address = address;}
	@Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
// Form4DemoAction.java
/**
 * 创建一个action
 */
public class Form4DemoAction extends ActionSupport{
	public String execute(){
		//1.获取表单数据
		HttpServletRequest request = ServletActionContext.getRequest();
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		String address = request.getParameter("address");
		
		//2.封装到实体类对象里面
		User user = new User();
		user.setUsername(username);
		user.setPassword(password);
		user.setAddress(address);
		
		System.out.println(user);
		return NONE;
	}
}

评价:最为灵活,但如果属性值比较多,用这种原始的方法来做就显得比较麻烦,而使用框架可以帮助我们省去这些复杂的步骤。

属性驱动

实现步骤:在action中定义几个成员变量,变量的名字要和表单中的输入项的name属性值一致;生成这些字段的set方法(建议将get/set方法都写出来,以免记混)

/**
 * 使用属性封装获取表单数据
 */
public class DataDemo1Action extends ActionSupport{
	//1.定义变量
	//变量的名称和表单输入项name属性值一样
	private String uusername;
	private String password;
	private String address;
	
	//2.生成变量的get/set方法
	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 getAddress() {return address;}
	public void setAddress(String address) {this.address = address;}
	
	@Override
	public String execute() throws Exception{
		System.out.println("username = "+username);
		System.out.println("password = "+password);
		System.out.println("address = "+address);
		return NONE;
	}
}
```xml
<!-- 在Struts.xml中添加action标签: -->
<action name="data1" class="cn.itcast.data.DataDemo1Action"></action>
<!-- 创建一个表单页面: -->
<form action="${pageContext.request.contextPath}/data1.action" method="post">
	username:<input type="text" name="username" /><br/>
	password:<input type="text" name="password" /><br/>
	address:<input type="text" name="address" /><br/>
	<input type="submit" value="提交" />
</form>

属性封装的缺陷:不能把数据直接封装到实体类对象里面去。

模型驱动

最大的好处:使用模型驱动可以直接把表单数据封装到实体类对象中,需要重点掌握。
实现的步骤:1、让Action实现接口ModelDriven;2、实现接口里面的方法getModel;3、在action中创建实体类对象,实体类属性名称要和表单输入项name属性值一样,切记。

//User.java
public class User{
	private String username;
	private String password;
	private String address;
	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 getAddress() {return address;}
	public void setAddress(String address) {this.address = address;}
	@Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
<!-- data2.jsp -->
<form action="${pageContext.request.contextPath}/data2.action" method="post">
	username:<input type="text" name="username" /><br/>
	password:<input type="text" name="password" /><br/>
	address:<input type="text" name="address" /><br/>
	<input type="submit" value="提交" />
</form> 
//DataDemo2Action.java
/**
 * 使用模型驱动获取表单数据
 */
public class DataDemo2Action extends ActionSupport implements ModelDriven<User>{
	//创建对象
	//前提条件:表单输入项name属性值和实体类属性名称要一样
	private User user = new User();//手动地创建对象
	@Override
	public User getModel(){
		return user;//返回创建的对象
	}
	@Override
	public String execute() throws Exception{
		return NONE;
	}
}
<!-- struts.xml -->
<action name="data2" class="cn.itcast.data.DataDemo2Action"></action>

【注意】对于同一个表单不能同时使用属性驱动和模型驱动,否则只执行模型驱动。

表达式封装

使用表达式封装可以把表单数据封装到实体类对象中,具体实现步骤如下:
第一步,在action里面创建实体类的引用。
第二步,在action中生成实体类的get/set方法。
第三步,在表单输入项的name属性值里面写表达式形式。

//import ...
/**
 * 使用表达式封装将表单数据封装到实体类对象中
 */
public class DataDemo3Action extends ActionSupport{
	//1.声明实体类
	private User user;
	//2.生成实体类变量的get/set方法
	public User getUser(){
		return user;
	}
	public void setUser(User user){
		this.user = user;
	}
}
<!-- 表单输入项的name属性的写法为对象名.属性名 -->
<form action="data3.action" method="post">
	username:<input type="text" name="user.username"/><br/>
	password:<input type="text" name="user.password"/><br/>
	address:<input type="text" name="user.address"/><br/>
	<input type="submit" value="submit"/>
</form>

【扩展】表达式封装和模型驱动比较
1、使用表达式封装和模型封装都能把表单数据封装到实体类对象中去。
2、不同点:
(1)使用模型驱动只能把数据封装到一个实体类对象里面。
(2)使用表达式封装可以把数据封装到不同的实体类对象中去。

struts2获取数据封装到集合中:

封装到list集合

第一步,在action中声明List及其get/set方法;
第二步,在表单输入项中使用表达式。

public class DataDemo5Action extends ActionSupport{
	//1.声明List变量
	private List<User> users;
	//2.生成get/set方法
	public List<User> getUsers(){return users;}
	public void setUsers(List<User> users){this.users=users;}
	public String execute() throws Exception{
		System.out.println("users[0] = "+"users[0]);
		System.out.println("users[1] = "+"users[1]);
		return NONE;
	}
}
<form action="${pageContext.request.contextPath}/data5.action" method="post">
	username0: <input type="text" name="users[0].username/><br/>
	password0:<input type="text" name="users[0].password/><br/>
	address0:<input type="text" name="users[0].adddress/><br/>
	username1:<input type="text" name="users[1].username/><br/>
	password1:<input type="text" name="users[1].password/><br/>
	address1:<input type="text" name="users[1].address/><br/>
	<input type="submit" value="submit"/>
</form>

封装到map集合

第一步,在action中声明Map及其get/set方法;
第二步,在表单输入项中使用表达式。

public class DataDemo6Action extends ActionSupport{
	//1.声明List变量
	private Map<String,User> users;
	//2.生成get/set方法
	public Map<String,User> getUsers(){return users;}
	public void setUsers(Map<String,User> users){this.users=users;}
	public String execute() throws Exception{
		System.out.println("users['one'] = "+"users['one']);
		System.out.println("users['two'] = "+"users['two']);
		return NONE;
	}
}
<form action="${pageContext.request.contextPath}/data5.action" method="post">
	username0:<input type="text" name="users['one'].username/><br/>
	password0:<input type="text" name="users['one'].password/><br/>
	address0:<input type="text" name="users['one'].adddress/><br/>
	username1:<input type="text" name="users['two'].username/><br/>
	password1:<input type="text" name="users['two'].password/><br/>
	address1:<input type="text" name="users['two'].address/><br/>
	<input type="submit" value="submit"/>
</form>

ognl概述

1、之前在WEB阶段,学习过EL表达式,EL表达式在jsp中获取域对象里面的值
2、OGNL是一种表达式,这个表达式功能更加强大。

  • 支持对象方法调用
  • 支持类静态方法调用和值访问,表达式的格式为@[类全名]@[方法名|值名]
  • 支持赋值操作和表达式串联
  • 访问OGNL上下文
  • 操作集合对象
    (1)在Struts2里面操作值栈数据
    (2)一般把ognl在struts2操作,和struts2标签一起使用操作值栈

3、OGNL不是struts2的一部分,单独的项目,经常和Struts2一起使用
(1)使用OGNL时候首先导入jar包,struts2提供jar包

ognl入门案例

1、对象方法的调用
使用ognl+struts2标签实现计算字符串长度:
${str.length()}
2、使用struts2标签
(1)使用jstl时候,除了导入jar包,在jsp页面中要引入标签库
使用struts标签时,也要用同样的方法引入标签库。

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

(2)使用struts2标签实现操作

<%@ taglib uri="/struts-tags" prefix="s" %><!-- 引入标签库 -->
<!-- 使用ognl+struts2标签库实现计算字符串长度,value属性值为ognl表达式 -->
<s:property value="'haha'.Length()" />

什么是值栈

1、之前在web阶段,在servlet里面进行操作,把数据放到域对象中,在页面中使用EL表达式获取,域对象在一定范围内存值取值。
2、struts2中提供了一种存储的机制,类似于之前学的域对象,是值栈也可以存值取值。
(1)在action里面我可以把数据放到值栈中,在页面中可以获取值栈的值。在实际编程中可以灵活选择域对象和值栈。
3、Servlet和Action的区别
(1)Servlet:Servlet默认在第一次访问的时候创建,只创建一次,单实例对象。
(2)Action:访问时候创建,每次访问Action时都会创建Action对象,创建很多次,多实例对象。
4、值栈的存储位置
(1)每次访问action时都会创建Action对象
(2)在每个action中都会有且只有一个值栈对象。

如何获取值栈对象

1、获取值栈对象有多种方式
(1)常用方式:使用ActionContext类里面的方法得到值栈对象

public String execute() throws Exception{
	//1.获取ActionContext类对象
	ActionContext context = ActionContext.getContext();
	//2.使用方法得到值栈对象
	ValueStack stack1 = context.getValueStack();
}

2、每个action对象中只有一个值栈对象

public String execute() throws Exception{
	//1.获取ActionContext类对象
	ActionContext context = ActionContext.getContext();
	//2.使用方法得到值栈对象
	ValueStack stack1 = context.getValueStack();
	ValueStack stack2 = context.getValueStack();
	System.out.println(stack1==stack2);//结果为true,说明一个action对象只有一个值栈对象
}

值栈内部结构

栈的特点:后进先出 栈的操作:压栈、出栈、取栈顶元素
1、值栈分成两部分
(1)第一部分root,类型为CompoundRoot,继承了ArrayList,结构是List。一般操作都是root里面的数据
(2)第二部分context,类型为OgnlContext,实现了Map接口,结构是Map。context存储的是对象引用,其中键值key是固定的,对应的值为:

  • request——request对象的引用
  • session——session对象引用
  • application——ServletContext对象引用
  • parameters——传递的一些相关参数
  • attr——使用attr操作,获取域对象里面的值,如果多个域对象定义了相同的属性,那么获取的就是域范围最小的域对象里面的值

2、struts2里面有标签s:debug,使用这个标签就能够查看值栈结构和存储值
(1)访问action,执行action的方法有返回值,配置返回值到JSP页面中,在JSP页面中使用这个标签。
①首先导入标签库
②激活调试模式

<constant name="struts.devMode" value="true"></constant>

③使用s:debug标签查看值栈结构和值

<s:debug></s:debug>

④运行页面,并点击debug可以查看值栈结构。
在action中没有做任何操作,值栈的栈顶元素是action,这是因为值栈对象中有action值栈对象的引用。

向值栈放数据

1、向值栈中放数据有多种方式:
(1)获取值栈对象,调用值栈对象里面的set方法
(2)获取值栈对象,调用值栈对象里面的push方法
(3)常用方式:在action中定义一个变量,生成变量的get方法

//使用值栈里面的set方法
public String execute() throws Exception{
	//1、获取值栈对象
	ActionContext context = ActionContext.getContext();
	ValueStack stack = context.getValueStack();
	//2、调用set方法
	stack.set("username","itcastitheima");//执行结果,栈顶出现了一个HashMap
	//3、调用push方法
	stack.push("abcd");//执行结果,栈顶出现了一个String对象
}
public class ValueStackDemoAction throws Exception{
	//1.定义变量
	private String name;
	//2.生成变量的get方法
	public String getName(){
		return name;
	}
	@Override
	public String execute() throws Exception{
		//3.在执行的方法里面向变量设置值
		name = "abcdefg";
	}
}
//执行的结果name值出现在值栈中的Action中

向值栈中放对象

1、步骤:
第一步:定义对象
第二步:生成对象的get方法
第三步:在执行的方法中向对象中设置值

//User实体
public class User{
	private String username;
	private String password;
	private String address;
	public String getUsername(){return this.username;}
	public String getPassword(){return this.password;}
	public String getAddress(){return this.address;}
	public void setUsername(String username){this.username=username;}
	public void setPassword(String username){this.password=password;}
	public void setAddress(String address){this.address=address;}
}
//Action类
public class ObjectDemoAction{
	//1.定义对象的变量
	private User user = new User();
	//2.生成变量的get方法
	public User getUser(){
		return user;
	}
	@Override
	public String execute() throws Exception{
		//3.向值栈的User中放值
		user.setUsername("lucy");
		user.setPassword("123");
		user.setAddress("美国");
		return "success"
	}
}

向值栈放List集合

1、步骤
第一步 定义List集合变量
第二步 生成变量的get方法
第三步 在执行的方法里向List中放值

public class HelloAction extends ActionSupport {
	//1.定义list集合
    private List<User> list = new ArrayList<>();
	//2.get方法
    public List<User> getList() {
        return list;
    }

    public void setList(List<User> list) {
        this.list = list;
    }

    @Override
    public String execute() throws Exception {
		//3.向list中设置值
        User user1 = new User();
        user1.setUsername("zhangsan");
        user1.setPassword("123");;
        user1.setAddress("america");
        User user2 = new User();
        user2.setUsername("lisi");
        user2.setPassword("456");;
        user2.setAddress("france");
        list.add(user1);
        list.add(user2);
        return SUCCESS;
    }
}

从值栈中获取数据

1、步骤:
(1)向值栈中放数据
(2)使用struts标签+OGNL表达式来获取值栈数据
注意struts标签只能用于JSP页面,不能用于HTML
2、从值栈获取字符串

<!-- 先获取字符串的值 -->
<s:property value="username"/>

3、获取对象

<!-- 获取对象的值 -->
<s:property value="user.username"/>
<s:property value="user.password"/>
<s:property value="user.address"/>

4、从值栈获取list集合
(1)方法1:

<!-- 获取对象的值 -->
<s:property value="list[0].username"/><br/>
<s:property value="list[0].password"/><br/>
<s:property value="list[0].address"/><br/>
<s:property value="list[1].username"/><br/>
<s:property value="list[1].password"/><br/>
<s:property value="list[1].address"/><br/>

(2)方法2:
s:iterator标签,遍历值栈中的集合元素

<!-- 获取对象的值 -->
<s:iterator value="list"><!-- value为集合名称 -->
	<s:property value="username"/><br/>
	<s:property value="password"/><br/>
	<s:property value="address"/><br/>
</s:iterator>

注意:html注释不能注释注释中的标签,但JSP标签可以
(3)方法3:

<s:iterator value="list" var="user"><!-- value为集合名称, user为迭代器变量 -->
	<!--
		遍历值栈list集合,得到每个user对象
		优化机制:每次遍历出来的user对象放到context里面,在context中分配一个临时空间,
		“键”为var的属性值,“值”为每次遍历list元素的引用
		获取context里面数据特点:写ognl表达式,使用特殊符号#
	-->
	<s:property value="#user.username"/>
	<s:property value="#user.password"/>
	<s:property value="#user.address"/>
</s:iterator>

5、其它操作
1、使用set方法向值栈中放数据,如何获取

<s:property value="username"/>

2、使用push方法向值栈放数据,如何获取
(1)使用push方法放数据,没有名称,只有设置的值。
(2)向值栈中放数据,值栈中的数据存到数组中,数组的名称为top

<s:property value="[0].top"/>

EL表达式获取值栈数据

<c:forEach items="${list}" var="item">
	<c:out value="${item.username}"/>&nbsp;
	<c:out value="${item.password}"/>&nbsp;
	<c:out value="${item.address}"/><br/>
</c:forEach>

【重点】为什么使用EL表达式能够去到值栈中的数据
1、EL表达式获取域对象值
2、向域对象里面放值使用setAttribute方法,获取值使用getAttribute方法
3、底层增强request对象里面的方法getAttribute方法
(1)首先,从request域变量中获取值,如果获取到直接返回
(2)如果从request域获取不到值,到值栈中获取值,并放到域对象中
4、查看源代码

public class StrutsRequestWrapper extends HttpServletRequestWrapper 
	//增强了getAttribute方法
	public Object getAttribute(String key) {}
}

ognl表达式#、%使用

#的使用:
1、使用#获得context里面的数据
2、演示#操作
(1)向request域放值
(2)在页面中使用ognl获取

<s:property value="#request.username"/>
%的使用
1、在struts2中有一类表单的标签
(1)在struts2标签里面使用ognl表达式,必须加%,否则不识别
<%-- } --%>
<%-- <s:textfield name=“username” value="#request.username"></s:textfield> --%>
<s:textfield name=“username” value="%{#request.username}"></s:textfield>

struts2拦截器概述

1、struts2是一个框架,封装了很多的功能,struts2里面封装的功能都是在拦截器里面
2、struts2里面封装了很多的功能,有很多拦截器,不是每次这些拦截器都执行,每次执行默认的拦截器
3、struts2默认拦截器的位置
struts2-core核心包,struts-default.xml,<interceptor-stack>标签
4、拦截器在什么时候执行
在action对象创建之后,action方法执行之前执行

拦截器底层原理

1、拦截器底层使用的两个原理
第一个 aop思想
(1)描述:
AOP=面向切面(方面)编程,有基本功能,扩展功能,不通过修改源代码方式扩展功能
(2)图示
在这里插入图片描述
第二个 责任链模式
(1)在java中有很多设计模式,责任链模式是其中一种
(2)责任链模式与过滤链很相似
过滤链:一个请求可以有多个过滤器进行过滤,每个过滤器只有放行才能到下个过滤器
(3)责任链模式:
-要一次性执行多个操作,有添加、修改、删除,首先执行添加操作,执行完之后类似于放行操作。接着执行修改操作,执行完之后类似放行操作。最后执行删除操作。
在这里插入图片描述
2、aop思想和责任链模式如何用到拦截器中
(1)描述:
拦截器在action对象创建之后和action中的方法执行之前执行拦截器操作
在action方法执行之前执行默认拦截器,执行的过程使用aop思想
在action里面并没有直接调用拦截器的方法,而是使用配置文件的方法来进行操作
在执行拦截器的时候,执行很多的拦截器,这个过程使用责任链模式
假如有三个拦截器,执行拦截器1,执行拦截器1之后做放行操作,执行拦截器2,执行拦截器2之后做放行操作,执行拦截器3,执行拦截器3之后放行,执行action的方法
(2)画图分析
在这里插入图片描述
3、查看源代码
(1)执行action

execute.executeAction(request,response,mapping);

(2)创建action对象,使用动态代理方式

ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace,name,method,extraContext,true,false);

代理对象:不是真正的对象,实现对象的功能。

(3)执行action的方法

proxy.execute();

(4)执行很多的拦截器

if(interceptors.hasNext()){}

类似于放行的操作

return invocation.invoke();

重要的概念:

1、过滤器与拦截器的区别:
(1)过滤器:过滤器可以过滤任意的内容,比如html、jsp、servlet、资源路径
(2)拦截器:拦截器只可以拦截action
2、Servlet与action的区别:
(1)Servlet默认第一次访问时候创建,创建一次,单实例对象
(2)action每次访问时候创建,创建多次,多实例对象

自定义的拦截器

1、在Struts2中有很多拦截器,这些拦截器是Struts2封装的功能,但是在实际开发中,struts2中的拦截器可能没有要使用的功能,这时候就要自定义拦截器。
2、拦截器的基本结构:
(1)查看源代码查看拦截器的基本结构:
查看modelDriven源代码
拦截器类:继承了AbstractInterceptor,这个类又实现了Interceptor接口,在接口中有三个方法,init方法,destroy方法,intercept方法。

class ModelDrivenInterceptor extends AbstractInterceptor{}
class AbstractInterceptor implements Interceptor{}
public abstract class AbstractInterceptor implements Interceptor {
    public AbstractInterceptor() {}
    public void init() {}//初始化操作
    public void destroy() {}//销毁
    public abstract String intercept(ActionInvocation var1) throws Exception;//拦截逻辑的操作
}

(2)开发中,建议使用另外一种方式
写类,继承MethodFilterInterceptor类实现
让action里面某个方法不进行拦截
(3)让拦截器和action有关系
不是在action调用拦截器的方法,而是通过配置文件方式建立关系

自定义登录拦截器

1、需求:
在项目中,有很多action的超链接,实现只有是登录的状态,才可以点击action的超链接实现功能,如果不是登录状态,点击action超链接返回到登录界面。
2、登录的状态:使用session域对象实现
(1)登录成功之后,把数据放到session里面
(2)判断session是否有值,可以知道是否是登录状态
3、实现登录的基本功能

<body>
	<h1>xxx的主页</h1>
	<form action="${pageContext.request.contextPath}/helloAction.action" method="post">
	  username:<input type="text" name="username"/><br/>
	  password:<input type="text" name="password"/><br/>
	  <input type="submit" value="submit"/>
	</form>
	<p>current user:${sessionScope.username }</p>
</body>
public class HelloAction extends ActionSupport {
    @Override
    public String execute() throws Exception {
        //1、得到request对象
        HttpServletRequest request = ServletActionContext.getRequest();
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //2、查询数据库判断用户名和密码是否正确
        if("admin".equals(username) && "250".equals(password)){//成功
            request.getSession().setAttribute("username",username);
            return SUCCESS;
        }else{
            return ERROR;
        }
    }
}

4、添加登录拦截器功能
(1)判断是否登录:判断session中是否有名称为username的值
(2)拦截器具体实现过程
第一步:创建一个类继承MethodFilterInterceptor类
第二步:重写MethodFilterInterceptor类里面的方法写拦截器逻辑

public class LoginInterceptor extends MethodFilterInterceptor {
    //这个方法里面写拦截器逻辑
    @Override
    protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
        //判断session中是否含有名称是username的值
        //得到session
        HttpServletRequest request = ServletActionContext.getRequest();
        HttpSession session  = request.getSession();
        Object obj = request.getSession().getAttribute("username");
        //判断
        if(obj != null){
            //登录状态
            //做一个类似于放行的操作
            return actionInvocation.invoke();
        }else{
            //不是登录状态
            //不到登录,不执行action里面的方法,直接返回到登录页面
            //到result标签里找到名称是login的值,到配置路径里面
            return "success";
        }
    }
}

第三步:配置action和拦截器关系(注册拦截器)
(1)在要拦截的action标签所在的package标签中声明要注册的拦截器

<!-- 声明拦截器 -->
<interceptors>
	<interceptor name="loginIntercept" class="cn.ljh.interceptor.LoginInterceptor"
</interceptors>

(2)在具体的action标签中使用声明的拦截器

<!-- 使用自定义拦截器 -->
<interceptor-ref name="loginIntercept"></interceptor-ref>

(3)struts2里面会执行很多默认拦截器,但是如果在action里面配置了自定义的拦截器,默认的拦截器就会失效。
解决:手动使用默认拦截器。复制默认拦截器的配置标签,或者使用interceptor-stack引入所有的拦截器

<!-- 把默认拦截器手动使用一次 -->
<interceptor-ref name="defaultStack"></interceptor-ref>

5、配置的拦截器,对action里面所有的方法都进行拦截
(1)在action里面有一个login方法,这个方法不需要拦截,问题是,永远都登录不进去了
(2)解决:让login方法不进行拦截
直接通过配置的方式让action里面某些方法不进行拦截。

<interceptor-ref name="loginIntercept">
	<!-- 配置action里面某些方法不进行拦截
		name属性值,excludeMethods
		值,action不拦截方法名称
	-->
	<param name="excludeMethods">login</param>
</interceptor-ref>

6、如果登录状态,直接到功能页面,如果不是登录状态,直接到登录页面
在表单中使用target属性。

Struts2标签库

0、struts2标签只能使用在JSP页面中
1、s:property 和ognl表达式在jsp页面获取值栈数据
2、s:iterator 获取值栈中list集合数据,表示list集合
3、s:debug 查看值栈内容

struts2表单标签

1、html表单标签
(1)form: action、method、enctype
(2)普通输入项: type=""
text、password、checkbox、radio、file、hidden、button、submit、image、reset
其他:
select、textarea
2、在struts2里面对应html表单标签大部分都有

<%@ taglib uri="/struts-tags" prefix="s" %>
<!-- ... -->
<s:form>
	<!-- 1、普通输入项 -->
	<s:textfield name="username" label="username"></s:textfield>
	
	<!-- 2、密码输入项 -->
	<s:password name="password" label="password"></s:password>
	
	<!-- 3、单选输入项 -->
	<!-- value属性值和显示值一样的 -->
	<s:radio list="{'male','female'}" name="sex" label="sex"></s:radio>
	<!-- value属性值和显示值不一样 -->
	<s:radio list="#{'male':'','female':''}" name="sex1" label="sex"></s:radio>
	
	<!-- 4、复选框 -->
	<s:checkboxlist list="{'eat','sleep','code'}" name="hobby" label="兴趣">
	
	<!-- 5、下拉输入框 -->
	<s:select list="{'初中','高中','大学'}" name="college" label="学历">
	
	<!-- 6、文件上传项 -->
	<s:file name="file" label="上传文件"></s:file>
	
	<!-- 7、隐藏项 -->
	<s:hidden name="hid" value="abcd"></s:hidden>

	<!-- 8、文本域 -->
	<s:textarea rows="10" cols="3" name="resume" label="简历"></s:textarea>
	
	<!-- 9、提交按钮 -->
	<s:submit value="提交"></s:submit>
	
	<!-- 10、重置 -->
	<s:reset value="重置"></s:reset>
</s:form>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值