1, 当Action设置了某个属性后,Struts将这些属性封装一个叫做Struts.valueStack的属性里。获取valueStack对象: ValueStack vs = (ValueStack) request.getAttribute("struts.valueStack");
调用ValueStack 的vs.findValue("books")方法(books为Action中的属性);
2, struts2的Action类是一个普通的POJO类(通常包含一个无参的execute方法)从而很 好的重用代码。
4,struts2通常直接使用Action来封装HTTP请求参数,所以Action中要定义与请求参数对应的属性,并且为该属性提供相应的 getter和setter方法。
5,即使Action需要处理的请求name和pass两个HTTP请求参数,Action类也可以不包含name和pass属性,因为系统是通过对应的 getter和setter方法来处理请求参数。而不是通过属性来处理请求参数的。所以Action类是否包含name和pass属性不重要, 重要的是需要setter和getter方法。
6,Action类的属性,不仅可以封装请求参数,还可以用于封装处理结果,通过Struts2标签来输出对应的属性值。如 <s:Property value="tip">。
7,ActionContext类。Struts2的Action并未直接与任何Servlet api耦合,从而更加方便测试该Action(可以通过脱离web容器测试Action)。但是对于Web应用的控制器来说,不访问Servlet api 几乎是不能的。例如跟踪HTTP session状态等。访问的Servlet Api 就 是HttpServeltRequest、HttpSession、ServletContext,这三个类分别jsp内置对象中的request、session、application。Web应用中提供了一个ActionContext类,Struts2的Action可以通过该类来访问Servlet api。
该类的ActionContext提供getContext方法得到ActionContext实例,
该类的方法:
①Object get(Object key),该方法类似于HttpServletRequest的getAttribute(String name)方法;
②Map getApplication(),返回Map对象,该对象模拟了应用的ServletContext的实例
③static ActionContext getContext(),静态方法,获得系统提供的ActionContext实例。
④Map getParameters()获取所有的请求参数,类似于调用HttpServletRequest对象的getParammeterMap方法。
⑤Map getSession()该Map对象模拟了HttpSession实例。
⑥void setApplication(Map application)直接传入一个Map实例,将该Map实例的key-value对转换成application的属性名、属性值。类似的还有setSession(Map session)
⑦put(Object key, Object value)直接为HttpServletRequest设置属性。相当于request.setAttribute(key,value).可通过EL表达式输出。
8,ServletActionContext类(是ActionContext类的子类)。 虽然Struts2提供了ActionContext来访问Servlet Api,但是这种访问毕竟不能直接获得Servlet Api实例,为了Action中 直接访问Servlet api,Struts2提供了如下的接口:ServletContextAware、ServletRequestAware、ServletResponseAware 。如果Action实现这些接口,能分别直接访问用户请求的ServletContext、HttpServletRequest、HttpServletResponse实例。
并且,为了直接访问Servlet api。Struts2提供了一个ServletAction类。通过ServletActionContext类可以更加方便的地直接访问Servlet api。这类的主要方法(全是static):
①getActionContext(HttpServletRequest req)得到当前的ActionContext实例。
②getActionMapping()得到ActionMapping实例(得到 action mapping为context)。
③getRequest()得到HttpServletRequest实例(Gets the HTTP servlet request object)。
④getResponse()得到HttpServletResponse实例(Gets the HTTP servlet response object.)
⑤getServletContext()得到ServletAction实例(Gets the servlet context.)
⑥getValueStack(HttpServletRequest req)得到ValueStack实例。
⑦setRequest(HttpServletRequest request)(Sets the HTTP servlet request object)相应的有setResponse(HttpServletResponse response)、setServletContext(ServletContext servletContext)。
9,虽然可以在Action类获取HttpServletResponse,但如果希望通过HttpServletResponse来生成服务器响应是不可能的,因为Action只是控制器(它并不直接对浏览器生成任何相应)。即如果在Action中写如下代码:response.getWriter().println("hello world");
是没有意义的。
10,对于使用Struts2框架的应用而言,尽量不要让超级链接链接到某个视图资源,
因为这种方式增加了额外的风险,推荐将所有请求都发给Struts2框架,让该框架来处理用户的请求,
即使是简单的超级链接。
11,逻辑视图名是指:Action返回的字符串;物理视图是指:页面的实际名称。
Struts2通过配置逻辑视图名和物理视图之间的映射关系,一旦系统收到Action返回的某个逻辑视图,系统就会把相应的物理视图呈现给用户。
12,默认值:如果配置<result../>元素时没有指定location参数,系统将会把<result..>...<result../>中间的字符串当成实际视图资源。如果没有配置parse参数,则默认值为true(该参数指定实际视图名是否可以使用OGNL表达式);如果没有指定name属性则默认值为Struts2的默认处理结果类型(dispacher).
13,归纳起来,Struts2内建支持结果类型如下(14):
①chain结果类型:Action链式处理结果类型。
②chart结果类型:用于整合JFreeChart的结果类型。
③dispatch结果类型:用于jsp整合的结果类型。
④freemarker结果类型:FreeaMarker整合的结果类型。
⑤httpheader结果类型:用于控制Http行为的结果类型。
⑥jasper结果类型:用于JasperReports整合的结果类型。
⑦jsf结果类型:用于整合JSF整合的结果类型。
⑧redirect结果类型:用于直接跳转到其他的URI的结果类型。
⑨redirectAction结果类型:用于直接跳转到其他的Action的结果类型。
⑩stream结果类型:用于向浏览器返回一个InputStream(一般用于文件下载)。
⑾tiles结果类型:用于与Tiles整合的结果类型。
⑿velocity结果类型:用于与Velocity整合的结果类型。
⒀xslt结果类型:用于与XML/XSLT整合的结果类型。
⒁plainText结果类型:用于显示某个页面的原始代码的结果类型。
14,【redirect】结果类型。
这种结果类型与dispatch结果类型相对,dispatch结果类型是将请求Forward(转发)
到指定的jsp资源。而redirect结果类型,则意味着将请求Redirect(重定向)
到指定的视图资源。
dispatch结果类型与redirect结果类型的差别就是转发和重定向的差别:
重定向会丢失所有的请求参数和请求属性---当然也会丢失Action的处理结果。
使用redirect结果类型的效果是:系统将调用HttpServletResponse的sendRedirect(String)方法来重定向指定的视图资源,这种重定向的效果就是重新产生一个请求。所以所有的请求参数、请求属性、Action实例和Action中封装的属性全部丢失。
15,【redirectAction】结果类型.
使用redirectAction结果类型时,系统将重新生成一个新的请求,
只是该请求的URI不是一个具体的试图资源,而是一个Action。因此前一个Action处理结果
,请求参数,请求参数都会丢失。
16,除了可以通过通配符来配置Action(result),还可以使用OGNL表达式,这种
用法允许让请求参数来决定结果。如:
<action name="save" class="......." method="save">
<result name="input">/..jsp</result>
<result type="redirect" >edit.action?skillName=${currentSkill.name}</result>
</action>
对于上面的表达式语法,要求对应的Action实例里应该包含currentSkill属性,且currentSkill属性必须包含name属性--否则,${currentSkill.name}表达式为null。
17,模型驱动:
对于Struts1的ActionForm对象而言。它的唯一作用就是封装请求参数,当Struts1拦截到用户的请求后,Struts1负责将请求参数封装成ActionForm对象。
如果Struts2的开发者怀念这种开发方式,则可以使用Struts2提供的模型驱动模式,
这种模式也通过专门的JavaBean来封装请求参数。
相比于Struts1的Action类,Struts2的Action承担了太多的责任,既用于封装来回请求的参数,也保护了控制逻辑---这种模式实在不太清晰。出于清晰的考虑,应该采用单独的Model实例来封装请求参数和处理结果,这就是所谓的模型驱动。
作用:Struts2的模型对象可以封装更多的信息,它不仅可以封装用户的请求参数,而且还可以封装Action的处理结果。用单独的JavaBean实例来贯穿MVC流程。
使用模型驱动时,Action必须实现ModelDriven接口,且必须实现getModel方法,
该方法用于把Action和与之相对应的Model实例关联起来。
简单的说,模型驱动使用单独的VO(值对象)来封装请求参数和处理结果。
18,【属性驱动】:
使用属性(Property)作为贯穿MVC流程的信息携带者,当然属性无法独立存在,他必须依附于一个对象,这个对象就是Action实例,
简单的说,属性驱动使用Action实例来封装请求参数和处理结果。
19,Struts2的【处理异常机制】:
在execute方法中手动捕捉异常,当捕捉到特定的异常时,返回特定的视图--但是
这种方式很是繁琐,需要在execute中书写大量的catch块,最大的缺点
还在于异常与代码耦合,一旦需要改变异常处理方式,必须修改代码!
Struts2提供了一种声明式的异常处理方式。
在输出错误信息的jsp页面,有两种输出方式:
①通过struts2标签输出异常对象的message属性。<s:property value="exception.message"/>。
②通过struts2标签输出堆栈信息。<s:property value="exceptionStack"/>。
注意:全局异常映射的result属性通常不要使用局部结果,局部异常映射的result属性可以使用全局结果,也可以不使用。
20,对于WEB应用而言,所有的请求参数都是字符串类型的。
21,【类型转换器】:
struts2的类型转换器实际上是基于OGNL实现的,在OGNL项目中有一个TypeConverter接口,因其实现的方法过于麻烦,所以OGNL项目还提供了一个该接口的实现类:DefaultTypeConverter,通过继承该类来实现自己的转换器。
实现自定义的类型转换需要重写DefaultTypeConverter类的convertValue方法。
22,上面的类型转换器都是基于OGNL的DefaultTypeConverter类实现的,基于
该类实现类型转换器的时候时,将字符串转化成符合类型要通过convertValue方法实现
因此我们必须先通过toType参数来判断转换的方向,然后分别实现不同的转换逻辑。
为了简化类型转换的实现,struts2提供了一个StrutsTypeConverter抽象类(基于struts2的类转换器)
这个抽象类是DefaultTypeConverter类的子类.
23,对于以上的类型转换,我们一直只处理字符串数组的第一个元素---我们都假设请求参数是单个值。实际上,必须考虑请求参数是字符数组的情形, 假设用户信息的请求参数,名称都是user。那么这两个请求参数必须通过getParameterValues方法来获取参数。此时user请求参数必须是数组类型,或者List类型(实际上,List和数组是完全相通的)。
24,因为struts2内建的OGNL表达式的支持,那么可以用另一种方式将请求参数转换成复合类型,如(JSP页面中):
<input type=text name="user.name"/>,<input type=text name="user.pass"/>这样就不需要转换器了。
通过这种方式也可以把字符串转换成复合类型。但需要注意以下几点:
①因为struts2是需要直接创建一个复合类(User类)的实例,因此系统必须为该复合类构建一个无参的构造方法。
②如果希望使用user.name请求参数的形式为Action实例的user属性和pass属性赋值,则必须为user属性对应的复合类型提供setName方法,因为struts2是通过该方法类为属性赋值的。当然Action类还应该包含setUser方法。
25,表单元素enctype属性指定的是表单数据的编码方式。属性有如下3个值:
①application/x-www-form-urlencoded:这是默认的编码方式。它只处理表单中的value属性值,采用这种编码方式会将表单域的值处理成URL编码方式。
②multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定的文件内容也封装到请求参数里。
③text/plain:这种编码方式当表单的action的属性为mailto:URL的形式时比较方便,这种方式主要适用于直接通过表单发送邮件的方式。
26,通过Action在jsp页面输出提示信息,我们可以
①在Action中添加一个属性(通过setXx方法设置属性值),然后通过struts2的标签(<s:properties value="xx"/>)在jsp页面输出。
②通过ActionContext类来处理,如:ActionContext.getContext.put(key,value);然后通过EL表达式进行输出。
27,在Form表单中action属性的值要得到【上下文路径】:
①<%=request.getContextPath()%>
②还可以通过EL表达式得到 :${pageContext.request.contextPath}
28,得到【ValueStack】的对象有哪几种方法:
①ServletActionContext类中的方法:static getValueStack(HttpServletRequest req)【Gets the current value stack for this request】;
②ActionContext类中的方法:getValueStack()【Gets the OGNL value stack.】;
③ValueStack vs = (ValueStack) request.getAttribute("struts.valueStack");
(request.getAttribute()返回一Object类型)
29,【拦截器】。在默认的情况下,如果我们为某个Action定义了拦截器,则这个拦截器会拦截Action的所有方法。可能在有些情况下,我们无需拦截器所有的方法,只需要拦截某些方法,此时就需要struts2拦截器的方法过滤特性。
30,struts2的【校验】,可以继承ActionSupport类重写validate方法,利用ActionSupport的addFieldError方法把错误信息通过key保存起来,在jsp页面通过struts2标签的fielderror属性输出错误信息。注意:struts2的校验在配置action的时候需要提供输入页面(就是<result name="input">)。这是对action中的所有方法进行校验。
如果要对action中指定的方法进行校验,把validate方法名改为:validateXxx,其中Xxx是需要校验的指定方法名。
31,输入校验的流程:
①类型转换器把请求参数进行类型转换,并把转换后的值赋给action中的属性。
②如果在执行类型转换的过程中出现异常,系统会将异常信息保存到ActionContext中,conversionError拦截器将异常信息 添加到fieldErrors中。不管类型转换是否失败都会转入第三步。
③系统通过反射技术调用action中的validateXxx方法。
④再调用validate方法。
⑤如果fieldErrors中存在错误信息,系统自动将请求转发至input视图。如果没有错误信息将执行action中的方法。
注意:如果validate方法里没有问题,却返回input页面,可能是类型转换有问题。所以返回input视图有两种原因(类型转换有问题或校验出错).
32,基于xml配置方式对action内所有的方法进行校验,xml文件的命名规则是:ActionClassName-validation.xml
如果只需要对action内指定的方法进行校验,则xml文件的命名规则为:ActionClassName-ActionName-validation.xml。其 中ActionName(action的逻辑名称)。它的配置一般用通配符(有助于实验)。
①基于xml配置方式的校验,如果ActionClassName-validation.xml、ActionClassName-ActionName-validation.xml同时存在则把两个文件汇总,在进行校验。如果两个xml文件的校验规则起了冲突则执行后面的xml文件。
②如果action继承了另一个action则先找到父action校验文件,在找到子action校验文件,再把4(每个action都有指定方法名或非指定方法名)个文件汇总。
33,【国际化】。国际化按范围分为:全局、action、package的范围的资源文件。Properties文件命名的规则为:xxx_language_country.properties(xxx为用户定义的名字,第二部分为语言类别。)
Ⅰ定义好了国际化文件后,需要在struts.xml文件配置<constants name="struts.custom.i18n.resources" value="国际化文件的名称(xxx)">[这是定义全局的国际化资源文件],可以在jsp页面显示国际化信息<s:text name="key"/>。
Ⅱ也可以通过action类继承ActionSupport类,再调用个getText方法的资源文件的key(getText("key")).然后可以通过EL表达式在jsp页面输出。
Ⅲ也可以通过struts2的表单标签的key属性。如<s:textfield key="key"/>或<s:textarea key="key"/>
输出带有占位符的国际化信息:
①<s:text name="key"><s:param>....</s:param>..</s:text>
②通过ActionSupport类的getText(String key,String[] str)或者getText(String key,List list)
34,包范围的国际化资源文件,
在大型的应用程序中,整个应用有大量的内容需要国际化,如果我们把国际化的内容放置在全局资源属性文件中,显然会导致资源文件变得过于庞大、臃肿,不便于维护,这个时候我们需要针对不同的模块,使用包范围来组织国际化文件。
在java的包下创建名为:package_language_country.properties的资源文件,package为固定写法,language_country是对应的语言类别。处于该包及子包都可以访问该资源文件。当在包范围找不到对应的资源文件,然后会在全局范围内查找。
35,Action范围的资源文件。在Action所在的路径创建名为:ActionClassName_language_country.properties
如果同时存在全局、package、Action范围的国际化资源文件,系统搜索的顺序是:Action-->package-->全局范围。
36,以上三种配置有的是基于配置的国际化资源文件。我们也可以通过无配置的方式进行直接访问某个资源文件。
通过struts2标签,如:
<s:i18n name="xxx">
<s:text name="key">
</s:i18n> 其中xxx是全局范围文件的名称的前缀。
如果直接想访问包范围的国际化资源文件
<s:i18n name="com.johnny.action.package">
<s:text name="key">
</s:i18n> 其中package是固定写法。
37,OGNL表达式(Object Graphic Navigation Language对象图导航语言),当struts2接受一个请求时,会迅速创建ActionContext、ValueStack、action,然后把action存放在ValueStack,所以OGNL表达式可以迅速访问action实例的属性。
OGNL表达式一般要配合Struts2标签使用。
在struts2中,EL表达式只能访问OgnlValueStack的root里的属性。即action的属性(因为action的属性放在OgnlValueStack的root属性里)。利用OGNL表达式创建List/Map对象,<s:set var="" value="" scope=""/>scope指定变量的被放置的范围,可以为application、session、request、page和action。如果没有设置属性,则默认为OGNLContext(OGNL上下文)中,如果在OGNLContext访问时不用#,如果scope为request....中,则需要用#。value:赋给变量的值,如果没有该属性,则将ValueStack的栈顶的值赋给变量。
38,防止表单重复提交,首先要有struts的token标签。再使用系统的拦截器。