struts2框架学习

2009.9

Action
  简单数据校验
Result
常用配置文件
OGNL&ValueStack
Tags
---------------------Project
  类型转换、上传与下载、interceptor、防止重复提交
MVC思想深入剖析
源码解读
其他话题
  校验框架、i18N、AJAX、FreeMarker、异常处理


前言
  不侧重讲简介
  不侧重讲区别
    Action
  重点讲
    操作
    happy的操作
    尊重学习规律的操作
      学习上痛苦的根源之一是只能走的时候逼我来跑
    一段时间可以,长了集中不了精力
    先讲应用,再讲理论,先学脉络,后学细节
    去掉让同学们学的不舒服的地方
  struts2是用webwork的技术,名字换成struts2


1、做hellowrld
war文件是可以用winrar直接打开的,解压struts2-blank.war
拷贝WEB-INF\classes\下.xml配置文件,struts.xml 到src目录下
拷贝jar包 到WEB-INF\lib目录下
拷贝web.xml 到WEB-INF目录下 看filter-class配的类
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter,NG叫next generation
<url-pattern>永远写/*


2、
struts.xml中
<action>标签定义了action name,里面的<result>标签定义了对应的jsp文件
浏览器使用http://localhost:8080/Struts2_0100_Introduction/hello.action访问,就可以访问jsp文件,点action可以省略
访问这个action,返回result里的jsp


3、
将<action>里的hello改为hello_struts
访问hello_struts会报错:
HTTP Status 404 - There is no Action mapped for namespace [/] and action name [hello_struts] associated with context path [/Struts2_0100_Introduction].

修改后不能立即反馈,修改常量:
<constant name="struts.devMode" value="true" />
开发模式打开

引入struts源码
引入javadoc
引入xml catalog的dtd文件

程序运行机制:
浏览器发送一个http请求 -> tomcat读取请求后知道给哪个web application处理 -> 读这个web application对应的web.xml ->
发现一个配置是一个filter,它过滤所有的url地址 -> 地址就会被org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter接收到 ->
请求的地址是Struts2_0100_Introduction/hello_struts2(namespace是斜杠) -> 参考struts.xml文件 -> 先查namespace "/" -> 在namespace "/"下查action叫hello_struts2 ->
如果有找里面对应的result是谁 -> 把hello.jsp的结果反馈给浏览器

请求来了后中间要经过一次中转,好处是将请求和展现分开,不直接写死,灵活、可扩展性

JUDE画图工具


4、
Action
Action入门-struts的作用
namespace
定义自己的Action
路径设置
调用Action的自定义方法
通配符
接收用户输入(3种方式)
乱码问题解决
简单数据校验
action中访问web元素
包含模块配置文件
默认action

struts.xml中:
1)package也是给action打包分类,不同package可以用同名的action
2)extends默认就这么写extends="struts-default"
3)namespace
namespace决定了action的访问路径,默认""(空),可以接收所有路径的action。
namespace必须以/开头,namesapce可以写为/,或者/xxx,或者/xxx/yyy,对应的action访问路径为/index.action、
/xxx/index.action、/xxx/yyy/index.action。
namespace最好也用模块来进行命名。
namespace为空的package,囊括了其它package处理不了的action。
4)result
凡是name叫success的result都可以不写名字,如果action不写名字默认就是success
注:
具体视图的返回可以由用户自己定义的Action来决定
具体的手段是根据返回的字符串找到对应的配置项,来决定视图的内容
返回的视图jsp文件要放在WebContent目录下,而不是WEB-INF下
5)class
<action name="front" class="com.bjsxt.test">
当访问这个action时,会找到这个class,执行里面的excute方法
执行过程加入:
当struts2的filter读struts.xml,发现这个action是一个class的时候 -> 找到这个class的对象 -> 调用它的execute方法 ->
获取返回值 -> 更具这个返回值("success")找到对应的jsp
注:
1)具体Action的实现可以是一个普通的java类,里面有public String execute方法即可
2)或者实现Action接口,重写execute方法
3)不过最常用的是从ActionSupport继承,重写execute方法,好处在于可以直接使用Struts2封装好的方法
4)如果xml中果Action不配class,默认执行的是ActionSupport类,ActionSupport源码在xworks框架内(用到了另外一个框架)
5)Struts2里面的Action中的class对象,当我们每次调用Action的时候,必定new一个对象


5、
struts2的路径问题
如果我们访问一个未配置的namespace -> 首先到web.xml -> 到对应的struts2的filter -> 然后在struts.xml找namespace -> 没有对应的namespace ->
返回到web.xml交给tomcat处理 -> tomcat访问welcome-file标签内的文件

链接路径问题
struts2中的路径问题是根据action路径,而不是jsp路径来确定,所以尽量不要使用相对路径。
虽然可以用redirect方式解决,但redirect方式并非必要。
解决办法非常简单,统一使用绝对路径。(在jsp中使用request.getContextRoot方法来拿到
webapp的路径)或者使用myeclipse经常用的,指定basePath

webapp名字+斜杠,对应的就是WebContent根路径


6、
Action执行的时候不一定要执行execute方法
可以在配置文件中配置Action的时候用method=来指定执行哪个方法
<action name="userAdd" class="com.bjsxt.struts2.action.UserAction" method="add">
也可以在url地址中动态指定(动态方法调用DMI)(推荐)
前者会产生太多的action,所以不推荐使用

<action name="userAdd" class="com.bjsxt.struts2.action.UserAction">
调用时:user/userAdd!add 动态调用user namespace中userAdd action中的add方法
需要设置开关:<constant name="struts.enable.DynamicMethodInvocation" value="true" />


7、
Action的通配符
使用通配符,将配置量降到最低
不过,一定要遵守“约定优于配置”的原则(约定好统一的格式)

<package name="actions" extends="struts-default" namespace="/actions">
  <action name="Student*" class="com.bjsxt.struts2.action.StudentAction" method="{1}">
    <result>/Student{1}_success.jsp</result>
  </action>

  <action name="*_*" class="com.bjsxt.struts2.action.{1}Action" method="{2}">
    <result>/{1}_{2}_success.jsp</result>
  </action>
</package>

星号匹配全部,{1}代表第一个星号(以此类推),如果调用的是add,那么星号代表add,{1}代表add
星号和{1}代表的内容根据我们往里面传的值来确定

如果有多个都能匹配,则匹配最最精确的哪个
<action name="Student_add">
<action name="*_*">
如果是Student_add匹配到第一个action


8、
Action如何接收参数,action里有三种接收参数的方法

1)第一种方式,使用action属性接收参数
user/user!add?name=a&age=8
在这里后面有参数,在自己的action的类里面定义这两个属性,写好get、set方法
当new这个action类的时候,他会自动的把这两个属性设置好

struts2是调用set方法去设置值,所以set方法名要和属性名相匹配

2)第二种方式,使用DomainModel(域模型)
域模型:就是在项目里头真正存在的实体的概念

定义一个模型类user
user/user!add?user.name=a&user.age=8
仍然是调了namespace为user,下的action为user所对应的class的add方法

在这个class里头,定义了User对象
private User user;
当然别忘了setUser、getUser

扩展知识:
vo = value object
do = data object
dto = data transfer object
这3个概念有一些区别,但现在不用区分的很清楚

当前端输入的参数并不是正好与域模型匹配在一起,也就是并不是全部都需要的时候

前端输入:用户名name + 密码password + 密码确认confirmingPassword
定义一个userDTO类,它可以有这三个属性
在action里面定义userDTO,前台输入的参数和它匹配,填充DTO对象
在action里处理完了后,用userDTO生成user对象(域模型)
所以DTO的作用就是接收并传递了参数

3)第三种方式,使用ModelDriven(不常用)
user/user!add?name=a&age=8
表面看和属性传递写法一样,但是在UserAction.java里面仍然写了private User user = new User(),没定义属性
是因为这个action实现了ModelDriven接口

隐含了非常重要的程序设计的思想
MVC
V:view 视图,处理完之后的结果,来动态定义对应的视图,就是jsp
M:model 模型,用来处理我们的请求,就是model
C:controler 控制器,谁来控制我用哪个模型来处理,谁来控制要到哪个视图去,就是action来控制。看action里域模型是谁,返回的是谁

MVC把请求的产生、请求的处理、请求的结果全给它分开了

由C来控制双方的通讯,然后把view和model的耦合度解开来

重写ModelDriven接口的getModel()方法

struts2在MVC中重点实现了C那部分(action)!!

小结:
接收参数有三种方式,用属性、用域模型也有可能用DTO、用ModelDriven,最常用的是用第二种


9、
在接收参数过程中,参数有中文
如果有中文,在jsp尽量用post,不要用get

i18n:internationalization 国际化
中文乱码问题添加:
<contant name="struts.i18n.encoding" value="GBK" />

struts的文档在源码包里,struts-2.3.24\docs\docs\index.html
struts的常量配置在struts-2.3.24\src\core\src\main\resources\org\apache\struts2下的default.properties文件中

可以用spring的filter来解决中文问题,在web.xml的struts的filter前先过滤一下


10、
简单数据校验
使用addFieldError方法和s:fieldError标签简单处理数据校验

分析小程序的过程:(顺着一条线读下来)
从页面开始观察它的提交或者链接到哪里去
然后到后台查它的处理是什么
然后再看它的处理是怎么处理的

在Action里如何往前台传递错误信息?
Action没有和request、responce属性绑在一起的,也没有ServletContext
在struts2里面使用this.addFiledError添加对于属性校验的错误信息
this.addFieldError("name", "name is error");

前台页面使用struts标签
<s:fielderror fieldName="name" theme="simple"/>

struts的标签库在struts-tags.tld中
引入标签库:<%@taglib uri="/struts-tags" prefix="s" %>

s:fielderror自带格式,所以不常使用,要拿出返回的错误字符串,使用<s:debug></s:debug>标签
debug标签可以显示出值栈,包含这个action的属性和各种error信息
使用标签s:property专门取value stack和下面的stack context(其实就是ActionContext)里面的属性
<s:property value="errors.name[0]"/>

errors.name[0]这个表达式就是OGNL表达式!

小结:调用addFiledError,struts2就会在值栈里把你的message放进去


11、
访问web元素
一般需要访问request,session,application这些web元素

取Map类型的request,session,application,真实类型的HttpServletRequest,HttpSession,ServletContext的引用:

1)Map类型-依赖于容器
private Map request; //页面上是HttpRequese
private Map session; //页面上是HttpSession
private Map application; //页面上是ServletContext

request = (Map)ActionContext.getContext().get("request");
session = ActionContext.getContext().getSession();
application = ActionContext.getContext().getApplication();

request.put("r1", "r1");
session.put("s1", "s1");
application.put("a1", "a1");

context叫上下文,它就是一个当前程序执行的环境,它会把周围的环境放到这样一个对象里
你想访问周围环境内容的时候就通过它去取
context包含了程序执行的所有条件的集合(配置,参数,输入。。。)只有满足了这些条件程序才能执行起来

在前台页面,可以用OGNL表达式取,也可以用jsp页面上的取法
struts2会挨着排的把map里的值复制到HttpRequest对象上去
<s:property value="#request.r1"/> | <%=request.getAttribute("r1") %>
<s:property value="#session.s1"/> | <%=session.getAttribute("s1") %>
<s:property value="#application.a1"/> | <%=application.getAttribute("a1") %>
<s:property value="#attr.a1"/>
<s:property value="#attr.s1"/>
<s:property value="#attr.r1"/>

#attr会帮你搜request,session,application,搜到第一个就拿出来(很少用)
原因是自己应该精确的知道,你把你想要的值放哪儿了

第一种取法request,session,application依赖于容器(这里是struts2的ActionContext里面放的值)

2)Map类型-IoC
第二种方式实现了接口:RequestAware,SessionAware,ApplicationAware
重写setRequest、setSession、setApplication方法

@Override
public void setRequest (Map<String, Object) request) {
  this.request = request;
}

aware表示得知、知道的意思,实现了RequestAware接口应该知道request的存在

在action里是如何获得request的?
肯定是有人调了setRequest方法,我们才能获得request
其中蕴含着非常重要的设计思想:IoC,也称为DI
//DI dependency injection(依赖注入)
//IoC inverse of control(控制反转)

IoC说明:
1> struts2 new了一个LoginAction
2> struts2问LoginAction你实现了RequestAware接口了吗
3> LoginAction回答说我实现了
4> struts2就会调用这个接口的setRequest方法,把request交给这个action
5> strues2帮你初始化了request
6> 依赖注入就是action的成员变量request依赖于struts2这个容器,
依赖于外界的环境,注入这个值给我,而不是自己主动的去拿
7> 控制反转就是,原来是自己控制的,现在给其他人控制了,控制就反转了

PS:在spring里面定义成员变量,没事别自己初始化,全都交给spring去初始化,
spring初始化的时候用的是配置文件,你想把它初始化成什么就在配置里面写什么
PS2:spring另一特点AOP就是面向切面编程,将像log, transaction, statistical
这种本来分散在各个逻辑模块中的辅助代码抽象成单独的模块;然后将其注入(植入)到业务逻辑的代码中。

IoC方式是最常用的!!!

3)原始类型-依赖于容器
private HttpServletRequest request; //请求
private HttpSession session; //会话
private ServletContext application; //应用程序

request = ServletActionContext.getRequest();
session = request.getSession();
application = session.getServletContext();

4)原始类型-IoC
成员变量
private HttpServletRequest request; //请求
private HttpSession session; //会话
private ServletContext application; //应用程序

实现ServletRequestAware接口,重写setServletRequest方法
this.request = request;
this.session = request.getSession();
this.application = session.getServletContext();


小结:
前台页面取法
第一种方式是使用HttpRequest对象取
第二种方式是使用标签(NGOL表达式)取


12、
模块包含
struts.xml中有一个配置叫做include
<include file="login.xml">
作用是把另一个xml当成struts.xml包含在这里


13、
默认Action

定义个package:
<package name="default" namespace="/" extends="struts-default">
<default-action-ref name="index"></default-action-ref>
<action name="index">
  <result>/default.jsp</result>
</action>
</package>

default-action-ref表示,在这个namespace里,如果找不到它对应的action
默认的就用它


14、
Action总结
1)实现一个Action的最常用方式,从ActionSupport继承
2)DMI动态方法调用
3)通配符配置 * {1} {2}
4)接收参数的方法(一般用属性或者DomainModel来接收)
5)简单参数验证addFieldError
  一般不使用struts2的UI标签
6)访问web元素
  a)Map类型
    i. IoC
    ii. 依赖struts2
  b)原始类型
    i. IoC
    ii. 依赖struts2
7)包含文件配置
8)默认action处理

从ActionSupport继承
按照约定写好自己的各种方法:add、delete
用DomainModel接收参数
如果需要访问session用IoC访问


15、result的配置
result的类型
dispatcher
redirect
chain
redirectAction
freemarker
httpheader
stream
velocity
xslt
plaintext
tiles

1)
<action name="r1">
  <result type="dispatcher">/r1.jsp</result>
</action>
不配type默认是dispatcher
dispatcher的意思是运用服务器跳转,也就是forward到一个jsp页面

2)
<action name="r2">
  <result type="redirect">/r2.jsp</result>
</action>
客户端跳转重定向,这时浏览器地址栏里面显示的是action的地址还是jsp的地址呢?

什么是服务器端跳转?
1> 客户端发起请求访问服务器的一个url地址
2> 服务器端跳转forward,相当于客户端根本不知到跳到另一个页面去了
3> 最后是另一个页面返回给客户端

什么是客户端跳转?
1> 客户端发起请求访问服务器的一个url地址
2> url地址说redirect到另一个地址
3> 反馈给浏览器
4> 浏览器重新发起一个新的请求到另外一个地方

3)
<action name="r3">
  <result type="chain">r1</result>
</action>
chain的意思是服务器forward到一个action,dispatcher只能跳转到页面

4)
<action name="r4">
  <result type="redirectAction">r2</result>
</action>
redirectAction表示客户端跳转到一个action

Redirect和Dispatcher区别
使用Dispatcher(forward)是服务跳转,浏览器不知道它所请求的具体资源来源,浏览器的地址栏不会变;
使用redirect,服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址。所以地址栏显示的是新的URL。

forward,前后两个页面使用同一个request作用域
redirect,使用一个新的request作用域


16、
Global Result 全局结果集
1)
UserAction.java:
public String execute() throws Exception {
  if (type == 1) return "success";
  else if (type == 2) return "error";
  else return "mainpage";
}

struts.xml:
<package name="user" namespace="/user" extends="struts-default">
<globel-results>
  <result name="mainpage">/main.jsp</result>
</globel-results>

<action name="user" class="com.bjsxt.struts2.user.action.UserAction>
  <result>/user_success.jsp</result>
  <result name="error">/user_error.jsp</result>
</action>
</package>

传的值是1,页面返回User Success!
传的值是2,页面返回User Error!
传的值是3,页面返回MainPage
在对应的UserAction的配置里面,并没有一个result叫做mainpage

如果有多个action有共同的result,都配在每一个action里面,不如写在公用的一个里面

2)
如果有另外一个package想用这个package里面的结果集
<package name="admin" namespace="/admin" extends="user">
  <action name="admin" class="com.bjsxt.struts2.user.action.AdminAction>
    <result>/admin.jsp</result>
  </action>
</package>

加上extends="user",extends作用是从另一个包继承,相当于user包的配置admin包全有了

3) struts-default是什么
上面的例子,admin继承了user,user继承了struts-default,所以admin也继承了struts-default
struts-default在Lib里的struts2-core包里的struts-default.xml中

struts-default.xml里面定义了一大堆bean,包,result-type,一大堆拦截器,一大堆拦截器栈

拦截器的原理,跟servlet请求通过filter的原理一模一样


17、
动态结果集(dynamic result)
<package name="user" namespace="/user" extends="struts-default">
  <action name="user" class="com.bjsxt.strtus2.user.action.UserAction">
    <result>${r}</result>
  </action>
</package>

UserAction.java:
public String execute() throws Exception {
  if (type == 1)
    r="/user_success.jsp";
  else if (type == 2)
    r="/user_error.jsp";
  return "success";
}

也就说说这个r值是动态确定的,$用来在配置文件里往值栈里头取值

小结:
1> 我们可以用一个属性来保存结果
2> 这个属性里面的结果可以由我们动态确定
3> 在struts.xml里面用${r}把它的值取出来
4> 一定不要忘了为动态结果的保存值设置set get方法


18、
带参数的结果集,向结果传参数
<package name="user" namespace="/user" extends="struts-default">
  <action name="user" class="com.bjsxt.struts2.action.UserAction">
    <result type="redirect">/user_success.jsp?t=${type}</result>
  </action>
</package>

把前台传给action的参数,再传给result的结果页面

前台把这个type的值传给了action,action把type值传给了user_success.jsp页面

小结:
1> 一次request只有一个值栈
2> 一个action forward到另一个action不需要传参数
3> 因为凡是forward过程,这几个action共享同一个值栈
4> 服务器端的forward对客户端来说只有一次request
5> jsp页面的OGNL表达式value="t"只能从值栈里取值,没有action就没有值栈
   from valuestack: <s:property value="t"/> --取不出来
   from actioncontext: <s:property value="parameters.t"/> --可以取出来
6> debug标签打开的值,上面是值栈,下面是actioncontext,值栈必须要有action才会有


19、
Result总结
1)常用四种类型:
  dispatcher(默认)
  redirect
  chain
  redirectAction
2)全局结果集
  global-results | extends
3)动态结果(了解)
  在action中保存一个属性,存储具体你想跑去那个视图的location
4)传递参数
  客户端跳转才需要传递
  ${}表达式(不是EL)


20、
OGNL表达式
计算机的书是用来查的

朴素的编程的原理
写项目,先做原型在一点点往上包
瀑布式模型,先定义好所有的细节,直接开发最后的结果,开发了200多天开发完了,需求变了
迭代式模型,先做原型,在往上迭代,能够应对需求改变
要迭代式开发

<ol>
  //值栈里的值可以直接访问
  //value里面是OGNL表达式,前面是标签
  <li>访问值栈中的action的普通属性:username = <s:property value="username"/></li>
  //user.xxx只有传,才会构造
  //想初始化domain model,可以自己new,也可以url传参数值(struts2帮你new,但这时候需要保持参数为空的构造方法)
  <li>访问值栈中对象的普通属性(get set方法):<s:property value="user.age"/></li>
  <li>访问值栈中对象的普通属性(get set方法):<s:property value="cat.friend.name"/></li>
  <li>访问值栈中对象的普通方法:<s:property value="password.length()"/></li>
  <li>访问值栈中对象的普通方法:<s:property value="cat.miaomiao()"/></li>
  <li>访问值栈中action的普通方法:<s:property value="m()"/></li>
  <hr />
  //访问静态方法如何访问
  //前面@写类名,后面@写静态方法名
  //要访问静态方法struts.xml需配置
  //<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
  //这些静态值从Lib里的struts2-core包里的default.properties中去查
  <li>访问静态方法:<s:property value="@com.bjsxt.struts2.ognl.S@s()"/></li>
  <li>访问静态属性:<s:property value="@com.bjsxt.struts2.ognl.S@STR"/></li>
  <li>访问Math类的静态方法:<s:property value="@@max(2, 3)"/></li>
  <hr />
  <li>访问普通类的构造方法:<s:property value="new com.bjsxt.struts2.ognl.User(8)"/></li>
  <hr />
  //访问集合
  //数组和List访问方式一模一样
  <li>访问List:<s:property value="users"/></li>
  //大括号在OGNL里面可以代表一个集合
  <li>访问List中某个元素:<s:property value="users[1]"/></li>
  <li>访问List中元素某个属性的集合:<s:property value="users.{age}"/></li>
  //推荐后面那种写法,更直观
  <li>访问List中元素某个属性的集合中的特定值:<s:property value="users.{age}[0]"/> | <s:property value="users[0].age"/></li>
  <li>访问Set:<s:property value="dogs"/></li>
  //Set是没有顺序的,所以取不到
  <li>访问Set中某个元素:<s:property value="dogs[1]"/></li>
  <li>访问Map:<s:property value="dogMap"/></li>
  //推荐第一种写法
  <li>访问Map中某个元素:<s:property value="dogMap.dog101"/> | <s:property value="dogMap['dog101']"/> | <s:property value="dogMap[\"dog101\"]"/></li>
  <li>访问Map中所有的key:<s:property value="dogMap.keys"/></li>
  <li>访问Map中所有的value:<s:property value="dogMap.values"/></li>
  <li>访问容器的大小:<s:property value="dogMap.size()"/></li>
  <hr />
  //投影其实就是过滤器的意思
  //尖括号里有三种写法?#,^#,$#
  //小尖号表示开头,$代表结尾,?代表过滤条件
  //#this会挨着排的从users里取出元素判断
  <li>投影(过滤):<s:property value="users.{?#this.age==1}.{age}"/></li>
  <li>投影:<s:property value="users.{^#this.age>1}.{age}"/></li>
  <li>投影:<s:property value="users.{$#this.age>1}.{age}"/></li>
  <li>投影:<s:property value="users.{$#this.age>1}.{age} == null"/></li>
  <hr />
  //中括号访问的就是OGNL这个栈里面从上往下的第几个元素
  //他会从第0个集合开始一直输出到栈底
  <li>[]:<s:property value="[0]"/></li>

</ol>

什么情况下栈里有可能有两个action在栈里?
如果用到服务器端跳转,它会把用到的action挨着排往里压,这时就会有多个action了


21、
struts标签
1)通用标签
  a)property
  b)set
    默认为action scope,会将值放入request和ActionContext中
    page、request、session、application
  c)bean
  d)include(对中文文件支持有问题,不建议使用,如需包含,改用jsp包含)
  e)param
  f)debug
2)控制标签
  a)if elseif else
  b)iterator
    collections map enumeration iterator array
  c)subset
3)UI标签
  a)theme
    simple shtml(默认) css_shtml ajax
4)AJAX标签
  用ajax框架代替
5)$#%的区别
  a)$用于i18n和struts配置文件
  b)#取得ActionContext的值
  c)%将原本的文本属性解析为ognl,对于本来就是ognl的属性不起作用
    参考<s:property和<s:include
6)看到var,它就会放到ActionContext里头,用#号取出来


22、
浏览器访问:http://xxxxx/tags.action?username=u&password=p

property标签
用于显示变量值的标签

<ol>
  <li>property:<s:property value="username"/></li>
  <li>property取值为字符串:<s:property value="'username'"/></li>
  <li>property设定默认值:<s:property value="admin" default="管理员"/></li>
  <li>property设定HTML:<s:property value="'<hr/>'" escape="false"/></li>
  <hr/>
struts2里面标签的属性有的是字符串有的是OGNL表达式


22-2、
set标签
  <li>set 设定adminName值(默认为request和ActionContext):<s:set var="adminName" value="username" /></li>
  定义了一个变量叫做adminName,它的value是username,当没有设scope范围的时候,默认范围是request和ActionContext
  它会把这个变量既放到request里面又放到ActionContext里面
  <li>set 从request取值:<s:property value="#request.adminName" /></li>
  <li>set 从ActionContext取值:<s:property value="#adminName" /></li>
  <li>set 设定范围:<s:set name="adminPassword" value="password" scope="page" /></li>
  <li>set 从相应范围取值:<%=pageContext.getAttribute("adminPassword") %></li>
  <li>set 设定var,范围为ActionContext:<s:set var="adminPassword" value="password" /></li>
  <li>set 使用#取值:<s:property value="#adminPassword" /></li>
  <hr />

scope的范围有五个application、session、request、page或action,默认范围是action,也就是说在
request里面放一个,在ActionContext里面放一个

用了var,默认放在ActionContext里面,如果后面指定了scope范围,就不会放在ActionContext里面
假设adminPassword指定了scope为session,要取值的话
  <li>set 从相应范围取值:<s:property value="#session.adminPassword" /></li>

set标签id和name都已经废弃了,以后用scope、value、var


22-3、
bean标签:把类的对象生成出来,string类型
  <hr />
  <li>bean 定义bean,并使用param来设定新的属性值:
    <s:bean name="com.bjsxt.struts2.tags.Dog" >
      <s:param name="name" value="'dagou'"></s:param>
    </s:bean>
  </li>
  <li>bean 查看debug情况:
    <s:bean name="com.bjsxt.struts2.tags.Dog" var="myDog">
    </s:bean>
  </li>
  <hr />
定义了var变量名,就能在ActionContext里面看到这个对象
定义name是哪个类的对象


22-4、
include标签

jsp包含文件有两种:
<%@ include file%>  静态包含
<jsp:include>  动态包含

  <li>include _include1.html 包含静态英文文件
  <s:include value="/_include1.html"></s:include>
  </li>
  <li>include _include2.html 包含静态中文文件
  <s:include value="/_include2.html></s:include>
  </li>
  <li>include _include1.html 包含静态英文文件,说明%用法
  <s:set var="incPage" value="'/_include1.html'" />
  <s:include value="%{incPage}"></s:include>
  </li>
  <hr />


22-5、
控制标签
在http里面所有传递的参数都是字符串类型
  <li>if elseif else:
  age = <s:property value="#parameters.age[0]" /><br />
  <s:if test="#parameters.age[0] < 0">wrong age!</s:if>
  <s:elseif test="#parameters.age[0] < 20">too young!</s:elseif>
  <s:else>yeah!</s:else><br />

  <s:if test="#parameters.aaa == null">null</s:if>
  </li>
  <hr />


22-6、
iterator标签
  <li>遍历集合:<br />
  <s:iterator value="{1, 2, 3}" >
    <s:property/> |
  </s:iterator>
  </li>
定义一个集合,然后s:property会挨着排的把它取出来

  <li>自定义变量:<br />
  <s:iterator value="{'aaa', 'bbb', 'ccc'}" var="x">
    <s:property value="#x.toUpperCase()"/> |
  </s:iterator>
  </li>

  <li>使用status:<br />
  <s:iterator value="{'aaa', 'bbb', 'ccc'}" status="status">
    <s:property/>
    遍历过的元素总数:<s:property value="#status.count"/> |
    遍历过的元素索引:<s:property value="status.index"/> |
    当前是偶数?:<s:property value="#status.even"/> |
    当前是奇数?:<s:property value="#status.odd"/> |
    是第一个元素吗?:<s:property value="#status.first"/> |
    是最后一个元素吗?:<s:property value="#status.last"/><br />
    </s:iterator>
  </li>
status属性,记录着每次循环到现在的状态


22-7、
UI标签
UI标签中的theme的概念:theme翻译成中文叫主题

在struts.xml中控制theme,默认为xhtml,可以设置为:simple/css_xhtml/ajax
很像java里面的抽象工厂,换皮肤


23-1、
struts2支持声明式异常处理
平时方法里遇到异常,不交给前台去处理,就是在这个类这个方法里catch后打印一下堆栈就好了
起码调用这个方法的其他方法是不知道有这个异常存在的

声明式的异常处理是指:你要是有异常了尽管往外抛,到时候我会给你一个统一的接口,然后让你在特定的页面做出处理

在方法里catch到异常后,继续往外抛:
catch (SQLException e) {
    e.printStackTrace();
    throw(e);
}

这也是为什么在ActionSupport里面我们经常重写的execute()方法,它默认也往外抛任何异常的原因:
public String execute() throws Exception {
    return SUCCESS;
}
抛完了后由struts2来处理。

在sturts.xml的action里加了两句话:
<exception-mapping result="error" exception="java.sql.SQLException" />
<result name="error">/error.jsp</result>

exception-mapping这句话是说异常的映射,一旦发生java.sql.SQLException这个异常的时候,result等于error
整个过程是:在方法里发生异常往外抛 -> Action接住,接住之后它还往外抛 -> struts接住,它发现有配置里有和这个异常匹配的result -> 找到result显示异常的界面

你可以为每一个action出的任何一个异常配一个页面,太麻烦了

写一个包,每一个action从这个包继承
<package name="bbs2009_default" extends="struts-default">
  <global-results>
    <result name="error">/admin/error.jsp</result>
  </global-results>

  <global-exception-mappings>
    <exception-mapping result="error" exception="java.sql.SQLException"></exception-mapping>
  </global-exception-mappings>
</package>
任何action出现这个异常,都跑到error里面,写在global-results里面


23-2、
声明式异常处理--原理
struts2Filter调拦截器的一个方法,拦截器帮你去调那个Action,调完之后返回到拦截器继续执行,拦截器执行完了之后再返回到struts2Filter
中间加了个东西,把调用过程分为了两段。并且通过设置,这个过程对struts2Filter是透明的,感觉不到interecptor的存在

当我们调某一个类的方法的时候,我在中间加一些自己的业务处理逻辑,这个过程完全可以做到透明化的,关键是你编程上的技巧

实际上这个Filter和interceptor实现是一模一样的,因为 这个Filter也是在一个HttpRequest请求的过程之中加了一道过滤器


23-3、
1)i18n原理
  a)ResourceBundle和Locale的概念
  b)资源文件
  c)native2ascii
2)struts的资源文件
  a)Action - Package - App级
  b)PropertiesEditor插件
  c)动态语言切换

i18n这一章略过。。。。。。


24、
拦截器

读代码原则:
1)这代码必须能运行
2)顺着一条线读进去
3)读大体的脉络
4)设置断点,然后一步一步单步往下走

struts2的request请求过程
1)struts2Filter调用Dispatcher的serviceAction方法
2)创建context,ValueStack
  把ValueStack设到request范围里
3)创建ActionProxy对象
4)把配置文件的interceptor加载到对象中
5)在ActionProxy里面调用了invocation.invoke()方法
6)在这个invoke()方法执行过程中,进行一系列的interceptor的执行过程
调用当前interceptor的intercept()方法
7)又调用了action的invocation的invoke()方法
在一个action里面把所有拦截器都过一遍
8)intercept()执行完了,调用invokeActionOnly()方法
9)最后调用真实action


 拦截器原理模拟

代码: http://pan.baidu.com/s/1hsezmFa


26、
自定义拦截器
自定义拦截器99%是用不上的,不是重点
明白原理,拿来用
代码: http://pan.baidu.com/s/1miGrjWs

关注项目、分析、设计、应用,更多的放在业务上。技术方面要理解背后的原理
做久了,除了业务之外其他的全是copy过来

自己定义一个类,实现Interceptor接口就能定义拦截器

在package里定义interceptor:
<interceptors>
  <interceptor name="my" class="com.bjsxt.interceptor.MyInterceptor"></interceptor>
</interceptors>

在action里调用这个拦截器:
<interceptor-ref name="my"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>

注意不能只写my这个interceptor,因为定义了interceptor-ref标签,它会自动覆盖原来的标签

拦截器体现了一种编程的理念,叫做 AOP(面向切面编程)
什么是面向切面,本来程序正常是一条线执行下去,现在切了一刀在上面,而且不想切了我可以把它抽回来


27、
interceptor的应用:使用token拦截器控制重复提交

token拦截器是防止,页面上有个表单,用户提交后执行慢,又点刷新,提交了很多次
用post提交页面会有提示是否重新发送,get方式提交不会有提示

1)第一种解决重复提交的方式,设置成post
2)token表示令牌,你要想提交,我先给你发个令牌,你令牌和我这里能对上就提交
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
3)页面增加
<s:token></s:token>
原理是,在打开页面时服务器端session里生成一串随机数
把这个随机数写到form里,返回给浏览器
客户端提交时检查服务器端有没有这串数字
一旦提交完成之后就会把这串数字清掉
4)在提交到的那个action里配置
<action name="user" class="com.bjsxt.action.UserAction">
  <result>/addOK.jsp</result>

  <interceptor-ref name="defaultStack"></interceptor-ref>
  <interceptor-ref name="token"></interceptor-ref>
  <result name="invalid.token">/error.jsp</result>
</action>
它会帮我拦截,看看session里面有没有


28、
servletConfig拦截器
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>

这个拦截器的作用是:
你只要实现了
 * @see ServletContextAware
 * @see ServletRequestAware
 * @see ServletResponseAware
 * @see ParameterAware
 * @see SessionAware
 * @see ApplicationAware
 * @see PrincipalAware
这些接口,它就会帮你注入对象

如果你实现了ServletRequestAware的接口,它就从context里把request拿出来,放到action里


29、
类型转换
类型转换也是struts2拦截器来实现的

把字符串转成特定的类型,把特定的类型转成字符串

关键是字符串转其他类型,比如string转date

一般处理日期,使用java.text包的:
SimpleDateFormat类来处理

用set、list、数组处理传多个值:
/test?interests=math&interests=english
展示:[math, english]

往map里传值:
/test?users['a']=usera&users['b']=userb
展示:[a=usera, b=userb]


30、
自己写转换器
从默认实现DefaultTypeConverter继承,实际上它也是实现了TypeConverter接口
package com.bjsxt.converter;

import java.awt.Point;
import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;

public class MyPointConverter extends DefaultTypeConverter{

    public Object convertVlaue(Object value, Class toType) {
        if(toType == Point.class) {
            Point p = new Point();
            String[] strs = (String[])value;
            String[] xy = strs[0].split(",");
            p.x = Integer.parseInt(xy[0]);
            p.y = Integer.parseInt(xy[1]);
            return p;
        }
        if (toType == String.class) {
            return value.toString();
        }
        return super.convertValue(value, toType);
    }
}
value就是你要把哪个值做转换,toType就是你要转换成什么类型,返回值是转换成这个类型的结果

当写完转换器之后,必须把转换器注册到struts2里面

三种注册方式:
1)局部:XXXAction-conversion.properties
注册给Action
p=com.bjsxt.converter

2)全局:xwork-conversion.properties
一般用全局的注册方式

3)Annotation
利用注解


31、
StrutsTypeConverter类继承了DefaultTypeConverter
写转换器也可以从这个抽象类继承,重写convertFromString方法(最常用)


32、
interceptor总结

struts拦截器以及源码解析
1)struts架构图
  见文档
2)struts执行过程分析
3)interceptor拦截器过程模拟
4)定义自己的拦截器
  a)acegi - spring security 权限管理的框架
5)使用token拦截器控制重复提交(很少用)
6)日期处理
7)类型转换
  a)写自己的转换器
  b) 三种注册方式
      局部:XXXAction-conversion.properties
      写p (属性名称)= converter
      全局:xwork-conversion.properties
      写com.xxx.xxx(类名)= converter
      Annotation
  c)如果遇到非常麻烦的映射转换
    改用request.setAttribute();
    改用session

补充视频中讲解的内容
1)Lambda表达式
2)验证框架
3)UI标签
4)类型转换中的异常处理
5)上传与下载文件
6)struts2注解

33、
struts2总结
1)Action
  a)namespace(掌握)
  b)path(掌握)
  c)DMI(掌握) 感叹号加方法名
  d)wildcard(掌握) 通配符的配置
  e)接收参数(掌握前两种) 使用Action属性,使用Domain Model
  f)访问request等(掌握Map IOC方式) 实现aware接口
  g)简单数据验证(掌握addFiledError和<s:filedError)
2)Result
  a)结果类型(掌握四种,重点两种) redirect、dispatcher
  b)全局结果(掌握)
  c)动态结果(了解)
3)OGNL表达式(精通)
  a)# % $
4)Struts标签
  a)掌握常用的
5)声明式异常处理(了解)
6)i18N(了解)
7)CRUD的过程(最重要是设计与规划)(精通)
8)interceptor的原理(掌握)
9)类型转换(掌握默认,了解自定义)
10)
struts2集成的东西很多,但真正用的时候不一定用它的
比如说ajax来说,不一定用struts2的ajax,你可以用各种框架jquery、dwr、extjs


34、 其他
一个人应该是在合适的时间做合适的事,而不是在当前的时间做以后的事
先学会走了再学跑

为什么找不到Ms right?
what is right,什么是right,什么是适合,这东西不像计算机一样对就对、错就错
现在看是Ms right,过了好长好长的时间50年后,想是不是另外一个是Ms right。
请大家enjoy right now!

把你宝贵的精力花在不相关的事情上,也是一种浪费
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值