回顾
1、Struts2框架的概述,前端控制器的模式,核心的过滤器
2、入门 编写 struts.xml配置文件
3、配置文件
配置文件的加载
4、Action类的编写和访问
在Struts2框架中使用Servlet的API
1、在Action类中也可以获取到Servlet一些常用的API
提供JSP的表单页面的数据,在Action中使用Servlet的API接收到,然后保存到三个域对象中,最后显示到JSP的页面上
提供JSP注册的页面
<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>
2、完全解耦合的方式
Struts2框架提供了一个类,ActionContext类,提供了一些方法,通过方法获取Servlet的API
常用的方法
static.ActionContext.getContext() 获取ActionContext对象实例
Map<String,Object>getParameters() 获取请求参数,相当于request.getParameterMap()
Map<String,Object>getSession() 获取的代表session域的Map集合,相当于操作session域
Map<String,Object>getApplication() 获取的代表application域的Map集合
void put(String key,Object value) 向request域存入值
3、使用原生Servlet的API的方式
Struts2框架提供了一个类,ServletActionContext 提供了一些静态的方法
getPageContext()
getRequest()
getResponse()
getServletContext()
页面的跳转
1、结果页面存在两种方式
全局结果页面
条件:如果<package>包中deec一些action都返回success,并且返回的页面都是同一个JSP页面,这样就可以配置全局的结果页面
全局结果页面针对的当前的包中的所有的Action,但是如果局部还有结果页面,会优先局部的,使用的标签
<global-results>
<result name="success">/demo1/suc.jsp</result>
</global-results>
局部结果页面
<result name="success">/demo1/suc.jsp</result>
2、结果页面的类型
结果页面使用<result>标签进行配置,包含两个属性
name 逻辑视图的名称
type 跳转的类型,一些常用的类型,常见的结果类型在struts-default.xml查找[重定向请求的参数没了 2个请求了]
dispatcher 转发,type的默认值.Action-->JSP
redirect 重定向Action-->JSP
chain 多个action之间跳转,从一个action转发到另一个Action。Action-->Action
redirectAction 多个action之间跳转,从一个action重定向到另一个Action。Action-->Action
stream 文件下载时使用
框架的数据封装
1、使用的原因
作为MVC框架,必须要负责解析HTTP请求参数,并将其封装到Model对象中
封装数据为开发提供了很多方便
Struts2框架提供了很强大的数据封装的功能,不再需要使用Servlet的API完成手动封装了
2、Struts2提供了两类数据封装的方式
第一种:属性驱动
提供对应属性的set方法进行数据封装
表单的哪些属性需要封装数据,那么在对应的Action类中提供该属性的set方法即可
表单中的数据提交,最终找到Action类的setXxx的方法,最后赋值给全局变量
Struts2框架采用的拦截器完成数据的封装
方式不是很好,因为属性很多,提供特别多的set方法,而且还需要手动将数据存入到对象中
这种情况下,Action类就相当于一个JavaBean,没有体现MVC的思想,Action类又封装数据,又接受请求处理,耦合性较高
在页面上使用OGNL表达式进行数据封装
页面中使用OGNL表达式进行数据的封装,就可以直接把属性封装到某一个JavaBean的对象中
在页面中定义一个JavaBean,并且提供set方法:例如 private User user;
页面中的编写发生了变化,需要使用OGNL的方式,表单中的写法<input type="text" name="user.username">
只提供一个set方法还不够,必须还需要提供user属性的get和set方法
先调用set方法,判断一下是否有user对象的实例对象,如果没有,调用set方法把拦截器创建的对象注入进来
第二种方式:模型驱动
使用模型驱动的方式,也可以把表单中的数据直接封装到一个JavaBean的对象中,并且表单的写法和之前的写法没有区别
编写的页面不需要任何变化,正常编写name属性的值
模型驱动的编写步骤
手动实例化JavaBean:private User user = new User();
实现ModelDriver<T>接口,实现getModel()方法,在getModel()方法中返回user
数据封装到集合中
1、封装复杂类型的参数(集合类型Collection、Map接口等)
2、页面中可能想批量添加一些数据,那么就可以使用集合。把数据封装到集合中
3、把数据封装到Collection中
因为Collection接口都会有下标值,所以页面的下标会有一些区别
<input type="text" name="products[0].name"/>
在Action中的写法 需要提供products的集合,并且提供get和set方法
4、把数据封装到Map中
Map集合是键值对的形式,页面的写法
<input type="text" name="map['1'].username"/>
Action中提供map集合,并且提供get和set方法
Struts2的拦截器
1、概述
拦截器就是AOP(Aspect-Oriented Programming)的一种实现,(AOP是指用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作)
过滤器:过滤从客户端发送到服务器请求的
拦截器:拦截对目标Action中的某些方法进行拦截
拦截器不能拦截JSP
拦截到Action中某些方法
2、拦截器和过滤器的区别
拦截器是基于JAVA反射机制的,过滤器是基于函数回调的
过滤器依赖于Servlet容器,而拦截器不依赖于Servlet容器
拦截器只能对Action请求起作用(Action中的方法)而过滤器可以对几乎所有的请求起作用(CSS JSP JS)
拦截器采用责任链模式
在责任链模式里,很多镀锡由每一个对象对其下家的引用而连接起来形成一条链
责任链每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行
在Struts2中可以定义多个拦截器,将多个拦截器按照特点顺序 组成拦截器栈(顺序调用 栈中的每一个拦截器)
3、在Struts2的核心是拦截器,运行流程
web.xml->org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter->#doFilter->execute.executeAction->dispatcher.serviceAction->创建代理对象->proxy.execute()->invocation.invoke() 迭代调用拦截器
自定义拦截器和配置
1、编写拦截器,需要实现Interceptor接口 实现接口中的三个方法
interceptor接口有很多的实现类,编写最简单的方式就是继承AbstractInterceptor实现类
public String intercept(ActionInvocation invocation){
User user = ServletActionContext.getRequest().getSession.getAttribute("existUser");
if(user == null){
} else{
//放行
return invocation.invoke();
}
}
2、需要在struts.xml中进行拦截器的配置,一共有两种方式
第一种
在<package>包中定义拦截器,出现在<package>包的上方
<interceptors>
//调用了拦截器
<interceptor name="DemoInterceptor" class="my.interceptor.DemoInterceptor"></interceptor>
</interceptors>
在某个action中加入拦截器
<interceptor-ref name="DemoInterceptor"/>
如果引入了自己定义的拦截器,那么Struts2框架默认的拦截器就不会再执行了,所以需要引入Struts2默认的拦截器
<interceptor-ref name="defaultStack"/>
第二种
在<package>包中定义拦截器的时候,自己直接定义一个拦截器栈
<interceptors>
<interceptor name="DemoInterceptor" class="my.interceptor.DemoInterceptor"></interceptor>
<!-- 定义拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="DemoInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
在Action包中引入自己定义的拦截器栈
<interceptor-ref name="myStack"></interceptor-ref>
使用拦截器判断用户是否已经登录
struts.xml
<struts>
<package name="demo1" namespace="/" extends="struts-default">
<!-- 配置全局的结果页面 -->
<!-- <global-results>
<result name="success" type="redirect">/demo1/suc.jsp</result>
</global-results> -->
<!-- 完全解耦合的方式 -->
<action name="demo1Action" class="my.demo1.Demo1Action">
<result name="success" >/demo1/suc.jsp</result>
</action>
<!-- 原生的方式 -->
<action name="demo2Action" class="my.demo1.Demo2Action">
<!-- <result name="success">/demo1/suc.jsp</result> -->
</action>
<!-- 重定向到Action -->
<action name="demo3Action_*" class="my.demo1.Demo3Action" method="{1}">
<result name="success" type="redirectAction">demo3Action_update</result>
</action>
</package>
<package name="demo2" namespace="/" extends="struts-default">
<!-- 属性驱动的方式 -->
<action name="regist1" class="my.demo2.Regist1Action"/>
<!-- 属性驱动方式,把数据封装到JavaBean对象中 -->
<action name="regist2" class="my.demo2.Regist2Action"/>
<!-- 模型驱动的方式 -->
<action name="regist3" class="my.demo2.Regist3Action"/>
<!-- 把数据封装到List集合 -->
<action name="regist4" class="my.demo2.Regist4Action"/>
<!-- 把数据封装到Map集合 -->
<action name="regist5" class="my.demo2.Regist5Action"/>
</package>
<package name="demo3" namespace="/" extends="struts-default">
<!-- 定义了拦截器的第一种方式
<interceptors>
调用了拦截器
<interceptor name="DemoInterceptor" class="my.interceptor.DemoInterceptor"></interceptor>
</interceptors> -->
<!--
第二种方式,定义拦截器栈
-->
<interceptors>
<interceptor name="DemoInterceptor" class="my.interceptor.DemoInterceptor"></interceptor>
<!-- 定义拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="DemoInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<action name="userAction" class="my.demo3.UserAction">
<!-- 只要是引用了自己的拦截器,默认栈的拦截器就不执行了。必须要手动引入默认栈 否则封装数据等功能都不能用了 -->
<!-- <interceptor-ref name="DemoInterceptor"/>
<interceptor-ref name="defaultStack"/> -->
<!-- 引入拦截器栈 -->
<interceptor-ref name="myStack"></interceptor-ref>
</action>
</package>
</struts>
/**
* 完全解耦合的方式 使用Servlet的API
* @author Administrator
*
*/
public class Demo1Action extends ActionSupport {
private static final long serialVersionUID = -894949390421537212L;
public String execute() {
//完全解耦合的方式
ActionContext context =ActionContext.getContext();
//获取到请求的参数,封装所有请求的参数
Map<String,Object> map = context.getParameters();
//遍历获取数据
Set<String> keys = map.keySet();
for (String string : keys) {
//通过key来获取到值
String[] vals = (String[]) map.get(string);
System.out.println(string+":"+Arrays.toString(vals));
}
//如果向request对象中存入值
context.put("msg", "东东");
//获取其他map集合
context.getSession().put("msg", "大哥");
context.getApplication().put("msg", "小弟");
return SUCCESS;
}
}
/**
* 原生的API
* @author Administrator
*
*/
public class Demo2Action extends ActionSupport{
private static final long serialVersionUID = -8870547619062626755L;
public String execute() {
//获取到request对象
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("msg", "小弟");
request.getSession().setAttribute("msg","小弟2");
ServletActionContext.getServletContext().setAttribute("msg", "大哥");
HttpServletResponse response = ServletActionContext.getResponse();
//使用输出流,输出内容
return SUCCESS;
}
}
public class Demo3Action extends ActionSupport{
private static final long serialVersionUID = 8482710457471965754L;
public String save() {
System.out.println("save。。");
return SUCCESS;
}
//访问demo3Action_update
public String update() {
System.out.println("update。。。");
return NONE;
}
}
/**
* 属性驱动
* @author Administrator
*
*/
public class Regist1Action extends ActionSupport {
private static final long serialVersionUID = -2605442413925139136L;
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;
}
//获取值set方法 往外传值get
public String execute() throws Exception {
System.out.println(username+":"+password+":"+age);
return NONE;
}
}
/**
* 属性驱动方式,把数据封装到JavaBean的对象中
* @author Administrator
*
*/
public class Regist2Action extends ActionSupport {
private static final long serialVersionUID = 1524512405660914227L;
//注意二:属性驱动的方式
private User user;
public User getUser() {
System.out.println("getUser");
return user;
}
//set 帮你new个 直接get/set都出来算了
public void setUser(User user) {
System.out.println("setUser");
this.user = user;
}
public String execute() throws Exception {
System.out.println(user.toString());
return NONE;
}
}
/**
* 模型驱动的方式
* 实现ModelDrivern接口
* 必须手动实例化对象(需要自己new)
* @author Administrator
*
*/
public class Regist3Action extends ActionSupport implements ModelDriven<User> {
private static final long serialVersionUID = 1524512405663214227L;
//必须要手动实例化
private User user = new User();
//获取模型对象
public User getModel() {
return user;
}
public String execute() throws Exception {
System.out.println(user);
return NONE;
}
}
/**
* 属性驱动的方式,把数据封装到List集合中
* @author Administrator
*
*/
public class Regist4Action extends ActionSupport {
private static final long serialVersionUID = 1524512405663214227L;
private List<User> list;
public List<User> getList() {
return list;
}
public void setList(List<User> list) {
this.list = list;
}
public String execute() throws Exception {
for (User user : list) {
System.out.println(user);
}
return NONE;
}
}
/**
* 属性驱动的方式,把数据封装到Map集合中
* @author Administrator
*
*/
public class Regist5Action extends ActionSupport {
private static final long serialVersionUID = 1524512405663214227L;
private Map<String,User> map;
public Map<String, User> getMap() {
return map;
}
public void setMap(Map<String, User> map) {
this.map = map;
}
public String execute() throws Exception {
System.out.println(map);
return NONE;
}
}
Demo1.jsp
<h1>Servlet API的第一种方式[完全解耦合的方式]</h1>
<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>
suc.jsp
<h1>使用EL表达式获取值</h1>
${msg }
${requestScope.msg }
${sessionScope.msg }
${applicationScope.msg }
Demo2.jsp
<h1>属性驱动的方式</h1>
<form action="${pageContext.request.contextPath }/regist1.action" method="post">
姓名:<input type="text" name="username"/><br/>
<input type="submit" value="注册"/>
</form>
<!-- 注意一:页面的编写规则,发生了变化,使用OGNL表达式的写法一 -->
<h1>属性驱动的方式(把数据封装到JavaBean的对象中)</h1>
<form action="${pageContext.request.contextPath }/regist2.action" method="post">
姓名:<input type="text" name="user.username"/><br/>
<input type="submit" value="注册"/>
</form>
<h1>模型驱动方式</h1>
<form action="${pageContext.request.contextPath }/regist3.action" method="post">
姓名:<input type="text" name="username"/><br/>
<input type="submit" value="注册"/>
</form>
<h1>向List集合封装数据(默认情况下,采用的是属性驱动的方式)</h1>
<!-- 后台 List<User> list -->
<form action="${pageContext.request.contextPath }/regist4.action" method="post">
姓名:<input type="text" name="list[0].username"/><br/>
姓名:<input type="text" name="list[1].username"/><br/>
<input type="submit" value="注册"/>
</form>
<h1>向Map集合封装数据(默认情况下,采用的是属性驱动的方式)</h1>
<form action="${pageContext.request.contextPath }/regist5.action" method="post">
姓名:<input type="text" name="map['1'].username"/><br/>
姓名:<input type="text" name="map['two'].username"/><br/>
<input type="submit" value="注册"/>
</form>
/**
* intercept用来进行拦截
*/
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("Action方法执行之前");
//执行下一个拦截器 需要返回值
String result = invocation.invoke();
System.out.println("Action方法执行之后");
return result;
}
}
<struts>
<package name="crm" namespace="/" extends="struts-default">
<!-- 配置拦截器 -->
<interceptors>
<interceptor name="UserInterceptor" class="my.interceptor.UserInterceptor"></interceptor>
</interceptors>
<global-results>
<result name="login">/login.htm</result>
</global-results>
<!-- 配置用户的模块 -->
<action name="user_*" class="my.action.UserAction" method="{1}">
<!-- <result name="login">/login.htm</result> -->
<result name="success">/index.htm</result>
<interceptor-ref name="UserInterceptor">
<!-- login方法不拦截 -->
<param name="excludeMethods">login</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
<!-- 客户的模块 -->
<action name="customer_*" class="my.action.CustomerAction" method="{1}">
<interceptor-ref name="UserInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
</package>
</struts>
/**
* 自定义拦截器,判断当前是否已经登录,如果登录,继续执行。如果没有登录,跳转到登录页面
* @author Administrator
*
*/
public class UserInterceptor extends MethodFilterInterceptor{
private static final long serialVersionUID = -5152960937504606199L;
/**
* 进行拦截的方法
*/
protected String doIntercept(ActionInvocation invocation) throws Exception {
//获取session对象
User user = (User) ServletActionContext.getRequest().getSession().getAttribute("existUser");
if(user == null) {
//没登录
return "login";
}
return invocation.invoke();
}
}