文章目录
用于学习与交流,视频课件原作者为黑马程序员,侵立删
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}"/>
<c:out value="${item.password}"/>
<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>