Struts2龙之总结

时间:2023-03-10 05:39:38
Struts2龙之总结

 一、Struts2执行流程: 

  1.客户端初始化一个指向servlet容器(tomcat)的请求;

  2.这个请求经过一系列过滤器(Filter);

  3.接着StrutsPrepareAndExecuteFilter被调用,StrutsPrepareAndExecuteFilter询问ActionMapper来决定这个请求是否需要调用某个Action;

  4.如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy;

  5.ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类;

  6.ActionProxy调用invoke方法创建一个ActionInvocation的实例。

  7.递归调用,判断是否还有下一个拦截器,当没有了下一个拦截器,就执行Action的业务方法;

  8.一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链;

  9.返回结果的时候依旧经过一系列拦截器。

  请求 ---- StrutsPrepareAndExecuteFilter 核心控制器 ----- Interceptors 拦截器(实现代码功能,只访问action时执行 ) ----- Action 的execuute --- 结果页面 Result

二、在web.xml 配置struts2 前端控制器 (Filter)

<filter>

<filter-name>struts2</filter-name>

<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>struts2</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

三、struts2 常量配置 (键值对 properties)

  struts2 默认常量 在 default.properties 中配置

  常用常量

  <constant name="struts.i18n.encoding" value="UTF-8"/>  ----- 相当于request.setCharacterEncoding("UTF-8"); 解决post请求乱码

  <constant name="struts.action.extension" value="action,,"/>  --- 访问struts2框架Action访问路径 扩展名 (要求)

  * struts.action.extension=action,, 默认以.action结尾扩展名 和 不写扩展名 都会分发给 Action

  <constant name="struts.serve.static.browserCache" value="false"/> false不缓存,true浏览器会缓存静态内容,产品环境设置true、开发环境设置false

  <constant name="struts.devMode" value="true" />  提供详细报错页面,修改struts.xml后不需要重启服务器 (要求)

   <action>的name 和 <package>的namespace属性 共同决定 Action的访问路径 !!!!!!!!

  例如 :

  <package name="usermanager" namespace="/user" extends="struts-default">

  <action name="hello" class="cn.itcast.struts2.demo1.HelloAction">

  访问路径 /user/hello.action

  3) <action> 元素配置默认值

<package> 的namespace 默认值 /

  <action> 的class 默认值 ActionSupport 类

  <result> 的 name 默认值 success

  <action> 的method默认值execute

4、 struts2 配置文件分离

通过 <include file="struts-part1.xml"/> 将struts2 配置文件 拆分

分离配置文件的原则:按模块进行分离,再统一组装到struts.xml  (<include>标签)

五、 Action的访问

HTTP请求 提交 Struts2 StrutsPrepareAndExecuteFilter 核心控制器 ------ 请求分发给不同Action

1、 让请求能够访问Action  ----- Action书写方式 三种

第一种 Action可以是 POJO  ((PlainOldJavaObjects)简单的Java对象) ---- 不需要继承任何父类,实现任何接口

* struts2框架 读取struts.xml 获得 完整Action类名

* obj = Class.forName("完整类名").newInstance();

* Method m = Class.forName("完整类名").getMethod("execute");  m.invoke(obj); 通过反射 执行 execute方法

第二种 编写Action 实现Action接口

Action接口中,定义默认五种 逻辑视图名称

public static final String SUCCESS = "success";  // 数据处理成功 (成功页面)

public static final String NONE = "none";  // 页面不跳转  return null; 效果一样

public static final String ERROR = "error";  // 数据处理发送错误 (错误页面)

public static final String INPUT = "input"; // 用户输入数据有误,通常用于表单数据校验 (输入页面)

public static final String LOGIN = "login"; // 主要权限认证 (登陆页面)

* 五种逻辑视图,解决Action处理数据后,跳转页面

第三种 编写Action  继承ActionSupport  (推荐)

在Action中使用 表单校验、错误信息设置、读取国际化信息 三个功能

2、 Action中方法调用

1) 在配置 <action> 元素时,没有指定method属性, 默认执行 Action类中 execute方法

<action name="request1" class="baoming.demo3.RequestAction1" />

2) 在<action> 元素内部 添加 method属性,指定执行Action中哪个方法

<action name="regist" class="baoming.demo4.RegistAction" method="regist"/> 执行 RegistAction 的regist方法

3) 使用通配符* ,简化struts.xml配置

<a href="${pageContext.request.contextPath }/user/customer_add.action">添加客户</a>

<a href="${pageContext.request.contextPath }/user/customer_del.action">删除客户</a>

struts.xml

<action name="customer_*" class="baoming.struts2.demo4.CustomerAction" method="{1}"></action>   ---  {1}就是第一个* 匹配内容

Action 构造方法调用几次?是否为单例

Action的生命周期?

因为每次请求都会调用构造方法,说明它是多实例的,不存在线程并发访问时的冲突问题

Struts1会不会存在线程安全问题?Struts1是单例的,会存在线程并发访问时的安全问题

3、 动态方法调用 (零配置路线)

访问Action中指定方法,不进行配置

1) 在工程中使用 动态方法调用 ,必须保证 struts.enable.DynamicMethodInvocation = true 常量值为true

2) 在action的访问路径 中 使用 "!方法名"

页面

<a href="${pageContext.request.contextPath }/user/product!add.action">添加商品</a>

配置

<action name="product" class="bao.struts2.demo4.ProductAction"></action>

执行 ProductAction 中的 add方法

六、在Action中使用Servlet API

1、 在Action 中解耦合方式 间接访问 Servlet API  --------- 使用 ActionContext 对象

在struts2 中 Action API 已经与 Servlet API 解耦合 (没有依赖关系 )

* Servlet API 常见操作 : 表单提交请求参数获取,向request、session、application三个范围存取数据

actionContext = ActionContext.getContext();

actionContext.getParameters(); 获得所有请求参数Map集合

actionContext.put("company", "数据"); / actionContext.get("company") 对request范围存取数据

3) actionContext.getSession(); 获得session数据Map,对Session范围存取数据

actionContext.getSession().put("key","value");

4) actionContext.getApplication(); 获得ServletContext数据Map,对应用访问存取数据

actionContext.getApplication().put("key","value");

*本质分析是否存在线程安全问题? 不会有 答案原码:ActionContext  (使用ThreadLocal存储相关的值)

3、 在Action中直接通过 ServletActionContext 获得Servlet API

ServletActionContext.getRequest() : 获得request对象 (session)

ServletActionContext.getResponse() : 获得response 对象

ServletActionContext.getServletContext() : 获得ServletContext对象

* 静态方法没有线程问题,ThreadLocal

七、 结果页面的配置

Action处理请求后, 返回字符串(逻辑视图名), 需要在struts.xml 提供 <result>元素定义结果页面

1、 局部结果页面 和 全局结果页面

<action name="result" class="bao.struts2.demo6.ResultAction">

<!-- 局部结果  当前Action使用 -->

<result name="success">/demo6/result.jsp</result>

</action>

<global-results>

<!-- 全局结果 当前包中 所有Action都可以用-->

<result name="success">/demo6/result.jsp</result>

</global-results>

2、 结果页面跳转类型

* 在struts-default.xml 定义了 一些结果页面类型

* 使用默认type 是 dispatcher 转发 (request.getRequestDispatcher.forward)

1)  dispatcher :Action 转发给 JSP

2)  chain :Action调用另一个Action (同一次请求)

<result name="success" type="chain">hello</result>  hello是一个Action的name

3) redirect : Action重定向到 JSP

<result name="success" type="redirect">hello.action</result>  hello是一个Action的name

4) redirectAction :Action重定向到另一个Action

<result name="success" type="redirectAction">hello</result>

Action : 可以作为模型,也可以是控制器 

属性驱动 和 模型驱动

第一种 :Action 本身作为model对象,通过成员setter封装 (属性驱动 )

页面:

用户名  <input type="text" name="username" /> <br/>

Action :

public class RegistAction1 extends ActionSupport {

private String username;

public void setUsername(String username) {

this.username = username;

}

}

  * struts2  Action 是多实例

 struts1 Action 是单例的

第二种 :创建独立model对象,页面通过ognl表达式封装 (属性驱动)

页面:

用户名  <input type="text" name="user.username" /> <br/>  ----- 基于OGNL表达式的写法

Action:

public class RegistAction2 extends ActionSupport {

private User user;

public void setUser(User user) {

this.user = user;

}

public User getUser() {

return user;

}

}

问题: 谁来完成的参数封装

<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>

第三种 :使用ModelDriven接口,对请求数据进行封装 (模型驱动 ) ----- 主流

页面:

用户名  <input type="text" name="username" /> <br/>

Action :

public class RegistAction3 extends ActionSupport implements ModelDriven<User> {

private User user = new User(); // 必须手动实例化

public User getModel() {

return user;

}

}

* struts2 有很多围绕模型驱动的特性

* <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> 为模型驱动提供了更多特性

对比第二种、第三种 : 第三种只能在Action中指定一个model对象,第二种可以在Action中定义多个model对象

<input type="text" name="user.username" />

<input type="text" name="product.info" />

属性驱动和模型驱动都是框架的拦截器来帮助实现的。

 数据类型转换问题

1、 struts2 内部提供大量类型转换器,用来完成数据类型转换问题

boolean 和 Boolean

char和 Character

int 和 Integer

long 和 Long

float 和 Float

double 和 Double

Date 可以接收 yyyy-MM-dd格式字符串  (yyyy/MM/dd不能转)

数组  可以将多个同名参数,转换到数组中

集合  支持将数据保存到 List 或者 Map 集合

案例: 输入合法年龄和生日可以自动转换

当输入abc 转换为 int类型 age时

Caused by: java.lang.NoSuchMethodException: bao.struts2.demo3.CustomerAction.setAge([Ljava.lang.String;

分析: 输入20 ,转换 int类型20  --- setAge(int)

输入abc,转换int 出错 ---- setAge(String) ----- 报错方法不存在异常

2、 自定义类型转换器 (了解

1) 自定义类型转换器

第一种 实现TypeConverter接口

convertValue(java.util.Map<java.lang.String,java.lang.Object> context, java.lang.Object target, java.lang.reflect.Member member, java.lang.String propertyName, java.lang.Object value, java.lang.Class toType)

第二种 继承 DefaultTypeConverter

convertValue(java.util.Map<java.lang.String,java.lang.Object> context, java.lang.Object value, java.lang.Class toType)

第三种 继承 StrutsTypeConverter

convertFromString(java.util.Map context, java.lang.String[] values, java.lang.Class toClass)  --- 请求封装

convertToString(java.util.Map context, java.lang.Object o)   --- 数据回显

类型转换器 一直都是双向转换

页面提交请求参数,封装到model --- 需要转换

model数据 需要在页面 回显  ---- 需要转换

2) 以 1990/10/10 为例,自定义日期转换器,完成转换  第二种方法

public Object convertValue(Map<String, Object> context, Object value,

Class toType) {

// 根据toType判断 是请求封装 还是 数据回显

DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");

if (toType == Date.class) {

// 请求参数封装 (value是字符串)

String[] params = (String[]) value;

String strVal = params[0]; // 转换为 日期类型

try {

return dateFormat.parse(strVal);

} catch (ParseException e) {

e.printStackTrace();

}

} else {

// 回显(value是 Date)

Date date = (Date) value;

return dateFormat.format(date);

}

return null;

}

3) 注册类型转换器

局部注册 : 只对当前Action有效 (针对属性)

全局注册 : 针对所有Action的日期类型有效 (针对类型 )

局部注册 : 在Action类所在包 创建 Action类名-conversion.properties , 格式 : 属性名称=类型转换器的全类名

全局注册 : 在src下创建 xwork-conversion.properties ,格式 : 待转换的类型=类型转换器的全类名

3、 类型转换中错误处理

通过分析拦截器作用,得知当类型转换出错时,自动跳转input视图 ,在input视图页面中 <s:fieldError/> 显示错误信息

* 在Action所在包中,创建 ActionName.properties,在局部资源文件中配置提示信息 : invalid.fieldvalue.属性名= 错误信息

校验器

1、 校验的分类 : 客户端数据校验 和 服务器端数据校验

客户端数据校验 ,通过JavaScript 完成校验 (改善用户体验,使用户减少出错 )

服务器数据校验 ,使用框架内置校验功能 (struts2 内置校验功能 ) ----- 必须的

2、 struts2 支持校验方式

代码校验 :在服务器端通过编写java代码,完成数据校验

配置校验 :XML配置校验(主流) 和 注解配置校验

4、 XML配置方式 数据校验 (企业主流校验)

代码校验 不适用于大型项目, 流程数据复杂时,开发量和维护量 都会很大

xml配置校验原理 : 将很多校验规则代码已经写好,只需要在xml中定义数据所使用校验规则就可以了

步骤一 :编写jsp

步骤二 :编写Action 继承ActionSupport 或者 实现 Validateable 接口

步骤三 :封装请求参数

* 使用xml校验 必须提供get方法

步骤四 :编写校验规则xml文件

在Action所在包 编写 Action类名-validation.xml 对Action所有业务方法进行校验

引入DTD

------ xwork-core-2.3.7.jar 中 xwork-validator-1.0.3.dtd

<!DOCTYPE validators PUBLIC

"-//Apache Struts//XWork Validator 1.0.3//EN"

"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">

内置校验器定义文件

xwork-core-2.3.7.jar 中 /com/opensymphony/xwork2/validator/validators/default.xml

内建校验器

* required (必填校验器,要求被校验的属性值不能为null)

* requiredstring (必填字符串校验器,要求被校验的属性值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)

* stringlength (字符串长度校验器,要求被校验的属性值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)

* regex (正则表达式校验器,检查被校验的属性值是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)

* int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)

* double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)

* fieldexpression (字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)

* email(邮件地址校验器,要求如果被校验的属性值非空,则必须是合法的邮件地址)

* url(网址校验器,要求如果被校验的属性值非空,则必须是合法的url地址)

* date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)

案例

required  必填校验器

<field-validator type="required">

<message>性别不能为空!</message>

</field-validator>

requiredstring  必填字符串校验器

<field-validator type="requiredstring">

<param name="trim">true</param>

<message>用户名不能为空!</message>

</field-validator>

stringlength:字符串长度校验器

<field-validator type="stringlength">

<param name="maxLength">10</param>

<param name="minLength">2</param>

<param name="trim">true</param>

<message><![CDATA[产品名称应在2-10个字符之间]]></message>

</field-validator>

int:整数校验器

<field-validator type="int">

<param name="min">1</param>

<param name="max">150</param>

<message>年龄必须在1-150之间</message>

</field-validator>

date: 日期校验器

<field-validator type="date">

<param name="min">1900-01-01</param>

<param name="max">2050-02-21</param>

<!-- OGNL表达式在配置文件中的写法-->

<message>生日必须在${min}到${max}之间</message>

</field-validator>

url:  网络路径校验器

<field-validator type="url">

<message>数据的主页地址必须是一个有效网址</message>

</field-validator>

email:邮件地址校验器

<field-validator type="email">

<message>电子邮件地址无效</message>

</field-validator>

regex:正则表达式校验器

<field-validator type="regex">

<param name="expression"><![CDATA[^13\d{9}$]]></param>

<message>手机号格式不正确!</message>

</field-validator>

fieldexpression : 字段表达式校验

<field-validator type="fieldexpression">

<param name="expression"><![CDATA[(password==repassword)]]></param>

<message>两次密码输入不一致</message>

</field-validator>

如何对指定的方法校验 ??? 格式  Action类名-ActionName(<action>元素name属性)-validation.xml

例如 : 校验AddCustomerAction中execute方法  配置 <action name="addcustomer" ...method="regist"/>

校验文件名字: AddCusotmerAction-addcustomer-validation.xml

 国际化信息显示

1、 国际化原理 ? 什么是国际化 ?

同一款软件 可以为不同用户,提供不同语言界面  ---- 国际化软件   internationalization  --i18n    本地化localization  l10n

需要一个语言资源包(很多properties文件,每个properties文件 针对一个国家或者语言 ,通过java程序根据来访者国家语言,自动读取不同properties文件 )

2、 资源包编写

properties文件命名 :  基本名称_语言(小写)_国家(大写).properties

例如 :

messages_zh_CN.properties 中文中国

messages_en_US.properties 英文美国

4、 struts2 框架国际化配置

第一种 全局国际化信息文件 (所有Action都可以使用 ) ------- 最常用

* properties文件可以在任何包中

* 需要在struts.xml 中配置全局信息文件位置

struts.xml

<constant name="struts.custom.i18n.resources" value="messages"></constant>   messages.properties 在src根目录

<constant name="struts.custom.i18n.resources" value="bao.resources.messages"></constant>   messages.properties 在 bao.resources 包

国际化信息

在Action中使用  : this.getText("msg");

在jsp中使用  :<s:text name="msg" />

在配置文件中(校验xml) : <message key="agemsg"></message>

 自定义拦截器

拦截器 的使用 ,源自Spring AOP(面向切面编程)思想

拦截器 采用 责任链 模式

*  在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。

*  责任链每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行

在struts2 中可以定义很多个拦截器,将多个拦截器按照特定顺序 组成拦截器栈 (顺序调用 栈中的每一个拦截器 )

拦截器与拦截器栈的区别?

拦截器:<interceptor>   拦截器栈:<interceptor-stack>

从功能上看,拦截器栈本身就是一个功能更加强大的拦截器

从结构上看,拦截器栈是由多个拦截器有序组织在一起

配置一个拦截器:<interceptor>

拦截器栈:<interceptor-stack>

在栈中引入一个定义好的拦截器<interceptor-ref name=""/>

定义默认的拦截器栈:<default-interceptor-ref name=""/>

类的写法

1、 struts2 所有拦截器 都必须实现 Interceptor 接口

2、 AbstractInterceptor 类实现了 Interceptor 接口. 并为 init, destroy 提供了一个空白的实现

所有实际开发中,自定义拦截器 只需要 继承 AbstractInterceptor类, 提供 intercept 方法实现

3、 常用struts2 拦截器

<interceptor-ref name="modelDriven"/> 模型驱动

<interceptor-ref name="fileUpload"/> 文件上传

<interceptor-ref name="params"> 参数解析封装

<interceptor-ref name="conversionError"/> 类型转换错误

<interceptor-ref name="validation"> 请求参数校验

<interceptor-ref name="workflow"> 拦截跳转 input 视图

Oracle和MySQL 作为应用数据库区别

mysql存在数据库概念,在企业开发中,针对一个项目创建一个单独数据库,创建单独用户, 为用户授予数据库权限 ,

oracle 一个数据库就是一个服务,在这个库中可以存在很多用户,每个用户有单独表空间 ,针对一个项目,只需要创建一个用户

Struts2 框架 上传与下载

文件上传

1) 企业常用文件上传技术 : jspSmartUpload(主要应用 JSP model1 时代) 、 fileupload (Apache commons项目中一个组件)、 Servlet3.0 集成文件上传 Part类

2) 文件上传  enctype="multipart/form-data"  是 MIME协议定义多部分请求体 (消息体)

3) 上传页面编写

存在 <input type="file" name="upload"/> 上传项,必须提供name属性

表单提交方式 必须 post 提交

表单编码类型 enctype="multipart/form-data"

4) Struts2 对文件上传的支持

提供 FileUpload 拦截器,用于解析 multipart/form-data 编码格式请求,解析上传文件的内容

fileUpload拦截器 默认在 defaultStack 栈中, 默认会执行的

在Action需要对上传文件内容进行接收

页面:

<input type="file" name="upload" />

Action :

public class UploadAction extends ActionSupport {

// 接收上传内容

// <input type="file" name="upload" />

private File upload; // 这里变量名 和 页面表单元素 name 属性一致

private String uploadContentType;//MIME类型   image/jpeg  text/html

private String uploadFileName;

}

* 格式 : 上传表单项name属性 + ContentType 、 上传表单项name属性 + FileName

* 为三个对象 提供 setter 方法

通过FileUtils 提供 copyFile 进行文件复制,将上传文件 保存到服务器端

5) Struts2 上传文件过程中错误处理

配置 input 视图 ,作为上传出错后 跳转页面

在文件上传时,如果发生错误 ,fileUpload拦截器 会设置错误信息,workflow拦截器 跳转到 input 视图

通过 struts.multipart.maxSize 常量设置文件上传总大小限制

* struts.multipart.maxSize=2097152 默认上传文件总大小 2MB

* 超过文件总大小,跳转input 视图, 通过 <s:actionError /> 回显错误信息

在struts.xml 设置上传总大小

<constant name="struts.multipart.maxSize" value="20000000"></constant>

设置上传文件总大小,对所有上传form有效,只想对当前form进行设置,可以设置fileUpload拦截器属性

FileUpload 拦截器有 3 个属性可以设置.

* maximumSize: 上传文件的最大长度(以字节为单位), 默认值为 2 MB

* allowedTypes: 允许上传文件的类型, 各类型之间以逗号分隔  image/jpeg,text/html

* allowedExtensions: 允许上传文件扩展名, 各扩展名之间以逗号分隔

如果针对fileUpload 进行参数设置,当出错时,在页面通过 <s:fieldError /> 回显错误信息

文件下载

文件下载原理:

1.一个流  输出流

2.两个头  response.setHeader("Content-Type","application/octet-stream");

response.setHeader("Content-Disposition","attachment;filename=1.jpg");

1) struts2 完成文件下载,通过 结果集类型 (Result Type) stream 来完成的

struts-default.xml 定义 <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>

2) 使用Stream结果集 完成文件下载

文件下载原理: 服务器读取下载文件内容,通过Response响应流写回, 设置 ContentType、 ContentDisposition 头信息

public class StreamResult extends StrutsResultSupport {

protected String contentType = "text/plain"; // contentType头信息  (下载文件对应 MIME协议规定类型 )

* html --- text/html . txt--- text/plain

protected String contentDisposition = "inline"; // ContentDisposition头信息 (下载文件打开方式 inline浏览器内部打开, attachment 以附件形式打开)

protected String inputName = "inputStream";  // 需要Action中 提供 getInputStream 方法 返回 InputStream 提供下载文件 内容

}

Action 提供 InputStream  返回值 getInputStream 方法 ------- 指定下载文件流

配置 stream 结果集 参数 <param name="contentType">${contentType}</param> ---- 在Action 中提供 getContentType

*  ServletActionContext.getServletContext().getMimeType(filename);

配置 stream 结果集 参数 <param name="contentDisposition">attachment;filename=${filename}</param> ---- 在Action 提供 getFilename

* 下载附件名乱码问题 , IE和火狐 解决不同

public String encodeDownloadFilename(String filename, String agent)

throws IOException {

if (agent.contains("Firefox")) { // 火狐浏览器

filename = "=?UTF-8?B?"

+ new BASE64Encoder().encode(filename.getBytes("utf-8"))

+ "?=";

} else { // IE及其他浏览器

filename = URLEncoder.encode(filename, "utf-8");

}

return filename;

}