Struts2头脑风暴(随笔,捋思路)
只是回想时的头脑风暴,具体配置需要查看官方文档,以下仅随意整理自己的思路,如有缺错请谅解
Struts2充当了MVC中View视图层,用来处理用户的请求及响应
1.引入:Servlet中用户请求对应于一个Servlet,是一对一的关系,而响应对应的页面视图也是一对一关系,这时可利用Map中key和value一对一的特性来设计视图层➡所有的请求都请求一个Filter(Filter也可进行请求的处理),而Filter内部封装Map,将请求与对应的控制部件对应,进行灵活的映射以达到需求
2.简要流程: 0)引入jar包:忽略具体(jar包部分重要的配置文件都在core核心包中的根目录中/首个文件中)
其中,重要配置文件 default.xml中定义了35种拦截器interceptor,默认执行的20种拦截器栈InterceptorStack,多种resultType返回视图类型
default.properties中定义了struts中的常量,但此文件内容不可修改,修改时优先在struts.xml中<content>标签修改
1)web.xml中注册struts2中启动项Filter
其中Filter的class为struts中的带Struts的..Executor..Filter
FilterMapping 中路径为/*
2)请求页面,访问Action
3)定义Action
Action中属性接收页面提交的参数
-
-
- 其中请求参数的接收方式有三种
-
属性驱动-->页面参数与Action中属性对应 页面参数name
域驱动-->将页面参数对应于dto(vo)中的数据传输对象,Action中属性为此域对象,此时页面中参数设置为域对象.属性名(student.name),访问时student.name...
ModelDriven-->ModelDriven是驱动器,Action需要实现ModelDriven接口,属性为Vo域属性,不需要set标签,提供getModel方法其中将域属性返回
源码分析:ModelDriven拦截器中将页面参数形成域对象赋给Action,
获取值栈中root数组,将getModel中获取的域对象放入root中
这时root中放置了域对象,root为数组形式,内部放置无名对象,访问时根据属性名直接访问
4)注册action
action使用package管理,<package name=" " package="必须以/开头" extends="struts-default">继承的包内部实现有拦截器,返回类型等,在struts.xml中定义
<action name=" " class=""(不定义class,会默认执行ActionSupport中内容,用于只用于访问action例如拦截器而内部没有实际意义的action就无需再定义action类了)
定义请求对应返回的视图:<result name=""> (其中,name="success"时可省略name值)
✳action的维度是action方法➡一个Action类多个action方法可对应多个action标签,需要定义method(可以不是executor方法,默认是)
5)定义相应页面
3)核心内容总结
1>获取ServletAPI
获取struts2包装后的ServletAPI: ActionContext.getContext().具体方法
原生ServletAPI: ServletActionServlet.getContext().方法
2>OGNL与值栈
ognl对象图导航语言,可轻松实现页面访问类中属性方法等功能,所以struts集成了ognl的jar包,也就是说ognl本来与struts无关,但要使用就必须产生"瓜葛"
struts2中定义了ognl上下文OGNLContext,其中包含了root根对象(ValueStack),session,request,attr,application,parameters等,除根对象外其他对象的访问需要使用#
- 源码分析:
引入值栈ValueStack,值栈用于传输在请求与Action之间的请求参数,在类中ValueStack是一个接口,OGNLValueStack是其唯一的实现类,其中包含了root属性,类型为CompRoot
而这种类型实现了ArrayList,但重写了其中的push,pop,peak方法,操作其中下标为0的数据,也就是当作了栈(后进先出)操作,还含有一个Map类型的属性,引用了ognl上下文
在ValueStack初始化的构造方法中调用setRoot,此时除初始化了root外还与堆中新建的OGNL对应的上下文产生了引用关系,此时OGNL就与struts2产生了"瓜葛"
- 值栈的获取很麻烦
值栈用于Action请求参数的传递,所以它的生命周期与Action的生命周期相同,而Action与请求相同,所以valueStack与request生命周期相同➡所以struts2设计者将ValueStack放置于Reauest的属性中
此时值栈的获取➡ServletActionContext.getContext().getRequest.GetAttribute("valueStack")通过请求获取
- 值栈的获取很容易
值栈中的context常用到,所以struts另外创立了ActionContext.getContext()获取的context就是valueStack中的context属性的引用
值栈也经常使用,所以就在ActionContext中也引用了ValueStack,所以此时值栈以及Context都很容易获得了
- Action操作对ActionContext中内容数据的存储
root中隐式 : 请求时将Action对象放入,Action中的属性放入
context中隐式 : parameters中存储请求中未进行类型转换时的参数
Action中存放action
<s:property value="name值">获取root中值➡首先在root中查找,找不到会再在context找
<s:property value="#request.name值>获取request中值(requestScope.getAttribute)➡首先在request中,没有则在root中,再在context中➡此时的request已不是原生request➡已被struts2包装
3>动态调用action方法
struts.xml中<action name="LoginAction_✳" method="{1}"
{1}为通配符,页面使用确切方法名代替*{1}可动态调用action方法
4>数据校验
页面请求Action,在Action中可对接收到的数据可进行数据校验
①手动代码实现
Action继承ActionSupport,(ActionSupport类实现了多个接口,有一个..Aware可以实现数据校验后跳转input视图)
重写invalid()方法,其中对action中属性值进行校验,如果不符合条件,则this.FieldError.add(key,value)
②xml配置
定位Action类----对哪个Action类中的某个属性验证则xml文件名为ActionClassName-validation.xml <文件名定位了具体什么什么文件时,就不需要再在配置文件中注册了>
定位哪个属性---<field name="属性名">
怎么验证--依赖于验证器,在default.xml中定义了16中验证器,此处引用,其中需要什么另外需求-->利用<param name="">值</param>设置
- 执行流程分析:
请求请求到对应的Action,执行action中的set方法将参数传递给Action的属性,接下来执行Action中的validat()方法,其中验证ValidationAware中有一个属性filedError的集合,如果集合中有元素,也就是集合size>=0,则有属性未通过数据校验,就会返回到input视图
5>类型转换
struts2中支持类型转换,Action中属性类型为什么,就会由页面传来的String类型转换为规定的类型,struts2内实现了多种类型的类型转换器TypeConversion,但日期类型只支持yyyy-MM-dd格式,所以也支持某些Action类中某些类型的属性用自定义的TypeConversion
- 实现步骤分析:
- 页面使用struts2标签定义表单页面
- 定义类型转换器
继承DefaultTypeConvert(不知道怎么定义-->追溯系统定义的类型转换器,看源码底层实现什么继承什么)
实现convertValue(Object value, Class toType)方法(其中value是将要转换的值,toType是要转换成的目标类型的class类型)
注意:方法中要实现双向转换-->
用于客户端提交数据后,服务端未通过转换,需要返回原input页面,此时为提高程序的友好,将之前不合格的数据从服务端已转换后的值返回到原页面原始类型值(数据回显)
数据回显(双向转换):根据toType类型判断方向
注意:SimpleDateFormat中.格式不正确抛出的异常是解析异常,而会发生跳转input的异常是类型转换异常,所以发生解析异常不会跳转,而会报错
解决办法-->在发生解析异常前手动thow 类型转换异常➡这就意味着再进行解析之前就需要格式验证,这时就用到了正则表达式➡Pattern.match(value,格式regx)判断格式
多格式转换:多种格式的日期字符串实现像日期类型转换
但➡一种SimpleDateFormat需要绑定一种格式形式
∴需要动态地根据正向转换输入格式进行动态创建SimpleDateFormat对象
但➡反向转换需要相同格式的sdf,但对应格式的sdf在正向if作用域中,此时获取不到➡数据的共享➡Session中共享
∴正向转换后将sdf保存到session中,反向转换从session中获取,一定有-->因为数据回显一定是已经正向转换过了的
-
- 注册类型转换器
局部:只规定指定的Action中类型
定位Action:文件名ActionClassName-coversion.properties
定位属性: 属性名=自定义类型转换器的全限定类名
全局:规定所有Action中类型
src下:文件名xwork-coversion.properties
定位属性: 类型类名=自定义类型转换器的全限定类名
-
- 注册input视图
对应action增加input视图 <result name="result">原input页面</result>
- 类型转换时页面跳转执行流程(源码分析)
- 请求Action并将参数进行类型转换,类型转换失败也就是发生类型转换异常后会生成默认值null,接下来通过set方法设置到Action中的属性值中,
接下来进行数据验证,null值一定不会通过验证,error集合中会增加元素,长度>=后就会返回到input视图
- 类型转换异常信息的修改
默认信息显示为英文-->提高友好性-->修改信息
对应Action包中创建 类名.properties文件
其中invalid.fieldvalue.变量名=提示信息
6>拦截器
struts2中大部分核心功能都由拦截器实现,系统创建了35种拦截器,默认执行的拦截器栈中拦截器有20种,在default.xml的struts-default包中定义了<interceptor-ref注册了
也可以自定义拦截器,手动注册,其中需要注意要将默认的拦截器栈引用
-
- 定义拦截器
跟踪源码看实现哪些接口,继承哪些类
继承..Interceptor
重写intercept(ActionInvocation invocation)方法-->ActionInvocation控制者拦截器的运行
eg:权限拦截器:访问登陆后才能访问的页面时通过此拦截器,判断session中是否有用户对象,有则放行invoation.invoke()没有返回拦截器对应于action中的result视图
-
- 注册拦截器
定义:package中,action前(查看每个标签中子标签个数位置的注释)
<interceptors>
<interceptor name="拦截器名" class="全限定类名">//定义拦截器
<interceptor-stack name="拦截器栈名" >//定义拦截器
<interceptor-ref name="拦截器名">
<interceptor-stack>
<interceptors>
注册引用:action中,result前
<interceptor-ref name="栈名/拦截器名(包含父包中)"/>
注意:不要落下默认引用的默认拦截器栈
可以上方定义栈,引用自己定义的和默认的,注册时只注册栈
注册被拦截后现实的视图-->根据拦截器中不通过返回的字符串定义result视图
7>文件上传
8>文件下载
9>国际化
10>防止表单重复提交
复习讲义: