Servlet API、封装数据、拦截器。
Struts2 框架中 Servlet API 的使用
Servlet 的 API
在 Action
类中也可以获取到 Servlet
一些常用的 API
- 案例需求:提供
JSP
的表单页面的数据,在 Action
中使用 Servlet
的 API
接收到,然后保存到三个域对象中,最后再显示到 JSP
的页面上。
提供 JSP 注册的页面:
1 2 3 4 5 6
| <h3>注册页面</h3> <form action="${ pageContext.request.contextPath }/xxx.action" method="post"> 姓名:<input type="text" name="username" /><br/> 密码:<input type="password" name="password" /><br/> <input type="submit" value="注册" /> </form>
|
完全解耦合的方式
为了避免与 Servlet API
耦合在一起,方便 Action 类做单元测试,Struts2 对 HttpServletRequest
、HttpSession
和 ServletContext
进行了封装,构造了三个 Map 对象来替代这三种对象,在 Action 中,直接使用 HttpServletRequest
、HttpSession
、ServletContext
对应的 Map 对象来保存和读取数据。
要获得这三个Map对象,可以使用 com.opensymphony.xwork2.ActionContext类
ActionContext
: 请求上下文
常用的方法如下:
1 2 3 4 5
| static ActionContext getContext() -- 获取 ActionContext 对象实例 java.util.Map<java.lang.String,java.lang.Object> getParameters() -- 获取请求参数,相当于 request.getParameterMap(); java.util.Map<java.lang.String,java.lang.Object> getSession() -- 获取的代表 session 域的 Map 集合,就相当于操作 session 域。 java.util.Map<java.lang.String,java.lang.Object> getApplication() -- 获取代表 application 域的 Map 集合 void put(java.lang.String key, java.lang.Object value) -- 注意:向 request 域中存入值。
|
案例演示
Demo1Action.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
public class Demo1Action extends ActionSupport { @Override public String execute() throws Exception { ActionContext context = ActionContext.getContext(); Map<String, Object> map = context.getParameters(); Set<String> keys = map.keySet(); for (String key : keys) { String[] vals = (String[]) map.get(key); System.out.println(key + ":" + Arrays.toString(vals)); } context.put("msg","小天"); context.getSession().put("msg","小明"); context.getApplication().put("msg","小红");
return SUCCESS; } }
|
JSP 输入页面:
1 2 3 4 5 6
| <h3>完全解耦合方式</h3> <form action="${ pageContext.request.contextPath }/demo1Action.action" method="post"> 姓名:<input type="text" name="username" /><br/> 密码:<input type="password" name="password" /><br/> <input type="submit" value="注册" /> </form>
|
JSP 跳转页面:
1 2 3 4
| <h3>使用 EL 表达式获取值</h3> ${requestScope.msg} ${sessionScope.msg} ${applicationScope.msg}
|
struts.xml 配置文件:
1 2 3 4 5 6
| <package name="demo1" namespace="/" extends="struts-default"> <action name="demo1Action" class="com.renkaigis.demo1.Demo1Action"> <result name="success">/demo1/success.jsp</result> </action> </package>
|
案例结果:
输入信息:
跳转并取值:
控制台输出结果:
1 2
| password:[12345] username:[renkai]
|
使用原生 Servlet 的 API 的方式
直接访问 Servlet API
将使 Action
类与 Servlet API
耦合在一起,Servlet API
对象均由 Servlet
容器来构造,与这些对象绑定在一起,测试过程中就必须有 Servlet
容器,这样不便于 Action
类的测试,但有时候,确实需要访问这些对象,Struts2
同样提供了直接访问 ServletAPI
对象的方式。
要直接获取 Servlet API
对象可以使用 org.apache.struts2.ServletActionContext
类,该类是 ActionContext
类的子类。
1 2 3 4 5
| HttpServletRequest request=ServletActionContext.getRequest(); HttpSession session=request.getSession(); session.setAttribute("xxx",xxx); HttpServletResponse response=ServletActionContext.getResponse(); HttpServletContext application=ServletActionContext.getApplication();
|
案例演示
Demo2Action.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public class Demo2Action extends ActionSupport { @Override public String execute() throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("msg", "天天"); request.getSession().setAttribute("msg", "美美"); ServletActionContext.getServletContext().setAttribute("msg", "园园");
HttpServletResponse response = ServletActionContext.getResponse();
return SUCCESS; } }
|
JSP 输入页面:
1 2 3 4 5 6
| <h3>ServletCActionContext 类</h3> <form action="${ pageContext.request.contextPath }/demo2Action.action" method="post"> 姓名:<input type="text" name="username" /><br/> 密码:<input type="password" name="password" /><br/> <input type="submit" value="注册" /> </form>
|
JSP 跳转页面:
1 2 3 4
| <h3>使用 EL 表达式获取值</h3> ${requestScope.msg} ${sessionScope.msg} ${applicationScope.msg}
|
struts.xml 配置文件:
1 2 3 4
| <action name="demo2Action" class="com.renkaigis.demo1.Demo2Action"> <result name="success">/demo1/success.jsp</result> </action>
|
案例结果:
输入信息:
跳转并取值:
结果类型的跳转
结果页面存在两种方式
全局结果页面
条件:如果 <package>
包中的一些 action
都返回 success
,并且返回的页面都是同一个 JSP
页面,这样就可以配置全局的结果页面。
全局结果页面针对的 当前包
中的所有的 Action
,但是如果局部还有结果页面,会 优先局部
的。使用的标签是:
1 2 3 4 5 6
| <package name="demo1" namespace="/" extends="struts-default"> <global-results> <result name="success">/demo1/success.jsp</result> </global-results> </package>
|
局部结果页面
也就是之前一直所使用的。
1
| <result>/demo1/success.jsp</result>
|
结果页面的类型
- 结果页面使用
<result>
标签进行配置,包含两个属性
name
– 逻辑视图的名称
type
– 跳转的类型,需要掌握一些常用的类型。常见的结果类型去 struts-default.xml
中查找。
dispatcher
– 转发,type的默认值
。Action--->JSP
redirect
– 重定向。 Action--->JSP
chain
– 多个action之间跳转,从一个Action转发到另一个Action。 Action---Action
redirectAction
– 多个action之间跳转,从一个Action重定向到另一个Action。 Action---Action
stream
– 文件下载时候使用的
Struts2 框架的数据封装
数据的封装
- 作为
MVC
框架,必须要负责解析 HTTP
请求参数,并将其封装到 Model
对象中
- 封装数据为开发提供了很多方便
- Struts2 框架提供了很强大的数据封装的功能,
不再需要使用 Servlet 的 API 完成手动封装了
!
Struts2 中提供了两类数据封装的方式:
第一种方式:属性驱动
提供对应属性的 set
方法进行数据的封装。
表单的哪些属性需要封装数据,那么在对应的 Action
类中提供该属性的 set
方法即可。
表单中的数据提交,最终找到 Action
类中的 setXxx
的方法,最后赋值给全局变量。
注意0:Struts2
的框架采用拦截器完成数据的封装。
注意1:这种方式不是特别好:因为属性特别多,提供特别多的set方法,而且还需要手动将数据存入到对象中。
注意2:这种情况下,Action
类就相当于一个 JavaBean
,就没有体现出 MVC
的思想,Action
类又封装数据,又接收请求处理,耦合性较高。
Action:
1 2 3 4 5 6 7 8 9 10 11 12 13
| private String username; private String password; private Integer age;
public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setAge(Integer age) { this.age = age; }
|
struts.xml
1 2 3 4
| <package name="demo2" extends="struts-default" namespace="/"> <action name="regist1" class="com.renkaigis.demo2.Regist1Action"/> </package>
|
jsp页面
1 2 3 4 5 6 7
| <h3>属性驱动的方式</h3> <form action="${ pageContext.request.contextPath }/regist1.action" method="post"> 姓名:<input type="text" name="username" /><br/> 密码:<input type="password" name="password" /><br/> 年龄:<input type="text" name="age" /><br/> <input type="submit" value="注册" /> </form>
|
输出结果
属性驱动:把数据封装到 JavaBean 对象中
在页面上,使用 OGNL
表达式进行数据封装。
在页面中使用 OGNL
表达式进行数据的封装,就可以直接把属性封装到某一个 JavaBean
的对象中。
在页面中定义一个 JavaBean
,并且提供 set
方法:例如:private User user;
页面中的编写发生了变化,需要使用 OGNL
的方式,表单中的写法:<input type="text" name="user.username">
注意:只提供一个 set
方法还不够,必须还需要提供 user
属性的 get
和 set
方法!!!
原理: 先调用 get
方法,判断一下是否有 user
对象的实例对象,如果没有,调用 set
方法把拦截器创建的对象注入进来。
Action:
1 2 3 4 5 6 7 8
| private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; }
|
struts.xml
1 2 3 4
| <package name="demo2" extends="struts-default" namespace="/"> <action name="regist2" class="com.renkaigis.demo2.Regist2Action"/> </package>
|
jsp页面
1 2 3 4 5 6 7 8
| <h3>属性驱动的方式(把数据封装到 JavaBean 的对象中)</h3> <%--页面的编写发生了变化,使用的是 OGNL 表达式的写法--%> <form action="${ pageContext.request.contextPath }/regist2.action" method="post"> 姓名:<input type="text" name="user.username" /><br/> 密码:<input type="password" name="user.password" /><br/> 年龄:<input type="text" name="user.age" /><br/> <input type="submit" value="注册" /> </form>
|
输出结果
1
| User{username='renkai', password='12', age=25}
|
第二种方式:模型驱动
使用模型驱动的方式,也可以把表单中的数据直接封装到一个 JavaBean
的对象中,并且表单的写法和之前的写法没有区别!
编写的页面不需要任何变化,正常编写 name
属性的值
模型驱动的编写步骤:
Action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class Regist3Action extends ActionSupport implements ModelDriven<User> { private User user = new User(); @Override public User getModel() { return user; }
@Override public String execute() throws Exception { System.out.println(user); return NONE; } }
|
struts.xml
1 2 3 4
| <package name="demo2" extends="struts-default" namespace="/"> <action name="regist3" class="com.renkaigis.demo2.Regist3Action"/> </package>
|
jsp页面
1 2 3 4 5 6 7
| <h3>模型驱动的方式</h3> <form action="${ pageContext.request.contextPath }/regist3.action" method="post"> 姓名:<input type="text" name="username" /><br/> 密码:<input type="password" name="password" /><br/> 年龄:<input type="text" name="age" /><br/> <input type="submit" value="注册" /> </form>
|
输出结果
1
| User{username='renkai', password='12', age=25}
|
Struts2 把数据封装到集合中
(默认采用的是属性驱动的方式)
把数据封装到 List 集合中
- 因为
Collection
接口都会有下标值,所有页面的写法会有一些区别,注意:
1
| <input type="text" name="products[0].name" />
|
- 在
Action
中的写法,需要提供 user
的集合,并且提供 get
和 set
方法。
Action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class Regist4Action extends ActionSupport { private List<User> list; public List<User> getList() { return list; } public void setList(List<User> list) { this.list = list; } @Override public String execute() throws Exception { for (User user : list) { System.out.println(user); } return NONE; } }
|
struts.xml
1 2 3 4
| <package name="demo2" extends="struts-default" namespace="/"> <action name="regist4" class="com.renkaigis.demo2.Regist4Action"/> </package>
|
jsp页面
1 2 3 4 5 6 7 8 9 10 11
| <h3>向List集合封装数据(默认情况下,采用的是属性驱动的方式)</h3> <form action="${ pageContext.request.contextPath }/regist4.action" method="post"> 姓名:<input type="text" name="list[0].username" /><br/> 密码:<input type="password" name="list[0].password" /><br/> 年龄:<input type="text" name="list[0].age" /><br/>
姓名:<input type="text" name="list[1].username" /><br/> 密码:<input type="password" name="list[1].password" /><br/> 年龄:<input type="text" name="list[1].age" /><br/> <input type="submit" value="注册" /> </form>
|
输出结果
1 2
| User{username='renkai', password='12', age=25} User{username='xiaomei', password='34', age=22}
|
把数据封装到 Ma 中
1
| <input type="text" name="map['one'].name" />
|
注意:里面的 key
值可以自定义。
- Action中提供
map
集合,并且提供 get
和 set
方法
Action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public class Regist5Action extends ActionSupport { private Map<String, User> map; public Map<String, User> getMap() { return map; } public void setMap(Map<String, User> map) { this.map = map; } @Override public String execute() throws Exception { System.out.println(map); return NONE; } }
|
struts.xml
1 2 3 4
| <package name="demo2" extends="struts-default" namespace="/"> <action name="regist5" class="com.renkaigis.demo2.Regist5Action"/> </package>
|
jsp页面:这里的 key
值自定义。
1 2 3 4 5 6 7 8 9 10 11
| <h3>向map集合封装数据(默认情况下,采用的是属性驱动的方式)</h3> <form action="${ pageContext.request.contextPath }/regist5.action" method="post"> 姓名:<input type="text" name="map['one'].username" /><br/> 密码:<input type="password" name="map['one'].password" /><br/> 年龄:<input type="text" name="map['one'].age" /><br/>
姓名:<input type="text" name="map['two'].username" /><br/> 密码:<input type="password" name="map['two'].password" /><br/> 年龄:<input type="text" name="map['two'].age" /><br/> <input type="submit" value="注册" /> </form>
|
输出结果
1
| {one=User{username='renkai', password='12', age=25}, two=User{username='xiaomei', password='34', age=22}}
|
Struts2 的拦截器技术
拦截器
拦截器概述
拦截器不能拦截JSP
拦截 Action
中某些方法
拦截器和过滤器的区别
1)拦截器是基于 JAVA反射机制
的,而过滤器是基于 函数回调
的
2)过滤器依赖于Servlet容器,而拦截器不依赖于Servlet容器
3)拦截器 只能对Action请求
起作用(Action中的方法),而过滤器可以对 几乎所有的请求
起作用(CSS JSP JS)。
在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链
责任链每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行
- 在 struts2 中可以定义很多个拦截器,将多个拦截器按照特定顺序组成拦截器栈(顺序调用栈中的每一个拦截器 )
Struts2 框架的核心是拦截器
自定义拦截器和配置
编写拦截器
需要实现 Interceptor
接口,实现接口中的三个方法
1 2 3 4 5 6 7 8 9
| protected String doIntercept(ActionInvocation invocation) throws Exception { User user = (User) ServletActionContext.getRequest().getSession().getAttribute("existUser"); if(user == null){ return "login"; } return invocation.invoke(); }
|
配置拦截器
注意: 只要引用了自己的拦截器,Struts2 框架默认栈的拦截器就不执行了,必须要手动引入默认栈。
需要在 struts.xml
中进行拦截器的配置,配置一共有两种方式
第一种方式:直接引入
- 在
<package>
包中定义拦截器,出现在 <package>
包的上方
1 2 3
| <interceptors> <interceptor name="DemoInterceptor" class="com.renkaigis.interceptor.DemoInterceptor"/> </interceptors>
|
1 2 3 4 5
| <action name="userAction" class="com.renkaigis.demo3.UserAction"> <interceptor-ref name="DemoInterceptor"/> <interceptor-ref name="defaultStack"/> </action>
|
第二种方式:定义拦截器栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <interceptors> <interceptor name="DemoInterceptor" class="com.renkaigis.interceptor.DemoInterceptor"/> <interceptor-stack name="myStack"> <interceptor-ref name="DemoInterceptor"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors>
<action name="userAction" class="com.renkaigis.demo3.UserAction"> <interceptor-ref name="DemoInterceptor"/> <interceptor-ref name="defaultStack"/> </action>
|