Struts2
2020-05-29 14:51:54 0 举报
AI智能生成
学习资料Struts 2
作者其他创作
大纲/内容
Struts2入门
Struts2在三层架构中的位置
它只是表现层MVC的解决方案。与业务层和持久层没有关系。
Struts2简介
Struts1
也是apache开发的一套mvc的开源框架。在2005年之前非常流行。
弊端:Struts1的核心控制器就是一个Servlet。随着使用者的增多,弊端开始出现。
Struts2
里面包含了WebWork2的核心及Struts的一些特性和功能。除此之外,和Struts1没有任何关系了。
struts2怎么处理请求
通过底部配置的过滤器strutsPrepareAndExecuteFilter处理请求
struts2请求处理类
struts2 -- action
S2阶段 ---servlet
springMVC --- controller
环境的搭建
pom依赖
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.24</version>
</dependency>
<dependency>
<groupId>org.apache.struts.xwork</groupId>
<artifactId>xwork-core</artifactId>
<version>2.3.24</version>
</dependency>
web.xml过滤器的配置
<filter>
<filter-name>strutsPrepareAndExecuteFilter</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>strutsPrepareAndExecuteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
编写前端界面
编写action
创建Action的方法
a) 实现Action接口
b) 继承ActionSupport类
重写execute方法
编写struts.xml
配置package --- 类似springMVC中controller类
配置Action-- controller类中的执行方法
<package name="Hello" extends="struts-default">
<!-- name: 访问action的路径 calss:action的位置-->
<action name="helloAction" class="com.struts.demo1.action.HelloAction">
<result name="success">/WEB-INF/result.jsp</result>
</action>
<action name="login" class="com.struts.demo1.action.LoginAction">
<result name="success">info.jsp</result>
<result name="error">error.jsp</result>
</action>
部署,运行,测试
Struts2的入门案例
分支主题
执行过程
分支主题
解释框架图中的各个层次的意思。
1、我们一般会把我们做的项目部署到Tomcat服务器上,由服务器解析我们所做的Struts2项目。
2、项目部署完毕后通过jsp运行项目
分支主题
3、服务器开始启动,会加载先web.xml文件,web.xml其实是一个监管器
分支主题
StrutsPrepareAndExecuteFilter。表面意思就是struts准备并执行过滤器
作用:
一是为struts2执行做一些相关的准备。如加载相关的配置信息。
二是为struts2的request请求处理相关的信息。如设置编码格式和找到对应的action映射类
主要方法
void doFilter(javax.servlet.ServletRequest req, javax.servlet.ServletResponse res, javax.servlet.FilterChain chain)
doFilter()主要是通过ActionContex包装httpServletRequest,和HttpServletRespone用ActionContext包装请求和响应信息然后提交各Action类。至于怎么搞的自己看源码。
void init(javax.servlet.FilterConfig filterConfig)
Init()方法主要是读取响应的struts2的配置文件。顺序读取文件为;
1、default.properties(框架提供):配置了struts运行的参数
2、struts-default.xml(框架提供):struts框架加载创建、Result、拦截器等,框架运行要用
3、struts-plugin.xml(插件提供):插件文件,比如:第三方插件,转json插件、struts2和spring整合插件。。
4、struts.xml(程序员定义):它能配置的内容和struts-default.xml一样,重点要配置Action、自定义运行参数、自定义的拦截器
5、struts.properties(程序员定义):可以配置struts运行的参数
注意:图片中用的是FilterDispatcher类在struts2.1.3中已经过时了,改为了StrutsPrepareAndExecuteFilter类了
分支主题
通过ActionMapper寻找对应的Action。
1、web.xml检测到jsp表单里的提交到action的内容就会被检测拦截
2、然后通过StrutsPrepareAndExecuteFilte类进行过滤包装,将请求信息转发给ActionMapper,由ActionMapper检测这个信息是否要Struts2进行处理
3、需要,ActionMapper反馈给StrutsPrepareAndExecuteFilter,然后StrutsPrepareAndExecuteFilter创建ActionProxy对象
4、但ActionPropxy并不知道具体的action内容。于是又调用ConfigurationManager
5、ConfigurationManager之前将struts.xml,等一些struts2配置文件加载进入了内存。因此它能对struts.xml进行处理。
通过struts.xml的内容去查找相应的Action类。
那么如何查找呢?
分支主题
6、ActionProxy获知要使用的action后创建ActionInvocation类这个类包含四个属性.
分支主题
分支主题
基本架构
核心控制器
需要在web.xml中进行配置
对框架进行初始化,以及处理所有的请求
Action
Struts 2中核心控制器是Filter,Action是属于业务控制器,二者是有区别的
Result
实现对结果的调用
result元素的值指定对应的实际资源位置
name属性表示result逻辑名
Struts2的配置文件
加载时机: 当应用被tomcat加载的时候,struts2的配置文件就已经被加载过了。
constant元素
配置常量,可以改变Struts 2框架的一些行为
name属性表示常量名称,value属性表示常量值
https://www.cnblogs.com/shenming/p/4352341.html
package
将Action配置封装.就是可以在Package中配置很多action.
属性
name: 给包起个名字,起到标识作用.随便起.不能其他包名重复
namespace属性:给action的访问路径中定义一个命名空间
extends属性: 继承一个 指定包
abstract属性:包是否为抽象的; 标识性属性.标识该包不能独立运行.专门被继承
作用:简化维护工作,提高重用
注意:
包可以“继承”已定义的包,并可以添加自己包的配置
name属性为必需的且唯一,用于指定包的名称
extends属性指定要扩展的包
namespace属性定义该包中action的命名空间 ,可选
action
:配置action类
属性
name属性: 决定了Action访问资源名.
class属性: action的完整类名
method属性: 指定调用Action中的哪个方法来处理请求
作用
封装工作单元
数据转移的场所
返回结果字符串
result
结果配置
属性:
name属性: 标识结果处理的名称.与action方法的返回值对应.
type属性: 指定调用哪一个result类来处理结果,默认使用转发.
标签体:填写页面的相对路径
现Action中不同方法的调用
特点
避免动态方法调用的安全隐患
导致大量的Action配置
注意:
当多个配置文件中,有相同的参数,后面的会把前面的值给覆盖了
结果视图的配置
Result标签
(1)作用:配置结果视图(结果视图可以是一个jsp/html,也可以是一个action)
属性
name: 指定逻辑结果视图。
前提:和动作方法的返回值一致
作用:就是和动作方法的返回值进行比较,当一致时,前往当前配置的页面或者action
默认值:success
type:指定前往视图结果的方式。以何种方式前往
Type取值来源于struts-default.xml文件中package名称时sturts-defult包中定义类型;
分支主题
常用的结果类型:
Redirect:重定向(可以是重定向到另外的一个动作/jsp)
Dispatcher:请求转发(默认值)
RedirectAction:重定向到另一个动作(由于会自动在后面提添加.action,所以只能重定向到动作)
结果视图分类(全局结果视图/局部结果视图)
全局结果视图:
可以在多个action中使用
放在action标签外面,放在global-default标签内部的视图,
优先级:先找局部,再找全局
分支主题
局部结果视图
放在action里面,由result标签配置
分支主题
在动作类中访问Servlet的API
第一种方式:使用ServletActionContext类
HttpServletRequest
HttpServletRequest request = ServletActionContext.getRequest()
HttpServletResponse
HttpServletResponse response= ServletActionContext.getResponse()
HttpSession
HttpSession session=request.getSession()
ServletContext
ServletContext application =request.getServletContext()
第二种方式:使用实现接口的方式
HttpServletRequest :需要实现ServletRequestAware
HttpServletResponse:需要实现ServletRequestAware
HttpSession : 通过request对象获取
ServletContext : 需要实现ServletRequestAware
执行流程
struts-default.xml
我们在action标签内部不写任何的拦截器,默认执行defaultStack拦截器栈,如果定义了,则默认失效
Servletconfig拦截器分析
分支主题
Struts2进阶使用
封装请求正文到对象中
静态参数封装
在struts.xml配置文件中,给动作类注入值。调用的是setter方法。原因:是由一个staticParams的拦截器完成注入的。
动态参数封装:开发时用到的
1.属性驱动-没有实体类
表单数据都定义在动作类中,所以也称为动作类与模型写在一起
要求:
表单的name属性值,必须与动作中的成员get/set方法后面部分保持一致
注意事项:
1、Strut2框架会为我们解决post请求乱码问题,但是get请求不解决
2、Struts2框架会自动为我们转换数据类型:
(1)基本数据类型
(3)日期类型会按照本地格式转换成日期对象
(2)字符串数组或按照,+空格的方式拼接成字符串
①本地格式 yyyy-MM-dd
实现细节:
执行参数封装,是一个名为params的拦截器实现的
规则:去指定位置找属性,找到之后set方法赋值
分支主题
2.属性驱动-有实体
表单数据的字段定义到实体类中,把实体类定义在动作类中
分支主题
要求:
需要使用OGNL表达式指定表单元素的那么属性
OGNL表达式: Object Graphic Navigation language
对象 图 导航 语言
写法:
user.name user.age
分支主题
3.模型驱动 (使用最频繁)
要求:
1)动作类必须实现ModelDriver接口
2)动作类中必须定义模型,并且必须实例化
3)提供接口抽象方法实现,返回值必须是模型对象
实现细节:
模型驱动实现除了之前的params拦截器之外,还需要一个名叫modelDriver的拦截器配合
分支主题
用户注册案例
1、数据建模(实体模型和数据库)
2、建立业务层接口
3、建立持久层接口
4、数据源工具类
5、表现层使用Struts2框架实现
5.1、动作类:
5.2、配置文件
5.3、注册界面和结果视图
注册界面:
结果视图:
action优化使用
分支主题
动态方法调用
作用:减少Action数量
使用:actionName!methodName.action
禁用:将常量struts.enable.DynamicMethodInvocation设置为false (默认false)
分支主题
通配符(*)
作用:另一种形式的动态方法调用
分支主题
配置默认Action
没有Action匹配请求时,默认Action将被执行
通过<default-action-ref … />元素配置默认Action
分支主题
分支主题
分支主题
分支主题
数据验证
1、验证的方式
客户端验证:javascript
服务端验证:逻辑验证(我们的代码)
注意:如果客户端和服务端二选一的话,服务器端的不能省。
实际开发中:客户端+服务端
Struts2的服务端验证
2.1、编程式验证
前提:
动作类必须继承ActionSupport
分支主题
在代码中编写验证规则。
重写ActionSupport中的validate方法,根据自己的需求编写验证的方式和返回的信息
分支主题
在前端页面编写,添加异常信息的显示
普通表单的显示
分支主题
分支主题
struts表单的显示
分支主题
分支主题
a、针对动作类中的所有动作方法进行验证:
在动作类中覆盖public void validate()方法。
但是当我们再写一个动作方法时:
由此可知,该验证方法会对动作类中的所有动作方法进行验证。
b、针对动作类中的某个动作方法进行验证
针对上面的问题,解决办法:给不需要验证的动作方法添加一个@SkipValidation注解。
所有编程式验证的弊端:硬编码。
2.2、声明式验证(推荐)
通过编写验证规则的xml文件。需要验证时,编写xml文件,不要验证,就不写。
优势:解决了2.1编程式验证的弊端
a、针对动作类中的所有动作进行验证:在动作类所在的包中,建立一个ActionClassName-validation.xml的文件,内容如下:
注意:它是针对动作类中的所有动作方法。
分支主题
b、针对动作类中的某个动作进行验证:
它是针对指定动作方法进行验证:
给不需要验证的动作方法添加一个@SkipValidation注解。
分支主题
Struts2内置的常用声明式验证器
2.3.1位置:
xwork-core-2.3.15.3.jar\com\opensymphony\xwork2\validator\validator\default.xml
2.3.2、验证器注入参数
例如:我们使用requiredstring,默认是去空格,当我们不想去空格时,就可以给验证器注入参数。
基于字段的:
<!-- 基于字段的声明式验证 -->
<!-- field中name属性指定的是表单name属性的取值 -->
<field name="username">
<!-- struts2框架为我们集成了很多的内置验证器。requiredstring会验证输入内容是否为空,是否为空字符串。并且去掉左右空格-->
<field-validator type="requiredstring">
<!--给requiredstring的trim参数 通过 set方法注入参数-->
<param name="trim" >false</param>
<message>用户名必须存在</message>
</field-validator>
</field>
另一种基于验证器的:
<!-- 基于验证器的验证 -->
<validator type="requiredstring">
<!-- 以注入的方式,提供要验证的字段信息
setFieldName("password");
-->
<param name="fieldName">password</param>
<message>密码必须输入</message>
</validator>
常用验证器示例
分支主题
分支主题
分支主题
分支主题
数据类型的转换(明白原理,实际开发中几乎不用)
开发中的情况:实际开发中用户通过浏览器输入的数据都是String或者String[]。String/String[]————填充模型(set方法)————>POJO(plain old java object) pojo中有java的数据类型。POJO————————获取(get方法)————>页面展示:String
类型转换情况
写数据:(增,删,改)都是String或String[]数组转换为其他类型。
读数据:(查)其他类型转换为String。
Struts2提供的常用类型转换
a.基本数据类型自动转换。
b.日期类型:默认按照本地日期格式转换(yyyy-MM-dd)。
c.字符串数组:默认用逗号+空格,连接成一个字符串。
自定义类型转换器(知道)
示例:把日期格式按照 MM/dd/yyyy的格式转换
4.1、Struts2中的类型转换器结构:
4.2、编写类型转换器(编写一个类继承DefaultTypeConverter,实现抽象方法)
分支主题
4.3、注册类型转换器
局部类型转换器:只能指定javabean中的属性用
按照属性来注册。在属性所属的javabean的包下建立一个.properties文件。文件名称:javabean名称-conversion.properties
全局类型转换器:(推荐)
按照要转换的数据类型来注册。
at the top op classpath,建立一个固定名称xwork-conversion.properties的属性文件。
5、转换失败后的处理(需要掌握)
当转换失败后,页面提示:
解决办法:配置回显结果视图
问题:
配置了回显视图后,当转换失败时,可以回到请求页面,但是表单数据都没了?
显示错误提示:借助Struts2的标签库。
回显数据:使用struts2的标签库生成表单。(建议使用)
手动回显数据:可以借助EL表达式来获取。(是OGNL表达式,暂时知道就行)
错误信息中文提示:使用的是struts2的国际化。
问题:
类型转换器当转换失败后,如何进入input视图的?
原因:
是由一个叫做conversionError的拦截器完成的。
国际化概念(了解)
1、什么是国际化
软件的国际化:软件开发时,要使它能同时应对世界不同地区和国家的访问,并针对不同地区和国家的访问,提供相应的、符合来访者阅读习惯的页面或数据。
例子:亚马逊
中文:https://www.amazon.cn/
美国:https://www.amazon.com/
英国:https://www.amazon.co.uk/
2、什么需要国际化
程序:需要国际化。
数据:是什么样的就是什么样的。
比如:
用户注册的表单,有用户名,密码这5个汉字,在zh_CN语言环境,显示的就是用户名和密码。但是在en_US语言环境,显示的就应该是username和password。这就是程序。
用户名输入的是【张三】,密码输入的是【test】,那无论在什么语言环境都应该是是【张三】和【test】。这就是数据。
3、固定文本的国际化
例如:消息提示,错误提示和菜单,导航栏等等固定文本。
步骤:
3.1、创建一个消息资源包
一个资源包由多个文件组成,这些文件名都有命名规范:主要文件名_语言代码_国家代码.properties。 语言代码:由iso规定的。国家代码:有iso规定的
当文件只有主要文件名.properties时,表明它是默认资源包。浏览器会根据不同的语言环境找对应语言环境的资源包,当没有时,找默认的。
每个资源包的内容都由相同的key和对应语言环境的value组成。
比如:
message_zh_CN.properties message_zh_HK.properties message_en_US.properties
3.2、读取资源包中的内容
jsp中使用国际化:
使用jstl的fmt标签:
Struts2中的国际化(了解)
1、Struts2中使用国际化的前提
首先,我们要知道,在Struts2中,所有的消息提示都是基于国际化的。
其次,要想在Struts2中使用国际化,动作类必须继承ActionSupport类。
2、Struts2中使用国际化
2.1、配置资源包
a、配置全局资源包
b、配置包范围的资源包
资源包名称命名规范:package_语言代码_国家代码.properties(固定的)。以此种命名方式的资源包能被该包及其子包中的动作类访问。
优先级:高于全局消息资源包
c、局部消息资源包(只为动作类来使用的)
资源包名称命名规范:动作类名称_语言代码_国家代码.properties。以此种命名方式的资源包,只为动作类服务。
优先级最高(就近原则)。
Struts2中资源包的搜索顺序:
2.2、读取资源包的内容
a、动作类中的读取方式(实际开发中几乎从来不用)
b、在页面中读取资源包内容
直接访问jsp:
通过动作类访问jsp
c、自由指定读取资源包
Struts2高级核心
OGNL简介(非常重要)
1、什么是OGNL
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个单独的开源项目。
Struts2框架使用OGNL作为默认的表达式语言。
作用:存取java对象的任意属性,调用java对象的方法,OGNL成为了这个语义字符串与java对象之间沟通的桥梁
例如:user.name看上去是字符串,当它执行user对象的getName方法时,表示用OGNL表达式解释
分支主题
使用OGNL表达式获取数据,是我们在开发中经常用的
使用OGNL表达式实现给对象的赋值,是struts2框架做的。
2、OGNL的功能
前提:OGNL是struts2整合的一个开源项目,所以在struts2中,要想使用OGNL表达式,必须使用Struts2标签库
2.1、Properties标签 : 输出数据到浏览器
value属性:
是一个OGNL表达式,取到对应内容输出到浏览器上,如果没有任何对应内容则什么都不显示
2.2、OGNL和字符串的转换
表达式转字符串
%{“”}|%{‘’}
也可以将%{}去掉
字符串转OGNl
%{}将字符串套起来
2.3、OGNL访问对象方法
分支主题
2.4、OGNL访问对象的静态成员(静态属性)
访问静态属性需要按照固定的书写规范
规范是:@包名.包名..类名@静态名称
分支主题
2.5、OGNL访问对象的静态方法(静态属性)
访问静态属性需要按照固定的书写规范
OGNL调用静态方法:<s:property value="@java.lang.Math@random()"/>
使用前提是需要在struts.xml中进行配置
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
子主题 3
2.6、操作集合List与Map集合
1)List集合的使用:s:radio标签的list的属性值为ognl表达式
HTML的单选按钮
分支主题
OGNL的单选按钮
分支主题
2)Map集合的使用:
a.#{}表示创建map集合
b.#{‘key’,value}
注:虽然冒号编译不通过,但是可以执行
分支主题
Struts2对EL的改变
问题:学了OGNL表达式之后EL表达式还用不用?
EL表达式的所搜范围: page Scope————>request Scope————>sessionScope————>application Scope
当使用了struts2之后,它对request进行了增强,改变了EL表达式的所搜顺序;
表达式:page Scope————>request Scope————>valueStack(根中)————>contextMap————>sessionScope————>application Scope
contextMap(非常重要)
1、动作类的生命周期
明确:动作类是多例的,每次动作访问,动作类都会实例化。所以是线程安全的。与Struts1的区别是,struts1的动作类是单例的。
2、请求动作的数据存放
问题:
每次请求时,都会产生一些请求数据,这些数据存放到哪里去了?
明确:
在每次动作执行前,核心控制器StrutsPrepareAndExecuteFilter都会创建一个ActionContext和ValueStack对象。且每次动作访问都会创建。
这两个对象存储了整个动作访问期间用到的数据。并且把数据绑定到了线程局部变量(ThreadLocal)上了。所以是线程安全的。
3、contextMap:存储数据
分支主题
a.Application : 它是一个Map,里面存的是应用域中的所有数据
b.Session: 它是一个Map,里面存的是会话域中的所有数据
c.Request: 它是一个Map,里面存的是请求域中的所有数据
d.Value stack(root): 它是一个对象,LIst结构。
e.Action (the current action ): 它是一个对象,表示当前执行的action对象
f.Parameters : 它是一个Map,里面存的是请求参数。
g.Attr: 它是一个Map,里面存的都是四大作用域中的数据,并且查找顺序有小到大的顺序找
contextMap中的数据操作
分支主题
ActionContext以及它和ContextMap的关系
(1)ContextMap的解析并与ActionContext类的源码对比
分支主题
使用步骤:
A.获取ContextMap
B.根据key获取里面的map值
Object obj = ContextMap.get(“application”);
Map map = (Map)obj;
C.存入数据
(2)总结:ActionContext与ContextMap的关系
它是一个工具类,里面提供了便捷操作ContextMap的方法
ActionContext创建时间点: 每次请求都创建新的。放在struts2的核心过滤器的doFilter方法中创建
struts2的核心过滤器 = StrutsPrepareAndExecuteFilter
由于java ee应用是多线程的,它通过ActionContext绑定到ThreadLocal上实现了线程同步
我们通过代码得知,想要获取该对象需要调用ActionContext的静态方法getContext()从当前线程上获取
存数据:
需要熟悉ActionContext和valueStack的API。框架为我们存数据。
1.1、利用ActionContext存数据
1、创建
分支主题
2、存入
往actionContext中存入数据
分支主题
往应用域中存入数据
分支主题
往会话域中存入数据
分支主题
3、查看
<s:debug/>
4、获取
(1)使用OGNL获取map中的数据写法:
①#key
(2)如果还想继续往下获取,使用.key的方式
1.2、利用ValueStack存数据
分支主题
值栈分为两个部分
root专业叫做ObjectStack(对象栈)
平常我们用的值栈就是操作root里面的内容,很少去操作context。
结构是List集合。
获取方法:getRoot()
分支主题
context专业叫做ContextMap(Map栈),Map类型的栈。
在我们访问里面的对象的时候,会通过出栈的方式取东西,效率比较低,一般我们不会用
结构是Map集合
获取方法:getContext()
1、如何获取ValueStack:
分支主题
2、栈操作:
a.存入
1 向值栈放数据多种方式
第一种 获取值栈对象,调用值栈对象里面的 set 方法
分支主题
分支主题
第二种 获取值栈对象,调用值栈对象里面的 push方法
分支主题
分支主题
第三种 在action定义变量,生成变量的get方法
分支主题
2\向值栈放对象
实现步骤
第一步 定义对象变量
第二步 生成变量的get方法
第三步 在执行的方法里面向对象中设置值
分支主题
分支主题
3、向值栈放list集合
第一步 定义list集合变量
第二步 生成变量的get方法
第三步 在执行的方法里面向list集合设置值
分支主题
分支主题
b.查看
分支主题
2、取数据:用Struts2的标签(OGNL表达式)在JSP上(用的最多)
从值栈获取数据
a)OGNL获取值栈的数据,是直接写属性名称,得到的就是属性值
获取字符串
向值栈放字符串
分支主题
在jsp使用struts2标签+ognl表达式获取
分支主题
获取对象
1 向值栈放对象
分支主题
2 在页面中获取值
分支主题
获取list集合
第一种方式:
分支主题
第二种方式:
分支主题
第三种方式:
分支主题
其他操作
使用set方法向值栈放数据,获取
分支主题
(1)使用push方法设置值,没有名称,只有设置的值
(2)向值栈放数据,把向值栈放数据存到数组里面,数组名称 top,根据数组获取值
分支主题
Struts2中的拦截器(特别重要)
1、拦截器的重要性
Struts2中的很多功能都是由拦截器完成的。比如:servletConfig,staticParam,params,modelDriven等等。
是AOP编程思想的一种应用形式。
拦截器概述
拦截器
拦截器,在AOP中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。
拦截是AOP的一种实现策略。
拦截器是动态拦截Acion调用的对象。
一种可以提取action中可重用部分的方式。
拦截器链(Interceptor chain ,在struts2中被称为拦截器栈 Interceptor Stark)
将拦截器按一定的顺序连接成一条链。
在访问被拦截方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
拦截器的底层实现
拦截器方法都是通过代理的方式来调用的。
Struts 2的拦截器
当请 求到达Struts 2的ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器 对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。
Struts2拦截器是可插拔的,拦截器是AOP的一种实现
分支主题
拦截器底层使用两个原理
A.AOP思想
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP包含有基本功能,扩展功能,不通过修改源代码方式扩展功能
分支主题
B.责任链模式
(1)在java中有很多的设计模式,责任链模式是其中的一种
(2)责任链模式和过滤链很相似的
责任链模式:
要执行多个操作,有添加、修改、删除三个操作。
首先执行添加操作,添加操作执行之后 做类似于放行操作,执行修改操作,修改操作执行之后做类似于放行操作,执行删除操作
过滤链:
一个请求可有多个过滤器进行过滤,每个过滤器只有做放行才能到下一个过滤器
分支主题
aop思想和责任链模式如何应用到拦截器里面?
- 拦截器在action对象创建之后,action的方法执行之前执行
- 在action方法执行之前执行默认拦截器,执行过程使用aop思想,在action没有直接调用拦截器的方法,使用配置文件方式进行操作
- 在执行拦截器时候,执行很多的拦截器,这个过程使用责任链模式
-- 假如执行三个拦截器,执行拦截器1,执行拦截器1之后做放行操作,执行拦截器2,执行拦截器2之后做放行,执行拦截器3,执行拦截器3之后放行,执行action的方法
分支主题
源码分析
进入过滤器并执行action
分支主题
创建action对象,使用动态代理方式
分支主题
执行action的方法
分支主题
类似于放行的操作的方法
分支主题
执行很多的拦截器,遍历执行
分支主题
拦截器的作用
在不修改源码的基础上,对已有方法进行动态增强。
实现思想:抽取重复代码,统一管理,统一调用
拦截器的执行时机:
struts2.xml核心内部时,
在动作方法执行之前先正序执行
行完动作方法和视图结果之后,再倒序执行
拦截器与过滤器的区别
①拦截器是基于Java反射机制的,而过滤器是基于接口回调的。
②过滤器依赖于Servlet容器,而拦截器不依赖于Servlet容器。
③拦截器只能对Action请求起作用,而过滤器可以对所有请求起作用。
④拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。
自定义拦截器
查看源代码看拦截器结构
继承类
分支主题
在接口里面有三个方法
分支主题
操作
第一步:先创建一个普通类,继承AbstractInterceptor(也可以实现Interceptor接口)
第二步:配置拦截器
<!-- 声明一个拦截器 -->
<interceptors>
<interceptor name="Myinterceptor" class="com.struts.interceptor.MyInterceptor"/>
</interceptors>
<!--引用拦截器栈,在action标签中使用,当我使用自定义拦截器后,默认拦截器就会失效-->
<interceptor-ref name="Myinterceptor"/>
拦截器放行: invocation.invoke();
*关于结果视图的执行时机以及拦截器的返回值问题:
放行之前:拦截器的返回值可以控制显示哪个结果视图
放行之后:它一定会显示动作方法返回值所匹配的结果视图,此时已经不管拦截器返回的什么内容。
多个拦截器的执行顺序
是由引用顺序决定的,于声明顺序无关
解决登陆方法也要验证登陆的问题
1、将登陆的action提出出来,单独写道一个包中
2、继承MethodFilterInterceptor
1)配置不需要拦截的请求方法或者需要拦截的请求方法
开发中,建议使用另外一种方式
- 写类,继承 MethodFilterInterceptor类实现
-- 让action里面某个的方法不进行拦截
(3)让拦截器和action有关系
不是在action调用拦截器的方法,而是通过配置文件方式让建立关系
文件的上传(拦截器)和下载(stream结果类型)(需要练一遍)
1、文件上传
必要前提:
导入jar包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
a.表单method必须是post;
b.enctype取值必须是multipart/form-data;
c.提供文件选择域。
分支主题
动作类:
public class UploadAction {
private File upload;//定义一个File ,变量名要与jsp中的input标签的name一致
private String uploadContentType;//上传文件的mimeType类型
private String uploadFileName;//上传文件的名称
public File getUpload() {
return upload;
}
public void setUpload(File upload) {
this.upload = upload;
}
public String getUploadContentType() {
return uploadContentType;
}
public void setUploadContentType(String uploadContentType) {
this.uploadContentType = uploadContentType;
}
public String getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
public void uploadFile(){
try {
//在WebContent下新建一个upload的文件夹,获取其在服务器的绝对磁盘路径
String path = ServletActionContext.getServletContext().getRealPath("/upload");
//创建一个服务器端的文件
File dest = new File(path,uploadFileName);
//完成文件上传的操作
FileUtils.copyFile(upload, dest);
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意上传的文件是个临时文件,服务器重启后,文件就不存在了. 要想永久的保存,需要指定一个本地硬盘的目录.
struts.xml配置
分支主题
2、文件上传的配置
2.1、文件上传大小限制(默认是2MB)
如果上传文件超过了默认大小,upload拦截器会转向一个input的逻辑视图。
a、改变上传文件大小限制:
分支主题
单位:字节
2.2、限制文件上传的类型
a、通过限制上传文件的扩展名
分支主题
b、通过限制上传文件的类型
分支主题
分支主题
3、文件上传出现的问题以及解决方案
文件的大小问题
上传一个较大的文件,控制台会报空指针异常.
开启开发者模式
<constant name="struts.devMode" value="true"></constant
再次上传一个较大的文件,会发现这次在报的错误不再是空指针异常. 控制台报错信息如下
[http-bio-8080-exec-10] ERROR org.apache.struts2.dispatcher.Dispatcher - Could not find action or result
/strust2_fileupload/up
com.opensymphony.xwork2.config.ConfigurationException: No result defined for action com.thc.action.UploadAction and result input
解决方式:在struts.xml中的\ 标签中配置\名为input的视图 ,标签体为error.jsp
异常二:Request exceeded allowed size limit! Max size allowed is: 2,097,152 but request was: 1,346,882,290!
原因是在struts2框架default.properties配置文件中,定义了默认的文件上传大小为2097152字节(byte)为2M
在struts.xml的配置文件中,修改这个常量的大小.
<constant name="struts.multipart.maxSize" value="20971520"></constant>
4、多文件上传
jsp页面:
分支主题
配置文件:
分支主题
动作类:
分支主题
运行结果:
分支主题
5、文件下载:其实就是一种结果类型(Stream)
前端
分支主题
动作类:
分支主题
分支主题
//下载时候调用
public String downloadFile() throws UnsupportedEncodingException {
// 下载保存时的文件名和被下载的文件名一样
downPath = java.net.URLDecoder.decode(downPath,"UTF-8");
filename = downPath;
// 下载的文件路径,请在webapps目录下创建download目录,并预先放置好两个文件备用
downPath = "upload/" + downPath;
System.out.println("downPath:"+downPath);
System.out.println("filename:"+filename);
inputStream = ServletActionContext.getServletContext().getResourceAsStream(downPath);
System.out.println(inputStream);//若传过来的参数乱码,则inputStream为null
// 保存文件的类型
contentType = "image/jpeg";
//对下载的文件名按照UTF-8进行编码,解决下载窗口中的中文乱码问题
filename = MyUtil.toUTF8String(filename);
System.out.println(filename);
System.out.println("=============结束============");
return SUCCESS;
}
配置文件:
分支主题
contentType: 指定被下载文件的文件类型;
inputName: 指定被下载文件的入口输入流;
contentDisposition: 指定下载的文件名;
bufferSize: 指定下载文件时的缓冲大小。
运行结果:
分支主题
Struts2其他事项
OGNL配合通用标签的其他使用
1、iterator标签(很重要)
2、OGNL投影(了解)
2.1、使用过滤条件投影
2.2、投影指定属性
3、Struts2中#,$,%符号的使用(重要)
3.1、#
a、取contextMap中key时使用,例如<s:property value="#name" />
b、OGNL中创建Map对象时使用,例如:<s:radio list="#{'male':'男','female':'女'}" />
3.2、$
a、在JSP中使用EL表达式时使用,例如${name}
b、在xml配置文件中,编写OGNL表达式时使用,例如文件下载时,文件名编码。struts.xml——>${@java.net.URLEncoder.encode(filename)}
3.3、%
在struts2中,有些标签的value属性取值就是一个OGNL表达式,例如<s:property value="OGNL Expression" />
还有一部分标签,value属性的取值就是普通字符串,例如<s:textfield value="username"/>,如果想把一个普通的字符串强制看成时OGNL,就需要使用%{}把字符串套起来。
例如<s:textfield value="%{username}"/>。当然在<s:property value="%{OGNL Expression}" />也可以使用,但不会这么用。
4、其他标签
4.1、set标签
4.2、action标签
4.3、if标签,elseif标签 else标签
4.4、url和a标签(很有用)
Struts2的UI标签和主题
1、Struts2中UI标签的优势
自动的数据回显和错误提示功能
自带的简单样式和排版
2、表单标签的通用属性
说明:UI标签中value的取值一般都是字符串。
2.1、UI标签的通用属性
2.2、关于checkboxlist的使用:
2.3、UI标签的小案例以及模型驱动的分析
3、UI标签的模板(主题)
3.1、struts2中默认主题
默认主题的名称是XHTML,都是在struts的默认属性文件中定义着:default.properties
3.2、更改默认主题
a、更改表单某个元素的默认主题:使用的是表单元素的theme属性。
b、更改表单所有主题:使用的是form标签的theme属性。
c、更改全站所有表单主题:是在struts.xml配置文件中,覆盖原有主题的设置。
防止表单重复提交(拦截器)
1、回顾之前的解决办法:
2、Struts2中的解决办法:
2.1、使用重定向
遗留的问题:防不住后退,再提交。
2.2、使用<s:token/>生成令牌配合token拦截器
点击后退的时候,会提示:
配置结果视图:
遗留的问题:此种解决方式,是产生了错误之后再告知用户,你错了。
2.3、使用<s:token/>生成令牌配合tokensession拦截器
Springboot+hibernate+struts2
1、springboot整合hibernate
(1)首先添加依赖
分支主题
(2)编写hibernate核心配置文件
分支主题
(3)生成实体类
分支主题
分支主题
分支主题
分支主题
分支主题
因为没有配置sessionFactory所有不能选择其他选项,否则不会生成成功
如果需要使用注解,可自己配置
分支主题
(4)编写dao层
分支主题
JpaRepostory -- springbootJpa提供的封装操作session的类
UsersEntity -- 实体类名称
(5)编写service层
分支主题
分支主题
(6)测试
分支主题
分支主题
2、Springboot 整合 struts2
(1)导入依赖
<dependencies>
<!--mysql支持 MySQL的JDBC驱动包,用JDBC连接MySQL数据库时必须使用该jar包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--事务管理:原子性,一致性,隔离性,持久性 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--jpa 集成hibernate -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--springboot集成struts2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--debug模式下自动编译-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- springBoot整合jsp-->
<!--jsp页面使用jstl标签-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!--用于编译jsp-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.28</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.3.28</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>2.3.28</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
</dependencies>
<!-- 文件资源-->
<resources>
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/**</include>
</includes>
</resource>
</resources>
(2)Web.xml配置文件代替配置文件配置
@Configuration
public class ServletFilterConfig {
/**
* 因为这里是使用main方法加载的所以没有配置文件这里需要将struts2的核心拦截器实例
* 否则无法访问struts2
* 而web项目中struts2的配置文件如下:
* <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>
* 所以我们需要在这个配置类中将web中配置的拦截器类创建这样才实现了struts2的核心配置
* 而路径如果不配置的话默认的就是拦截所有路径
* @return
*/
@Bean
public FilterRegistrationBean filterRegistrationBean (){
FilterRegistrationBean frgb = new FilterRegistrationBean();
frgb.setFilter(new StrutsPrepareAndExecuteFilter());
List list = new ArrayList();
list.add("/*");
list.add("*.action");
frgb.setUrlPatterns(list);
return frgb;
}
}
(3) 使用JSP则关闭spring boot默认的模板
分支主题
(4)Struts.xml配置文件
<struts>
<!-- 表示从cn包下开始扫描 -->
<constant name="struts.convention.package.locators.basePackage"
value="com"/>
<!-- 表示从cn包下开始加载注解 -->
<constant name="struts.convention.package.locators"
value="com"/>
<!-- 表示struts的工厂类交给Spring实例 -->
<constant name="struts.objectFactory" value="spring"/>
<!--
SpringBoot集成struts2这个必须要配置
表示不把SpringBoot内置Tomcat的类加载器排除在外
默认是true 将类加载器排除了所以就算上面配置
如果这个没有配置就算运行不报错但是Action还是访问不了
-->
<constant name="struts.convention.exclude.parentClassLoader" value="false"/>
</struts>
(5)Action类的编写
@Actions
@Namespace("/")
public class UsersAction extends ActionSupport implements ModelDriven<UsersEntity> {
private UsersEntity entity;
@Override
public UsersEntity getModel() {
if(entity==null){
entity=new UsersEntity();
}
return entity;
}
@Autowired
private UsersService service;
@Action(value = "/login")
public String login() throws IOException {
UsersEntity user =
service.selectByNameAndPwd(entity.getName(),entity.getPassword());
JSONObject json=new JSONObject();
if(user != null ){
json.put("message","登录成功");
HttpSession session = ServletActionContext.getRequest().getSession();
session.setAttribute("name",entity.getName());
}else{
json.put("message","登录失败,请检查用户名/密码是否正确");
}
String jsons = json.toString();
ServletOutputStream os = ServletActionContext.getResponse().getOutputStream();
os.write(jsons.getBytes("utf-8"));
return NONE;
}
}
(6)测试
分支主题
0 条评论
下一页