框架,最佳实践,javaEE的层次,MVC,前端控制器
框架:
什么是框架,框架从何而来,为什么使用框架?
框架(framework):
1.是一系列jar包,其本质是对JDK功能的拓展(JDK就只能开发JavaSE最原生的东西).
2.框架是一组程序的集合,包含了一系列的最佳实践,作用是解决某一个领域的问题.
小推论:不同类型的框架,旨在解决不同领域的问题.
Web开发中的最佳实践:分层开发模式(技术层面的"分而治之")
JavaEE开发根据职责的纵向划分:表现层,业务层,持久层:
表现层(Predentation Layer):(web/MVC层)负责处理与界面交互的相关操作 .
代表框架:Struts/Webwork/Struts2/SpringMVC/EasyJweb
业务层(Business Layer):service层:负责复杂的业务逻辑计算和判断.
代表框架:Spring.
持久层(Persistent Layer):DAO层:负责将业务逻辑数据进行持久化存储(
代表框架:Hibernate,MyBatis(iBatis)).
最佳实践(Best Practice):实际上是无数程序员经历过无数次尝试之后,总结出来的处理特定问题的特定方法.
最佳实践三要素:可读性,可维护性,可拓展性.
简单就是美:
消除重复
化繁为简
简单必须可读,简单必须可拓展
减少依赖,消除耦合
J2ee先后的设计思想:
Model1:模式一.
在早期的时候J2ee的开发模式,是以JSP为中心.
使用到技术 JSP + JavaBean
--------------------------------------------------
如果仅仅是开发一个简单的应用,完全足也.
存在问题:
此时JSP不仅仅需要展现页面,还得处理请求.
而JSP本身是不擅长处理请求的.
Model2:模式二.
为了解决Model1中JSP不擅长处理请求的操作,在Model2中引用了Servlet,专门用于处理请求.
使用到技术:JSP + Servlet +JavaBean
在Model2中,以Servlet为中心(所有的请求都先发给Servlet).
------------------------------------------------------
责任分类思想(各自做给自最擅长的事情):
JSP: 界面展现.
Servlet:
1:接受请求参数,封装成对象.
2:调用业务方法处理请求.
3:控制界面跳转
JavaBean: 封装功能操作,可以复用.
Model2体现了MVC的思想:
MVC :
MVC架构型模式,它本身并不引入新的功能,只是用来指导我们改善应用程序的架构,使得应用的模型和视图相分离,从而得到更好的开发和维护效率。
--------------------------------------------------------------------------------
数据模型(Model):负责封装应用的状态,并实现应用的功能。通常又分为数据模型和业务逻辑模型,数据模型用来存放业务数据,比如订单信息、用户信息等;而业务逻辑模型包含应用的业务操作,比如订单的添加或者修改等。(domain,dao,service)
视图展现(View):界面,用来将模型的内容展现给用户,用户可以通过视图来请求模型进行更新。视图从模型获得要展示的数据,然后用自己的方式展现给用户,相当于提供界面来与用户进行人机交互;用户在界面上操作或者填写完成后,会点击提交按钮或是以其它触发事件的方式,来向控制器发出请求(JSP/html/富客户端技术(Flash:Flex/Java FX/Extjs/EasyUI))。
控制器(Controller):用来控制应用程序的流程和处理视图所发出的请求。当控制器接收到用户的请求后,会将用户的数据和模型的更新相映射,也就是调用模型来实现用户请求的功能;然后控制器会选择用于响应的视图,把模型更新后的数据展示给用户(Servlet/Filter/Struts2/SpringMVC)。
--------------------------------------------------
我们把JavaEE中的表现层的框架称之为MVC框架.
MVC框架的功能作用(WEB开发常见功能)
设置编码、接受请求参数、请求数据验证、处理响应、参数类型转换、
把参数封装成对象、文件上传下载、国际化、令牌机制、自定义标签等。
前端控制器:
前端控制器(front controller):J2EE中的设计模式:
主要提供一种可以集中式管理请求的控制器,一个前端控制器可以接受所有的客户请求,完成大部分通用的功能,并将每个请求递交给相应的请求处理对象(Action/Controler),并适当地响应用户。
前端控制器完成大部分通用功能,把把具体的操作交给各个Action去完成。
MVC框架都有前端控制器,使用MVC框架的步骤:jar-->配-->加-->配--运-->配
1):拷贝框架依赖的jar.
2):在web.xml中配置前端控制器.
ThreadLocal:又称为“线程局部变量”,它为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突(好比每一个线程都使用的是一个新的变量)。
模拟MVC框架:
目的:对不同的请求,完成大部分相同的操作后(Filter),然后分发给相应Action
我们已有的东西:url(用户提供)--Filter--Action
从url(用户提供)--Filter--Action到想要的结果,Filter需要做的是完成大部分相同的操作,其他操作由Action完成.-->即Filter需要做的是如果有共同操作则对所有请求的统一初始化操作+识别uri调用Action中相应的方法
由此可知对于Filter来说,Filter需要的是能够识别所有Url,而且知道Url和Action的方法之间的映射关系,在调用方法时知道Action中的所有方法的方法名,方法所在类包,方法的参数
1.如何识别Url?
过滤器配置在web.xml中即可
2.如何映射Url和Action的方法?
a)在Filter中用if方法如果url=url1,则调用的方法时method=method1
b)在Action对应的xml中将url名和方法名配置在一起如
3.如何知道Action的方法的方法名,方法所在包和类?
a)在调用方法的时候直接写明方法的所在包类和方法名.
b)在配置文件中配置url和对应的method时,同时写明方法的名字,所在类和包.
4.如果Action的方法中没有写跳转,Filter除了负责调用Action中的方法外,还需要处理Action方法的跳转,则Filter必须还要知道跳转与方法执行结果的映射关系,跳转的方式,跳转的地址,那么怎样去获取这些信息呢?
在配置文件值直接将方法执行结果与跳转配置在一起,由于方法的执行结果往往是不确定的所以其跳转也是多种的,所以需要配置多个这样的标签
这样做的问题:由于xml中很多的关联关系是通过”写在一起”方式来表达的,所以在Filter中处理这个xml文件时需要时刻注意多个属性(如:uri,class,method是否是在同一个标签中),这样Filter需要过分注意xml的结构关系,这样会导致filter的执行比较”难”,所以可以利用一个封装类ActionConfig封装class和method来表达package,class,method,results是否是一组的
为了保证在Action中能够得到请求request和响应response的相关信息,所以需要设定一个传递request和response的类ActionContext(封装request和response,然后提供他们的setter和getter方法).
Struts2的发展:
Struts2的前世今生:
------------------------------------------------------------------
1.早期开发模型Servlet+JSP+JavaBean(Model2)显得力不从心:
流程凌乱、数据传递无序、缺乏辅助功能。
2.MVC模式的轻量级Web应用框架:Apache Struts1很快风靡全球。
代码结构划分合理,实用工具框架(如验证框架、国际化框架)等。
3.时间推移,Struts1的缺点:
线程不安全、灵活性低、和ServletAPI耦合、页面传值麻烦等。
4.异军突起,SpringMVC和OpenSymphony的WebWork等。
5.Apache Struts + OpenSymphony WebWork2 = Struts2
-------------------------------------------------------------------
Struts2:基于MVC的轻量级的Web应用框架,
来源于Webwork2,与Struts1.x完全不兼容
====================================================
Struts2 是一个非常优秀的MVC框架,基于Model2 设计模型.
由传统Struts1和WebWork两个经典框架发展而来:
Struts2框架=Struts2+XWork
Strust2 核心功能:
允许POJO(Plain Old Java Objects)对象 作为Action.
Action的execute 方法不再与Servlet API耦合,更易测试
支持更多视图技术(JSP、FreeMarker、Velocity)
基于Spring AOP思想的拦截器机制,更易扩展
更强大、更易用输入校验功能
整合Ajax支持
等等
Struts2的组成:
struts2目录结构:
apps:该文件夹包含了基于struts2 的示例应用,这些示例应用对于学习者是非常有用的
docs:该文件夹下包含了struts2 相关文档,包括struts2 快速入门、struts2的文档以及API文档等
lib:该文件夹下包含了Struts2框架和核心类库,以及struts2第三方插件类库
src: 该文件夹下包含了Struts2框架的全部源代码
开发Struts2:
开发Struts2必须依赖的jar:
struts2-core-2.3.1.1.jar:Struts 2框架的核心类库
xwork-core-2.3.1.1.jar:Command模式框架,WebWork和Struts2都基于xwork
ognl-3.0.3.jar:对象图导航语言(Object Graph Navigation Language), struts2框架通过其读写对象的属性
freemarker-2.3.18.jar:Struts 2的UI标签的模板使用FreeMarker编写
commons-logging-1.1.x.jar:ASF出品的日志包,Struts 2框架使用这个日志包来支持Log4J和JDK 1.4+的日志记录。
commons-fileupload-1.2.2.jar: 文件上传组件,2.1.6版本后需要加入此文件
commons-io-2.0.1.jar:传文件依赖的jar包
commons-lang-2.5.jar:对java.lang包的增强
步骤:
第一个Struts2程序步骤:
1.准备Struts2依赖的jar文件.
注意:别拷贝Struts2中lib下所有的jar,必须的jar如右.
Struts2根/apps下,解压struts2-blank.war,拷贝其WEB-INF/lib中的所有的jar到自己的项目中.
2.在web.xml中配置前端控制器.StrutsPrepareAndExecuteFilter-(参阅struts2-blank项目的web.xml文件.)
3.定义一个POJO类:HelloAction,提供一个execute方法(公共无参数).
4.准备Struts2的配置文件:struts.xml.从struts2-blank\WEB-INF\classes中拷贝到项目的source folader目录.
5:部署项目,访问Action:
访问格式:http://ip:port/contextPath/namespace/actionName[.action]
http://localhost:80/oa/hello.action
Struts2的执行流程:
1.简单执行流程:
2.完整执行流程:
Struts2的执行步骤:
-----------------------------------------------------------
①.客户端发送请求;
②.该请求经过一系列的过滤器(Filter):其中可选过滤器ActionContextCleanUp,帮助Struts2和其他框架集成。例如:SiteMesh Plugin。
③.接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper,来决定该请求是否需要调用某个Action。
④.若ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy。
⑤.ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类。
⑥.ActionProxy创建一个ActionInvocation的实例。
⑦.ActionInvocation实例调用Action的前后,涉及到相关拦截器(Intercepter)的调用。
⑧.一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果是一个JSP或其他页面(也可以是其他的Action链)。 JSP页面展现可使用Struts2框架中的标签(该过程会涉及ActionMapper).
---------------------------------------------
在Strtus2框架中肯定会创建很多对象(Action,Proxy,Intercetor),都是由xwork容器负责创建的.在xwork容器中有一个类:ObjectFactory(对象创建).
3.Struts2的配置文件和简单配置
在struts.xml中引入其他的配置文件:
语法:
-----------------------------------------------------------------------------
在大部分应用里,随着应用规模的增加,系统中Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿。为了避免struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以将一个struts.xml配置文件分解成多个配置文件,然后在struts.xml文件中包含其他配置文件。下面的struts.xml通过
Struts2中的6大配置文件:
-----------------------------------------
Struts2框架按照如下顺序加载struts2配置:
1.default.properties:该文件保存在 struts2-core-2.3.7.jar 中 org.apache.struts2包里面:包含了Struts2的默认常量配置(编码,比如action的拓展名等).
2.struts-default.xml:该文件保存在 struts2-core-2.3.7.jar:包含了框架依赖的对象配置和结果类型,拦截器等配置.
3.struts-plugin.xml: 该文件保存在Struts2框架的插件中:struts-Xxx-2.3.7.jar.由插件提供
上述三个文件时框架自带的,我们不能修改,只能使用.
---------------------------------------------------------
4.struts.xml(使用最多的文件):该文件是web应用默认的struts配置文件.重点.配置自定义的Action和其他信息.
5.struts.properties:该文件是Struts的默认配置文件-->可以修改default.properties 的常量配置.
6.web.xml 该文件是Web应用的配置文件
上述三个文件是我们可以修改操作的.
---------------------------------------------------------
如果多个文件配置了同一个struts2 常量,则后一个文件中配置的常量值会覆盖前面文件配置的常量值.
注意:一般的,我们只在struts.xml中做常量配置.
常见的常量配置:
---------------------------------------
指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出
该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。
如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开
设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭
当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开
开发模式下使用,这样可以打印出更详细的错误信息
默认的视图主题
是否支持动态方法调用
package元素:是struts.xml根元素(strtus)的子元素. 和java中的package没有一点关系.
仅仅是为了统一管理多个action元素.
格式为:
可以一个模块分出一个
常用的属性:
name: 表示当前
extends: 自定义的
如此一来,当前
namespace: 命名空间,和action的name共同决定了一个action的访问路径.
abstract: 表示当前
如果为true,那么当前
-----------------------------------------------------------------------
开发中的最佳实践:
多个package共同的配置信息:
OA:
CRM:
action元素:是
语法:
常用的属性:
name: action的名称,该名称和当前action所在package的namespace共同决定了访问路径.
访问Action的格式:http://ip:port/contextPath/namsspace/actionName[.action]
注意:action名称没有/,在同一个
class: Action类的全限定名,表示把哪一个Action类交给Strtus2框架来管理.
class的默认值:com.opensymphony.xwork2.ActionSupport.
method: 当前action需要执行哪一个方法,注意:该方法必须在class对应的类中.
method的默认值:execute.
result元素:是
语法格式:
result元素的配置:
name属性: 唯一,表示逻辑视图名称,该名称就是action方法的返回结果.默认值:success.
type属性: 表示资源的跳转方式(请求转发/URL重定向),阅读struts-default包.默认:dispatcher
文本内容: 表示需要跳转资源的路径(从webapp下开始寻找).
result元素的type属性的常用值:
dispatcher: 请求转发:(Action请求转发到JSP),默认的.
chain: 请求转发:(Action请求转发到Action).
redirect: URL重定向:(Action重定向到JSP).
redirectAction: URL重定向:(Action重定向到Action).
stream: 文件下载使用,输入流.
-------------------------------------------------
result元素分类:
1):局部的结果视图:配置在
2):全局的结果视图:配置在
先从当前Action中去找指定名字的result,找到就跳转,找不到,再找全局的result,找到跳转,找不到,就报404 错误.
Action类的三种编写方式:
第一种.使用公共的POJO类作为Action. 提供公共的无参数的Action方法.(不推荐).
缺点:
没有一种方式约束Action方法必须是公共的无参数的.
Action方法的返回逻辑视图名可以自定指定. 有时起名不规范. 比如:"ooxx".
解决方案:第二种.
第二种.定义一个类,实现于com.opensymphony.xwork2.Action接口.并覆写execute方法即可.(不推荐)
Action接口中,不仅提供了Action方法的声明,也提供了常用的逻辑视图名称:
public static final String SUCCESS = "success";
public static final String NONE = "none";
public static final String ERROR = "error";
public static final String INPUT = "input";
public static final String LOGIN = "login";
缺点:
不支持国际化,数据校验,消息机制.
解决方案:第三种:
第三种.定义一个类,继承于com.opensymphony.xwork2.ActionSupport类.(推荐)
public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable {}
---------------------------------------------
真实开发中,我们却往往再提供一个BaseAction类.
ActionSupport.
---BaseAction(自定义Action的基类,存储AAction,BAction共同的代码.)
-----AAction
-----BAction
Action中多个Action方法会造成
解决方案:
方案1: DMI:动态方法调用 :官方不推荐.
格式: action名!方法名
比如: emp!edit emp!list
在Struts2新的版本中,默认的关闭了DMI.若我们需要使用DMI,就需要配置常量,启用动态方法调用.
此时:
注意:
1,开启动态方法调用,配置struts静态常量:DynamicMethodInvocation;
2,只需要配置一个action,并使用【action名称!action方法名称】的方式访问;
3,动态方法调用安全性很差,没法控制action中方法的访问权限;官方建议不使用;
方案2:使用通配符来访问(*).
使用*来完成通配,*可以表示任意字符;
每一个请求路径上的*都可以匹配具体请求的一段文字,并且可以再整个action配置中,使用{1},{2},{3}来完成替换;
访问Servlet的API:
什么是访问Servlet的API:
---------------------------------------------------------
使用JavaWeb中定义的相关的接口和类,详细如下:
1):通过请求对象获取请求参数(HttpServletRequest对象).
2):通过响应对象来处理响应操作(HttpServletResponse对象).
3):操作Servlet中的三大作用域对象(requerst,session,servletContext).
1>:HttpServletRequest
2>:HttpSession
3>:ServletContext
4):操作Cookie(Cookie对象).
---------------------------------------------------------
在Strtus2中访问ServletApi三种方式:
方式1: 通过实现感知接口(使用非常少).
方式2: 通过ServletActionContext类来访问(开发中使用比较多).
方式3: 通过ActionContext来访问(最规范的方式,开发中使用也比较多,操作作用域).
如果开发中需要使用到Cookie,此时就可以方式2.
方式1:通过实现感知接口.
需要访问/操作哪一个Servlet的API,就只需要让Action实现对应的感知接口.
HttpServletRequest ServletRequestAware
HttpServletResponse ServletResponseAware
ServletContext ServletContextAware
再实现各自的方法,具体如下:
实现感知接口的方式来访问Servlet的API:
1):确实很简单.
2):此时Action和Servlet的API严重耦合在一起.
也就是说,使用Strtus2必须依赖Servlet的相关的jar包.
解决方案:参照方式2.
方式2:通过ServletActionContext工具类.
ServletActionContext工具类,封装了获取request和response的静态方法.
常用方法:
static HttpServletRequest getRequest():获取请求对象
static HttpServletResponse getResponse():获取响应对象
通过ServletActionContext工具类.
1):也很简单也很直观.
2):也存在Action和ServletAPI耦合的问题.
3):但是在开发中很多人依然使用这种方式.
如果我想完全让Action和ServletAPI解除耦合,方式3.
方式3:通过ActionContext类.
ActionContext类,表示Action的上下文,每一次请求都是一个新的Action对象,可以先简单的理解为:ActionContext就封装了这一次请求所有相关的数据.
创建ActionContext对象:
ActionContext ctx = ActionContext.getContext();
常用的方法:
操作request作用域:
void put(String key,Object value):把共享数据存放在request中. 好比:request.setAttribute(String key,Object value);
Object get(String key):从request中取出共享数据. 好比:Object value = request.getAttribute(String key);
操作session作用域:
先获取Session的Map对象:Map
操作application作用域:
先获取application的Map对象:Map
获取请求参数:
先获取参数的Map集合,再从Map中取出每一个参数.
Map
J2EE的最佳实践:按照功能职责,分层开发(表现层/业务层/持久层):
表现层:
职责:
1.接受请求数据
2.把数据封装Model对象
3.调用业务逻辑方法处理请求
4.控制界面跳转
-------------------------------------------------
MVC思想:
M:Model.
V:JSP
C:StrutsPrepareAndExecuteFilter(前端控制器(分发请求).)
问题:Action到底充当什么角色?-->贴近控制器的Model,普通的Model(domain)都是贴近数据库的Model
-------------------------------------------------
Action获取请求参数三种方式:
第一种:Action本身作为Model对象,通过setter方法封装请求参数(属性注入)
第二种:创建独立Model对象,页面通过OGNL表达式封装(属性注入)
第三种:Action实现ModelDriven接口,对请求数据进行封装(模型驱动)
第一种:Action本身作为Model对象,通过setter方法封装请求参数(属性注入):
此时Action只需要提供参数同名的属性(setter方法,只需要给Action设置值.)
优点:简单,直观.
缺点:如果参数比较多,此时Action就的提供N个setter方法,感觉很繁琐.
------------------------------------
在这里偷偷把参数传递给Action的是拦截器:
在这里,也感受了,Struts2内置的类型转换器.
在Struts2中,每一次请求都是一个新的Action对象,在Action中的成员变量是线程安全的.
第二种:创建独立Model对象,页面通过OGNL表达式封装(属性注入).
此时的方式:
优点:把多个参数信息封装到对象,很容易.
缺点:JSP中,表单的参数名称,变得美工看不懂了.
第三种:Action实现ModelDriven接口,对请求数据进行封装(模型驱动):
该方式:
优点:JSP表单干净,也抽取了Model对象,Action也比较简单.
缺点:一个Action只能把请求的参数封装到一种类型的对象中去.
--------------------------------------------------
开发中的选择:
1):如果参数少,或者没有Model对象,选择第一种方式.
2):第二种方式使用最多.
3):在编写统一的CRUD的时候,方式3有优势.
-------------------------------------------------------
有时候,我们会综合应用,举个例子.
修改密码案例:
用户名 :____________________
原始密码:____________________
新 密 码:____________________
确认密码:____________________
因为在:User对象中,只有一个密码,没有新密码,确认密码的属性.
此时,方式1+方式2,或者方式1+方式3来完成.
把请求数据封装到集合中:
1):把多组请求数据封装到List集合.(常用)
2):把多组请求数据封装到Map集合.
拦截器:Interceptor,使用动态代理机制来实现,和Spring中的AOP是同一种思想.
------------------------------
拦截器:Struts2拦截器是在访问某个Action或Action的某个方法之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现.
AOP:面向切面编程.其实现原理:动态代理模式--->留给Spring
WebWork中文文档解释:拦截器是动态拦截Action而调用的对象。它提供了一种机制可以使开发者可以定义在一个Action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也提供了一种可以提取Action中可重用的代码的方式。
拦截器栈(Interceptor Stack):Struts2拦截器栈就是将拦截器按一定的顺序连接成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
-------------------------------------------------------------------------
拦截器的"美":
---------------------------------------------------
DRY原则:Dont't Repeat Yourself.
拦截器在设计和程序结构上的优点:
拦截器能把很多功能从Action中独立出来,分散到不同的拦截器里面,减少了Action的代码。如此,拦截器和Action本身的功能都更单一了。当通用的功能代码被封装在拦截器里面(代码模块化),就可以对不同的Action,根据功能需要,来配置相应功能的拦截器了。提高了拦截器所实现的功能的重用性,也变相实现了装配式和可插拔式的体系结构,使得整个系统结构变得更灵活。
1.简化Action的实现
2.功能更单一
3.通用代码模块化
4.提高重用性
Struts2中内置的拦截器:
在struts-core-2.3.x.jar--->struts-default.xml中
------------------------------------------
常见的拦截器:
1:params拦截器
这个拦截器偷偷的把请求参数设置到相应的Action的属性去的,并自动进行类型转换。
2.modelDriven拦截器
如果Action实现ModelDriven接口,它将getModel()取得的模型对象存入OgnlValueStack中。
3.execption拦截器
顾名思义,在抛出异常的时候,这个拦截器起作用。最好把它放在第一位,让它能捕获所有的异常。
4.validation拦截器
调用验证框架读取 *-validation.xml文件,并且应用在这些文件中声明的校验。
5.token拦截器
核对当前Action请求(request)的有效标识,防止重复提交Action请求。
6.fileUpload拦截器
用来处理文件上传
7.workflow拦截器
调用Action的validate方法,一旦有错误返回,重新定位到INPUT结果视图
8.servletConfig
通过感知接口,获取感应对象.
内置拦截器:
感知接口对应的拦截器
自定义拦截器(LoginInterceptor),控制必须登陆之后才能访问MainAction,如果没有登陆强制访问MainAction,则应该自动跳转到login.jsp页面:
--------------------------------------------------------------------
操作步骤:
第一步:定义拦截器类.
方式1:直接实现于com.opensymphony.xwork2.interceptor.Interceptor接口(太弱了).
方式2:直接继承于com.opensymphony.xwork2.interceptor.AbstractInterceptor(用的多).
方式3:直接继承于com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.
excludeMethods:可以设置该拦截器排除对哪些方法的拦截操作.
includeMethods:可以设置该拦截器必须等哪些方法做拦截操作.
第二步:在strtus.xml中注册该自定义拦截器.
第三步:引用自定义拦截器(在action元素或者package元素真正的来引用拦截器,生效).
待会截完整的图.
第一个版本:
上述代码和配置针对于上述案例没有问题,但是:
仅仅是在main的action元素中,引用了LoginInterceptor拦截器,那如果多个
解决方案:让loginInterceptor作用域于整个
此时发现:编写了package的默认拦截器之后,再也不能接受到请求参数了.
原因是:当我们自己设置默认拦截器之后,会覆盖了
解决方案:同时引用两个拦截器.
版本二:
Struts中给页面传递数据的方式:
1):通过Servlet的API来传递. request.setAttribute(String key,Object value).
太依赖Servlet API了,很不方便.
2):在Strtus2中有新的给页面传递数据的方式,OGNL-->升级版的EL:(依赖Strtus2标签)
---------------------------------------------------------------------------------------------
1.什么是OGNL
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。
Struts2框架使用OGNL作为默认的表达式语言。
EL(表达式语言),OGNL就是EL的升级版.
作用:Action和视图(JSP)之间数据交互的桥梁.
讲解OGNL之前,先得学习ValueStack.
--------------------------------------------------------------
2.什么是ValueStack(值栈)
值栈是对应每一个请求对象的轻量级的内存数据中心。
每一次请求的时候,都会创建一个新的ValueStack对象,该ValueStack对象封装了这一次请求相关的数据信息.
1).ValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础。
2).ValueStack贯穿整个Action的生命周期(一次请求到响应):每个Action类的实例都拥有一个ValueStack对象。 ValueStack相当于数据的中转站,在其中保存该次请求和当前Action对象和其他相关对象信息。
3).Struts2框架把ValueStack对象保存在名为“struts.valueStack”的request属性中。
新的请求:
ValueStack vs = new ValueStack..;
request.setAttribute("struts.valueStack",vs);
3.如何获取ValueStack对象.
方式1(最传统的方式):
ValueStack vs = (ValueStatck)ServletActionContext.getRequest().getAttribute("struts.valueStack");
ValueStack vs = (ValueStatck)ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
方式2(最简单的方式):
ValueStack vs3 = ActionContext.getContext().getValueStack();
4.ValueStack内部结构
ValueStack对象中有两个很重要的属性,这两个属性就是ValueStack的主要内部结构:
------------------------------------
root: 类型: CompoundRoot extends ArrayList : 栈的数据结构(后进先出)
context: 类型: Map: : 上下文
root:主要存储Action对象相关的数据信息.
context:主要存储映射关系数据.(key-value).
context存在root对象的引用(只要拿到context对象就可以获取到root对象):
context中还存在request、session、application、attr、parameters对象的引用。
获取到context,就可以获取ValueStack中所有的数据.
从root中获取数据: 直接使用属性名获取. --->
从context中获取数据: #key --->
#:表示ActionContext.getContext():其实就是我们的上下文.
5:Action如何往ValueStack中存储数据,JSP如何从ValueStack中取出数据.
1).把数据放入root中:(栈,ArrayList.每次都要压在栈顶)
方式1:ValueStack对象.getRoot().add(0, Obejct val);//把数据压入栈顶
方式2:ValueStack对象.getRoot().push(Object val):等价于valueStack对象.getRoot().add(0, Obejct val);
方式3:ValueStack对象.set(String propertyName,Object value);
方式4.在Action中提供一个可访问的属性(getter方法).
一般的,把单个对象可放入root中,就在栈顶.
------------------------------------------------------------------
2).把数据放入context中:
方式1:ValueStack对象.getContext().put(String key,Object value); 太长了.
方式2:ActionContext对象.put(String key,Object value);
一般的:把集合中的数据放入context中.
-------------------------------------------------------------------
如何从JSP中取出ValueStack中的数据:
此时必须使用Struts的标签.--->先引入Struts2标签
<%@ taglib uri="/struts-tags" prefix="s"%>
Struts2的调试标签:
访问方式:
1).获取root中数据:
若:放入root中的数据,没有属性名:
若:放入root中的数据有属性名:
2).把context中数据:
------------------------------------------------------------------
EL可以访问ValueStack中的数据:
不建议这么做:
为什么可以呢: Struts2重新包装而来请求对象.${msg}---><%=pageContext.findAttribute("msg") %>
StrutsRequestWrapper:先从ValueStack中取出数据,再放入request中.
第一个Struts2的标签:
第一步:在JSP中,引入Struts2的标签库:
<%@ taglib uri="/struts-tags" prefix="s"%>
------------------------------------------------------
输入校验:
一.客户端校验: 过滤正常用户的误操作,通过JS代码完成(可以绕开).
二.服务端校验: 整个应用阻止非法数据的最后防线(必须),使用Java代码.
------------------------------------------------------------------
Struts2校验方式:
1.代码方式校验:开发中常用(在代码中编写校验规则);
2.配置校验:
2.1.基于XML配置校验(推荐使用)。
2.2.基于Annotation配置校验。
Action中
* 要继承ActionSupport(才能拥有输入校验功能)
* 重写Validateable接口中的validate()方法 ,在该方法中完成验证
* 步骤如下:
* validate()方法在其他的业务方法之前执行
* 验证出错转向的页面
struts.xml配置
其中input转向是在action中已经定义好的.
public static final String INPUT = "input";
* 什么时候表示验证出错(转向input所指向的页面)
* super.addFieldError("字段名称", "错误信息");
* 当集合不为空时,转向错误页面.
* 若校验失败,不会再执行Action中的业务方法.
问题1:为什么校验失败,默认跳转到input结果视图.
观察:DefaultWorkflowInterceptor的源代码,发现如上图.
问题2.为什么在执行input方法之前,没有执行validate方法.
在validation和workflow拦截中,设置了不被校验的默认方法.
在配置validation和workflow拦截器的时候,会忽略掉input方法.
问题3:Action中某些方法不需要校验,却也执行了validate方法,如何避免问题.
可以为不需要校验的方法上,使用@SkipValidation(跳过验证).
问题4.开发中在一个Action中,校验的方法是少数,大多数方法都不需要校验.
难道大多数方法都要使用@SkipValidation标签吗?
为了避免该问题,我们就应该对指定的方法做校验操作,在Action类中提供:
public void validateXxx(){}:Xxx表示被校验的方法.
比如:在Action中新增和更新的方法叫做:saveOrUpdate.
此时需要对saveOrUpdate方法做校验,那么校验方法:validateSaveOrUpdate.
问题5:从workflow的拦截器可以看出,校验失败会跳到默认的结果视图("input").
问题:是否可以修改默认的结果视图?----?校验失败跳到"edit"视图
@InputConfig(resultName="edit")
必须贴在被校验的方法之上,而不是贴在validate上.
问题6:错误信息提示丑陋..
Struts2的国际化:
准备国际化资源文件:基本名词:app
中文:app_zh_CN.properties
英文:app_en_US.properties
---------------------------------
告诉Struts2框架:国际化资源文件叫什么名字.
---------------------------------
准备国际化的login.jsp
在JSP中,获取指定的国际化信息:(先引入Struts2的标签库)
-----------------------------------
若要让Action支持国际化,那么Action必须继承ActionSupport或者ActionSupport的子类.
在Action中获取资源信息:
通过ActionSupport类中的:
String getText(String name,String[] args);
参数: name:表示资源文件中的key
args:资源文件中指定key占位符的真实值.
返回:拼接好的一个国际化信息.
文件上传:
Strtus2的文件上传操作:
文件上传准备:
注意:
1:表单必须使用POST方式提交;
2:使用二进制编码.multipart/form-data
3:
要注意处理文件上传的表单的Action中必须要有文件控件的name为前缀的setter和getter,比如此Action对应的处理文件是headImg,所以必须要有setHeadImgFileName,setHeadImgContent Type,才能对文件的文件名和文件的类型做接收.(约定优于配置)
Strtus2对上传组件的支持:
default.properties
------------------------
struts.multipart.parser=jakarta : 设置在Struts2中使用哪一种上传组件
struts.multipart.saveDir= : 设置临时文件的保存路径(不要设置),默认就在Tomcat下.
struts.multipart.maxSize=2097152 : 设置一次请求的最大值,单位字节. 默认是2M
---------------------------------------------------------------------------
Strtus2中支持文件上传时因为拦截器的原因:
此处要注意拦截器的覆盖问题:
在一个包中引用多个
若文件过大,则提示:
解决方案:把提示信息作出国际化.支持中文.
提示信息都在struts-messages.properties 文件里预定义. (org.apache.struts2包下)
在struts.xml中引用资源文件:
文件下载:
使用Strtus2标签的好处:
表单自动回显数据.
对页面进行布局和排版.
支持AJAX应用.
---------------------------
Struts2标签:
通用标签
控制标签/数据标签
UI标签
引用标签库:
<%@ taglib uri="/struts-tags" prefix="s"%>