Struts2 快速笔记1
路线:
- Struts2的概述、Struts2的入门、Struts2常见的配置、Struts2的Action的编写
- Struts2的数据的封装、结果页面配置
- Struts2的值栈和OGNL表达式
- Struts2的标签库
基础
定义:基于MVC的模式的WEB层框架,Struts2和Struts1的架构完全不同。
常见的Web层框架:Struts2、WebWork、SpringMVC等。
从Servlet到框架的变化:传统的请求处理是对每一个请求进行相应的Servlet的处理,会产生许多的Servlet;WEB层的框架是基于前端控制器模型对请求进行处理的分发(过滤器)来设计的。
环境
下载:https://struts.apache.org/ 练习版本:2.3.34
Struts2解压目录
- apps:Struts提供的应用案例,几个WAR包
- docs:开发文档,API
- lib:开发框架的JAR包
- src:源码
使用:创建WEB项目,引入JAR包,配置
执行过程的顺序:当用户访问某一个Action的时候,先经过核心过滤器,在核心过滤器中执行一组拦截器(这组拦截器实现部分功能),执行目标Action,根据Action的返回值,进行页面跳转。
Struts配置文件的加载顺序:
-
init_DefaultProperties() ----加载default.properties
-
init_TraditionalXmlConfigurations(); ----加载struts-default.xml、struts-plugin.xml、struts.xml
-
init_LegacyStrutsProperties(); ----加载struts.properties
-
init_CustomConfigurationProviders(); ----加载配置提供类
-
init_FilterInitParameters() ; // [6] ----加载web.xml中过滤器初始化参数
-
init_AliasStandardObjects() ; // [7] ----加载Bean对象
-
加载顺序
-
default.properties
-
struts-default.xml
-
struts-plugin.xml
-
struts.xml
-
struts.properties
-
web.xml
注意:后配置的常量的值会覆盖先配置的常量的值。
-
配置
-
Package包,Struts中的包,为了更好地管理
- name
- extends:一般继承自struts-default,实现更多功能
- namespace:命名空间,与action标签中的name共同标记为访问路径,先查找有名称的如“aaa”,再找“/”,最后查找默认的“ ”;
- abstract:为true即可被继承
-
action
- name:访问的名,相当于Servlet的名字,和namespace决定请求访问路径
- class:对应执行的类
- method:class类中执行的方法,默认为execute
- converter:用于设置某些类型转换器
-
常量:在Struts2的框架中,提供了非常多的常量:default.properties中。
-
struts.i18n.encoding=UTF-8 ----Struts2中所有的post请求的中文乱码不用处理。
-
struts.action.extension=action, ----Struts2请求的默认的扩展名。默认扩展名是.action或者什么都不写。
-
在Struts中可以在Struts.xml,Struts.properties,web.xml三个地方修改常量配置。
-
-
action的三种编写方式
- POJO类:普通的一个Java类,写一个public的execute方法,返回一个string的值
- 实现Action接口,Action接口提供execute方法和5个返回常量,如SUCCESS/ERROR/LOGIN等
- 继承ActionSupport类:推荐,同样实现了Action的接口,还实现了一类验证等其他功能的接口
-
action的三种访问方式
-
通过method访问
-
使用通配符访问
-
动态访问
-
总结:Struts的执行过程:请求API----核心过滤器(StrutsPrepareAndExecuteFilter)-----执行一组拦截器(完成部分的功能)-----Action-------Result.
Struts2 常见API
Struts2的Servlet的API访问
struts中的action中,没有Servlet的request和response对象,可视作是解耦的。实际开发中,经常使用到Servlet的API,如session,response等对象。
Servlet是单例的,多个程序访问同一个Servlet只会创建一个Servlet的实例。Action是多例的,一次请求,创建一个Action的实例(不会出现线程安全的问题)
三种方式:
-
完全解耦方式:这种方式只能获得代表request、session、application的数据的Map集合,不能操作这些对象的本身的方法。
// 获取Servlet的API ActionContext actionContext = ActionContext.getContext(); Map<String, Object> map = actionContext.getParameters(); // 类似于request.getParametersMap(),返回一个map for (String key : map.keySet()) { String[] values = (String[]) map.get(key); System.out.println(key + " ... " + Arrays.toString(values)); } // 二、向域对象中存入数据 actionContext.put("reqName", "reqValue22");// 相当于request.setAttribute(); actionContext.getSession().put("sessName", "sessValue22"); // 相当于session.setAttribute(); actionContext.getApplication().put("appName", "appValue22"); // 相当于application.setAttribute(); return "demo1Success";
-
原生API:注意:这种方式可以操作域对象的数据,同时也可以获得对象的方法。
// 获取Servlet的API HttpServletRequest request = ServletActionContext.getRequest(); Map<String, String[]> map = request.getParameterMap(); for (String key : map.keySet()) { String[] values = (String[]) map.get(key); System.out.println(key + " ... " + Arrays.toString(values)); } // 二、向域对象中存入数据 request.setAttribute("reqName", "reqValue22"); request.setAttribute("sessName", "sessValue22"); ServletActionContext.getServletContext().setAttribute("appName", "appValue22"); return "demo1Success";
-
接口注入方式:
public class RequestDemo2 extends ActionSupport implements ServletRequestAware, ServletContextAware{ private ServletContext context; private HttpServletRequest request; //action是多例的,所以成员变量不会存在线程安全问题 @Override public String execute() { Map<String, String[]> map = request.getParameterMap(); for (String key : map.keySet()) { String[] values = (String[]) map.get(key); System.out.println(key + " ... " + Arrays.toString(values)); } // 二、向域对象中存入数据 request.setAttribute("reqName", "reqValue33"); request.setAttribute("sessName", "sessValue33"); context.setAttribute("appName", "appValue332"); return "demo1Success"; } @Override public void setServletContext(ServletContext context) { this.context = context; } @Override public void setServletRequest(HttpServletRequest request) { this.request = request; } }
Struts的数据——结果页面的转发
-
全局结果页面:对当前包(xml下的package)中的所有action均有效,只要返回相应的result,则可以跳转。
<!-- 配置Struts2的常量 --> <constant name="struts.action.extension" value="action"/> <package name="demo1" extends="struts-default" namespace="/"> <global-results> <result name="demo1Success">/demo1/demo2.jsp</result> </global-results> <action name="requestDemo1" class="com.leehao.struts_servlet.demo1.RequestDemo1"> <result name="demo1Success">/demo1/demo2.jsp</result> </action> <!-- 原生Servlet API --> <action name="requestDemo2" class="com.leehao.struts_servlet.demo1.RequestDemo2"> <result name="demo1Success">/demo1/demo2.jsp</result> </action> <!--接口注入 --> <action name="requestDemo3" class="com.leehao.struts_servlet.demo1.RequestDemo3"> <result name="demo1Success">/demo1/demo2.jsp</result> </action> </package>
-
局部结果页面:只针对当前action生效,相同配置下,局部的先生效
-
result的属性配置
- name属性 :逻辑视图的名称。默认值:success
- type属性 :页面跳转的类型。
- dispatcher :默认值,请求转发。(Action转发到JSP)
- redirect :重定向。(Action定向到JSP)
- chain :转发。(Action转发到Action)
- redirectAction :重定向。(Action重定向到Action)
- stream :Struts2中提供文件下载的功能。
Struts2的数据封装
Struts2框架是一个web层框架,web层框架(框架:软件的半成品,完成一部分功能)。Struts2提供了数据封装的功能。
在struts中,请求经过核心过滤器时,需要在初始化时,执行许多默认的初始拦截器来处理数据。其中,就有params/modelDriven/conversionError等,来实现参数的校验,类型转化和封装等功能。
-
属性驱动:提供属性set方法的方式(不常用)
-
属性驱动:页面中提供表达式方式。
public class UserAction2 extends ActionSupport { //一个私有的对象,名字和页面相同,表示直接将数据注入到该对象中 //struts生成一个User的对象,封装数据,将值赋给当前action的对象属性中,必须有getUser方法,否则只能生成一个属性的对象。 private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String execute() throws Exception { System.out.println(user); return NONE; } } --------------------------------jsp----------------------------------------- <h3>方式二:属性驱动,通过页面表达式</h3> <form action="${pageContext.request.contextPath }/userAction2.action" method="post"> 姓名:<input name="user.username" type="text"><br/> 密码:<input name="user.password" type="password"><br/> 生日:<input name="user.birthday" type="text"><br/> 年龄:<input name="user.age" type="text"><br/> 工资:<input name="user.salary" type="text"><br/> <input type="submit" value="提交"><br/> </form>
-
模型驱动:
模型驱动方式最常用的方式:
-
缺点:只能同时向一个对象中封装数据。
-
使用第二种可以向多个对象中同时封装数据
public class UserAction3 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; } } ----------------------------jsp------------------------------ <h3>方式三:模型驱动方法</h3> <form action="${pageContext.request.contextPath }/userAction3.action" method="post"> 姓名:<input name="username" type="text"><br/> 密码:<input name="password" type="password"><br/> 生日:<input name="birthday" type="text"><br/> 年龄:<input name="age" type="text"><br/> 工资:<input name="salary" type="text"><br/> <input type="submit" value="提交"><br/> </form>
-
-
Input逻辑视图错误
INPUT的逻辑视图的配置,Action接口中提供了五个逻辑视图的名称:
- SUCCESS
- ERROR
- LOGIN
- INPUT :input****在某些拦截器中会使用。
- NONE
示例图:
Struts复杂数据模型的封装
在实际开发中,有可能遇到批量向数据库中插入记录,需要在页面中将数据封装到集合中。
-
向集合List中封装
public class ProductAction1 extends ActionSupport { private List<Product> products; //必须设置set,get方法 public List<Product> getProducts() { return products; } public void setProducts(List<Product> products) { this.products = products; } @Override public String execute() throws Exception { for (Product product : products) { System.out.println(product); } return NONE; } } ---------------------jsp------------------------------------------ <h3>一:List类型数据集合</h3> <form action="${pageContext.request.contextPath }/productAction1.action" method="post"> 名称:<input name="products[0].pname" type="text"> 价格:<input name="products[0].price" type="text"><br/> 名称:<input name="products[1].pname" type="text"> 价格:<input name="products[1].price" type="text"><br/> 名称:<input name="products[2].pname" type="text"> 价格:<input name="products[2].price" type="text"><br/> <input type="submit" value="提交"><br/> </form>
-
向Map中封装
public class ProductAction2 extends ActionSupport { private Map<String, Product> map; //必须设置set,get方法 public Map<String, Product> getMap() { return map; } public void setMap(Map<String, Product> map) { this.map = map; } @Override public String execute() throws Exception { for (Entry<String, Product> entry : map.entrySet()) { System.out.println(entry.getKey() + " " + entry.getValue()); } return NONE; } } -----------------------------JSP------------------------------------------ <h3>二:MAP类型数据集合</h3> <form action="${pageContext.request.contextPath }/productAction2.action" method="post"> 名称:<input name="map['one'].pname" type="text"> 价格:<input name="map['one'].price" type="text"><br/> 名称:<input name="map['two'].pname" type="text"> 价格:<input name="map['two'].price" type="text"><br/> 名称:<input name="map['three'].pname" type="text"> 价格:<input name="map['three'].price" type="text"><br/> <input type="submit" value="提交"><br/> </form>