Struts2属于MVC框架
Struts2的优点:
1、侵入性低
2、提供了拦截器,可以利用拦截器进行AOP编程
3、提供了类型转换器
4、支持多种表示层技术:jsp,freeMarker,Veleocity
5、所有的请求都是使用拦截器处理
6、使用OGNL值栈
7、5和6也是缺点,导致执行效率低
Struts2的环境搭建:
1、创建web项目
2、放入struts2 的jar包(core:核心jar,ognl:值栈,xwork.jar)
3、在web.xml中配置过滤器(struts2定义的类 FilterDispatcher)
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
拦截所有请求 <url-pattern>/*</url-pattern>
4、执行过滤器的init方法,
5、Struts2的主配置文件放在src下
Struts2的配置:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="myLogin" namespace="/test" extends="struts-default">
<action name="login" class="com.asm.LoginAction">
//result标签相当于forward
<result name="loginSuccess">/success.jsp</result>
<result name="loginFailure">/failure.jsp</result>
</action>
</package>
</struts>
Struts2的action:收集表单数据,执行execute()方法,该方法没有参数,返回结果为String类型。
Struts2的执行流程:(执行环境 Tomcat6)
1、Tomcat一启动就创建filter对象,执行filter的init()
2、请求路径:项目名后+包下的命名空间名+访问的Action名,可以加上.action后缀,也可以不加。
http://127.0.0.1:8080/struts2/test/Login.action?message=first
3、如果需要添加参数,参数名要和Action类中的属性名一样
4、请求交给tomcat,tomcat创建request内置对象接收请求参数
5、Tomcat将请求交个Filter(filter发现是没有后缀或者是.action的请求,就把请求交给下一个拦截器,其余情况就不处理)
6、Filter会执到struts.xml中找到包(/test),在包下找到Action(Login)类
7、创建Action对象,将对象放到OGNL值栈中,通过拦截器设值(把表单中的参数名为message的值设到message属性中,如果类型不同可以自动转换),执行该对象的execute方法,根据返回值到主配置文件中找到对应的跳转路径,在jsp页面中共使用el表达式输出${message }
8、先到scope(request)中拿,发现没有就到值栈中拿,找到属性名为message的Action类,然后调用getMessage()方法输出
9、效率低
Struts2的流程
在web.xml中配置struts2
StrutsPrepareAndExecuteFilter(升级版的过滤器)
创建filter对象,执行init方法,读取struts2 的主配置文件
发送请求:
自动执行index.jsp
Login.jsp也会交给struts2的拦截器处理,因为配置的是拦截所有请求
但是拦截器拦截后判断如果不是.do,.action的请求就将请求交给Tomcat处理
请求路径:需要指定访问的包下的action
如果发现是.action结尾的请求,就会把请求交给另一个拦截器处理,找到对应的包名和action.通过命名空间找到包,通过action名字找到对应的action,然后马上创建action对象,自动把表单中的同名参数设到了action的同名属性,完成自动收集。
把封装用户信息的action,设到了值栈中
默认调用action的execute方法
返回字符串,字符串返回到action标签下的子标签 result,找到字符串对应的页面。
在jsp页面中首先通过EL表达式,找到属性名对应的属性值。到struts2的值栈中找到相应的值,如果没有就返回空串,如果有就返回其中一个。
值栈:
第一次接收请求会马上创建对象 ActionContext
该对象封装了action,数据结构为 最上部有个list集合,先入后出
有个map,以内置对象的属性名定义
一创建action,就放到值栈的list集合中(list集合中的值栈可以自动的设定,自动的获取)
valueStack,action
默认的配置:
Action标签中 默认调用的方法是 method=”execute”
没有指定class ,说明是ActionSupport
result标签中没有指定的result的name属性,默认值为success
包:struts2用包管理action,把一组相关业务的action放到一个包下
一个请求一个action,一个模块一个包(根据包分模块)
发请求时:指定包下的action路径(不同的包下的请求名可以相同)
Namespace可以不配置,默认的命名空间为:””
name:包名(必须唯一,是为了继承包而用的)
namespace:命名空间,直接体现在请求中(请求中通过命名空间找到对应的包)
extends 包中写了struts2的核心拦截器,继承后可以使用struts2的核心技术。(请求封装,拦截,国际化验证,上传等等)
<package name=” login” namespace=”/” extends=”struts-default” >
extends=”struts-default”下的拦截器:
自动将默认包读到内存中,
包定义为抽象:包中不能提供action
为什么要配置extends=”struts-default”,因为这个包是个抽象包,只能被继承,该抽象包中封装了很多拦截器(struts2的核心技术)。
Struts2中拦截器处理请求
包的作用:
1. 模块化开发(一个项目可以有多个包,(几个模块几个包)
2. 通过包能够使用struts2的核心技术 拦截器
包的搜索顺序:
包名为 path1/path2/path3
先找到包名为 path1/path2/path3,
如果没找到就找到包名为:path1/path2
如果还是没有找到就找包名为 path1
如果还时没有就去默认的namespace的package中找到名字为test的action<默认的命名空间为””>
result:(和struts1的forward标签类似)
对转向配置信息,通过转向信息觉得转到哪个页面
<result name=”success”>/WEB-INF/hello.jsp</result>
name:一定要和执行方法返回值相同
这边的跳转是服务端跳转
<result name=”success”type=”dispatcher”/>
默认使用转发(服务端)
如果需要变为客户端跳转就将type定义为 redirect
Type的取值:dispatcher,redirect,chain,redirectAction,plainText
redirectAction:后面跟的是转向路径的名字(转到下一个Action)
redirect:重定向到另一个页面
<result type=”redirectAction”>helloword</result>//这里只能转到当前包下的action
如果需要转到别的包下的action:
<result type=”redirectAction”>
这里是找到/test包下的helloword的action
<param name=”actionName”>helloword</param>
<param name=”namespace”>/test</param>
</result>
在result中可以在转向页面路径上取id值
<result type=”redirectAction”>helloword.jsp?id=${id}</result>
result的作用:提供转向路径,在后端处理器调用
result的配置位置:配置在<action>标签的内部,表明是当前action提供的转向信息,转到某一个页面或者是转到另一个action
常量:一般在主配置文件struts.xml文件中定义
Struts2默认的扩展名是.action,或者是不写(没有扩展名)
//可以接收任何扩展名的请求(.do ,.go)根据value的配置而确定
<constant name="struts.action.extension" value="do,go"/>
//指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出
<constant name="struts.i18n.encoding" value="UTF-8"/>
//设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭
<constant name="struts.serve.static.browserCache" value="false"/>
开发模式下使用,这样可以打印出更详细的错误信息
<constant name=”struts.devMode" value="true" />
默认的视图主题
<constant name="struts.ui.theme" value="simple" />
与spring集成时,指定由spring负责action对象的创建
<constant name="struts.objectFactory" value="spring" />
该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false。
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
上传文件的大小限制
<constant name="struts.multipart.maxSize" value=“10701096"/>
常量的作用:设定编码方式,动态方法调用,设定上传文件的大小
Action:接收中央控制器的请求,并且把请求交给业务对象处理
收集表单参数的两种方式:从请求中取得,从配置中取得
需要调用请求中的另外的方法:三种实现方式
- 配置method = “other”,默认是execute方法
- 开启动态方法调用 : !+方法名
http://127.0.0.1/8080/strut2/test/helloword!other?id=1
前提是将常量设置为true,动态常量开启<constant name= "struts.enable.DynamicMethodInvocation" value= "false" />
- 请求通配符(helloword_other.action)other:是指定的方法名
<action name=”helloword_*”class=”…” method={1} >
Struts2的简单流程:
- 请求交给拦截器,拦截器处理指定的请求,如果不属于struts2指定处理的请求类型就将请求交给Tomcat处理
- 如果属于:拦截器对请求做处理,先截取分析(多个拦截器)
- 创建action对象放入值栈中
- 调用action对象的方法,返回字符串
- 到action标签中找到返回字符串对应的result标签,完成相应的跳转
Sturts2的拦截器
拦截器是struts2的核心
作用:完成解析请求参数,将请求参数赋值给Actio属性,执行数据校验,文件上传等。
拦截器定义在extends= struts-default包中,会自动使用该包下的所有拦截器,如果自己定义了拦截器就会覆盖其他的拦截器。需要重新引入才可以使用。
拦截器的自定义
方式一:实现Intercepter接口,实现intercept方法,在intercept方法中调用invocation.invoke();语句上面的是对请求拦截,下面的语句是对结果拦截。
String result = invocation.invoke();方法的返回值是String类型,intercept方法的返回值就是返回这个result,然后再判定是不是继续激活拦截器还是将请求下传。
方法二:继承抽象类AbstractIntercepter,复写intercept方法,和一类似
方法三: 继承,MethodFilterIntercepter类
复写 doIntercept方法,里面的内容和一类似
注册拦截器:
在struts.xml中,在定义包下添加<interceptors>标签,里面定义<interceptor name=”标识”class=”拦截器的全路径名”>标签
拦截器的使用:
在<action>标签中 配置<interceptor-ref=”标识”/>
一定要配置<interceptor-ref=” defaultStack”/>表示重新引入struts2默认的拦截器
拦截器的执行顺序和Spring的拦截器执行顺序类似。
利用struts2获取内置对象
获取内置对象的方法一:实现这三个接口: ServletResponseAware, ServletReqestAware , ServletContextAware(注入)
实现相应的方法,设定实例全局变量。 注入属性到实例全局变量中,然后就可以直接使用(request,response,servletContext对象)
获取内置对象的方法二:通过ServletActionContext.类的静态方法 ServletActionContext.getRequest();(依赖查找)
ServletActionContext.getResponse();
ServletActionContext.getServletContext();
得到request,response,servletContext内置对象,然后使用
获取内置对象的方法三:通过值栈ActionContext
ActionContext ac = ActionContext.getContext();
从ActionContext中拿到application的map,设值,然后往对应的application内置对象中设值,属性名为app,属性值为123
ac.getApplication().put(“app”,”aaplication123”);
ac.getSession().put(“session”,”session 123”);
ac.put(“req”,”request123‘’);//设值到request内置对象中
ActionContext本身是个Map,往ActionContext中设值就是往map中设值,然后在将值设到request内置对象中
类型转换:
int,double,boolean类型的数据会自动转换
struts2的转换器在一个文件下:xwork-message.properties
待转换的类型 = 类型转换器的全类名
java.util.Date = com.struts.DateConverter
处理出错信息流程:(Action类继承ActionSupport)
1、 表单中有String类型的数据
2、 创建action对象后把字符串封装到参数名对应的属性名上,但是属性名是Util类型
3、 在默认的文件中找不到把string类型的数据转换为util类下的数据的信息
4、 先到actionContext(值栈)中找一个map,名为fielderror,将转换出错信息设到该map中
5、 创建Action对象执行action对象的相应方法,发现map集合的长度大于0 就返回input。
6、 跳到result标签中input所标识的页面
7、 在jsp页面中通过<s:fielderror/>输出出错信息
如果Action没有继承ActionSupport类,就逆行转换,将action中的util类型的日期转换为String类型输出。(不继承ActionSupport就不找到转换器)表单中的参数值是什么就是输出什么。将属性转换为String类
转换器的编写
编写一个类继承DefaultTypeConverter
复写方法 convertValue方法
Public class DateConverter extends DafaultTypeConverter{
Public Object convertValue(Map context,Object value,Class toType){
SimpleDateFormat sdf=new SimpleDateFormat(“日期格式”);
If(toType == Date.class){
//request.getParameterValues(name),从表单中取出来的参数值
String[] params=(String[]) value;
return sdf.parse(param[0]);
} }}
转换器的注册
在src下写一个xwork-conversion.properties文件中写上:
java.util.Date = com.struts.DateConverter
局部类型的转换器:
将上面的类型转换器注册为局部类型转换器:
在Action类所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的类名,后面的-conversion.properties是固定写法,对于本例而言,文件的名称应为typeconvertAction-conversion.properties(typeconvertAction为类名)
在properties文件中的内容为:
属性名称=类型转换器的全类名
对于本例而言, typeconvertAction-conversion.properties文件中的内容为
utilDate= com.asm.DateConverter
如果全局转换器和局部转换器都写了,优先使用局部转换器。
Struts2的异常处理
在struts.xml中配置:
<action name="login" class="com.asm.LoginAction" method="add">
<exception-mapping result="usernameException"
exception="com.asm.UserNotFoundException">
</exception-mapping>
<result name="usernameException">/usernameexception.jsp
</result>
<exception-mapping result="PasswordException"
exception="com.asm.PasswordErrorException">
</exception-mapping>
<result name="PasswordException">/passwordexception.jsp</result>
<result name="loginSuccess">/success.jsp</result>
<result name="input">/validate.jsp</result>
</action>
在某个Action中出异常了。就到action中找到exception-mapping标签,找到具体的异常处理类。
1.局部异常处理仅在对应的action方法中有效
2. <exception-mapping>中的result属性的值
来源于<result>元素中的name属性的值
当action方法向struts2抛出异常对象时, struts2根
据struts.xml中的exception-mapping标签的result
属性找到result标签的name属性从而确定相应的转向页面。
定义全局异常:
<package name="ex" namespace="/" extends="def">
<action name="login" class="com.asm.LoginAction" method="add">
<exception-mapping result="usernameException"
exception="com.asm.UserNotFoundException">
</exception-mapping>
<result name="usernameException">/usernameexception.jsp
</result>
</action>
异常处理流程:
1、发请求login.jsp
2、请求给struts2,struts2发现请求是.jsp结尾就把请求交给tomcat处理
3、Tomcat拿到login.jsp,执行login.jsp
4、在login.jsp页面中提交错误的用户名和密码
5、将请求交给struts2
6、Struts2将请求下传,截取请求
7、找到对应的action,创建action对象,将表单中的参数收集过来
8、Action对象和参数都存到值栈中,然后执行action的对应的方法(add)
9、创建业务对象,对登录表单中的数据进行验证(验证方法中声明不处理异常,直接抛出异常) public void add()throws Exception{}
10、验证出错就产生异常 throw new UserNotFoundException();
11、到该action类对应的action标签中找到与异常对象类型相同的exception-mapping标签,
12、通过exception-mapping找到result属性
13、根据result属性找到对应的result所指的页面,对异常进行处理
如果在该action标签中找不到对应的异常,就会到当前包所继承的包下找到全局异常信息,在包下找到execption-mapping,然后找到全局的result,转到相应的页面。
1.全局异常处理对所有的action方法都有效
2.全局性的异常应放在一个抽象包中供其他包继承
3.在相应的包中我们继承了全局异常所在的包,该包中的所有action的方法均可进行全局异常处理,处理方法和局部异常处理相同。
Struts2对请求的验证:
Struts2对于输入校验的方法:
1、采用手工编写代码实现
2、基于XML配置方式实现
采用手工编写代码实现
1、action类必须继承ActionSupport类
2、复写validate()方法,在调用action的方法之前会先调用validate()方法
3、validateLogin()方法,只对访问当前action类的login方法做验证
通过validateXxx()方法实现, validateXxx()只会校验action中方法名为Xxx的方法。validateLogin()
验证流程:
1.类型转换器对请求参数执行类型转换,并把转换后的值赋给action中的属性。
2.如果在执行类型转换的过程中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息添加到fieldErrors里。不管类型转换是否出现异常,都会进入第3步。
3.系统通过反射技术先调用action中的validateXxx()方法,Xxx为方法名。一旦出错就调用addFieldError()方法,将出错信息存到map中
4.再调用action中的validate()方法。一旦出错就调用addFieldError()方法 this.addFieldError("username", "用户名不能为空");
5.如果系统中的fieldErrors存在错误信息(即存放错误信息的集合的size大于0),系统自动将请求转发至名称为input的视图。在input指定的jsp页面中使用<s:fielderror/>显示失败信息。
6.如果系统中的fieldErrors没有任何错误信息,系统将执行action中的处理方法
验证方法的缺点:自己手动写验证规则代码
基于XML配置方式实现
使用基于XML配置方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件,校验文件和action类放在同一个包下,文件的取名格式为:ActionClassName-validation.xml,其中ActionClassName为action的简单类名,-validation为固定写法。
如果Action类为com.asm.LoginAction,那么该文件的取名应为:LoginAction-validation.xml。
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field>指定action中要校验的属性,<field-validator>指定校验器,上面指定的校验器requiredstring是由系统提供的,系统提供了能满足大部分验证需求的校验器,这些校验器的定义可以在xwork-2.x.jar中
<field name="username">//指定action中验证的属性名
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>为校验失败后的提示信息,如果需要国际化,可以为message指定key属性,key的值为资源文件中的key。
<message> 用户名不能为空!</message>
</field-validator>
</field>
</validators>
系统提供的校验器如下:
required (必填校验器,要求field的值不能为null)
requiredstring (必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
stringlength(字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)
regex(正则表达式校验器,检查被校验的field是否匹配一个正则表达式.expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
fieldexpression(字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
email(邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址)
url(网址校验器,要求如果field的值非空,则必须是合法的url地址)
date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
conversion(转换校验器,指定在类型转换失败时,提示的错误信息)
visitor(用于校验action中的复合属性,它指定一个校验文件用于校验复合属性中的属性)
expression(OGNL表达式校验器,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中)
防止用户重复提交请求:
发送请求的表单如下:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<body>
<form action="<%=request.getContextPath()%>/login.action" >
姓名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
<s:token></s:token>
</form>
</body>
</html>
<s:token></s:token>标签,它的作用就是在用户访问此页面时会生成一个sessionId,在提交时会服务器会据此验证表单是否已提交。
我们必须要在提交的表单中使用这个token tag,这样提交到的Action便能配置TokenInterceptor拦截器验证表单是否重复提交。
<input type="hidden" name="struts.token.name" value="token" />
<input type="hidden" name="token" value="D3VI7HWCJ4PSOSYFZ6A1QIJT0J6VAHTT" />
struts.xml主要配置内容如下:
<struts>
<package name="tokenTest" extends="struts-default">
<action name="login" class="com.asm.LoginAction">
<result name="success">/success.jsp</result>
<result name="loginFailure">/failure.jsp</result>
//重复提交请求后跳转到subError.jsp页面
<result name="invalid.token">/subError.jsp</result>
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
</package>
</struts>
调用action的execute方法之前会调用拦截器,该拦截器定义在struts-default包下,但是需要人为的引用该拦截器。<s:token/>
如果是新的请求就可以再次提交了,因为token的value值(相当于sessionID)会重新生成。这里只是为了避免同一个请求重复提交。
Struts2的标签
1、引入标签库 <%@ taglib uri="/struts-tags" prefix="s" %>
基础表单标签
在html我们常用的基础表单标签主要有文本域、密码域、提交、重置四种。
<s:form action="login" method="post" namespace="/my">
required设为true,表示此表单项为必填内容以“*”提示
requiredposition="right":*显示在标识的右侧
<s:textfield label="用户名" name="username" required="true" requiredposition="right"/>
<s:password label="密码" name="password" required="true" />
<s:reset value="重置" align="left"/>
<s:submit value="注册" align="left"/>
</s:form>
<s:bean name=”cn.struts2.User” id=”user”/ >该user对象存放在stackContext中,如果需要取出来就加 # 号
一个请求一个值栈,重新发请求后会创建新的值栈,销毁原来的值栈。
<s:set name="name" value="'kk'" />把属性名为name的属性值kk设置到map中,(栈中)
<s:property value=”name”>先到值栈里面找name为name的value中,如果没有找到就到栈里面找。
<!--创建集合对象,然后把集合对象放到值栈中 -->
<s:set var="list" value="{'第一个','第二个','第三个'}"/>
<!-- iterator迭代的特点:会把迭代的集合元素放到值栈的栈顶 -->
<s:iterator value=“list”> <!--从值栈取出名字为list的集合并依次取出各个元素,把集合元素放到值栈的栈顶 -->
<s:property/> <!--从值栈取出集合元素并显示出来-->
</s:iterator>
if/elseif/else标签
<s:set name="age" value="21" /> :存放在栈空间
<s:if test="#age==23">
23
</s:if>
<s:elseif test="#age==21">
21
</s:elseif>
<s:else>
都不等
</s:else>
当标签的属性值作为字符串类型处理时, “%”符号的用途是计算OGNL表达式的值。
<s:set name="myurl" value="'http://www.foshanshop.net'"/>
<s:url value="#myurl" /><br>//s:url不支持栈输出,需要使用%
<s:url value="%{#myurl}" />
输出结果:
#myurl
http://www.foshanshop.net
说明:由于url标签的value属性默认不支持ognl,所以我们要使用%{}来表示{}中的#bdUrl是一个ognl表达式。
Struts2的文件上传
上传条件:
1、导入jar包(在WEB-INF/lib下加入commons-fileupload-1.2.2.jar、commons-io-2.0.1.jar。这两个文件可以从http://commons.apache.org/下载。)
2、在常量中设定上传文件的最大值
3、表单属性enctype设置为:“multipart/form-data“,如下:
<form action="<%=request.getContextPath()%>/fileupload.action" method="post" enctype="multipart/form-data">
标题:<input type="text" name="title"><br>
文件:<input type="file" name="myfile"><br>
<input type="submit" value="提交">
</form>
4、Action类的属性定义
public class fileuploadAction {
private String title;
private File myfile;//得到上传的文件(把表单里的文件收集到当前action的myfile属性
private String myfileContentType;//得到文件的类型(struts2通过文件自动识别文件类型设到myfileContentType属性上,后面一定是ContentType,前面一定是上传文件的参数名)
private String myfileFileName;//得到文件的名称struts2通过文件自动识别文件类型设到myfileFileName属性上,后面一定是FileName,前面一定是上传文件的参数名)
//这里略省了属性的getter/setter方法
}
创建Action对象后,将表单中的参数封装到Action中相应的属性上。
下载文件:(Action类中的一个方法)
public String execute() throws Exception {
String realpath = ServletActionContext.getServletContext(). getRealPath ("/images");//放到项目根路径下的images文件夹中
File file = new File(realpath);
if(!file.exists()) file.mkdirs();//如果images文件夹不存在就手动创建
FileUtils.copyFile(myfile, new File(file,myfileFileName));//将上传myfile所指的文件对象拷贝到以myfileFileName命名的文件中
return "fileuploadSuccess";
}
多文件上传:
1、导入jar包commons-fileupload-1.2.2.jar、commons-io-2.0.1.jar。这2、form表单
<form action="<%=request.getContextPath()%>/mulfileupload.action" method="post" enctype="multipart/form-data">
标题:<input type="text" name="title"><br>
文件:<input type="file" name="myfile"><br>
文件:<input type="file" name="myfile"><br>
<input type="submit" value="提交">
</form>
3、Action类的属性定义
public class mulfileuploadAction implements Action {
private String title;
private File[] myfile;//得到上传的文件数组
private String[] myfileContentType;//得到文件的类型数组
private String[] myfileFileName;//得到文件的名称数组
//这里略省了属性的getter/setter方法
}
4、文件下载
public String execute() throws Exception {
String realpath = ServletActionContext.getServletContext()
.getRealPath("/mulimages");
File file = new File(realpath);
if(!file.exists()) file.mkdirs();
for(int i=0 ;i<myfile.length; i++){ File myfile1= myfile[i];
FileUtils.copyFile(myfile1, new File(file,myfileFileName[i]));
}
return "mulfileuploadSuccess";
}
Struts2的国际化
1、在src下配置国际化资源文件
2、在struts.xml中通过常量指定国际化资源文件的基名
<constant name="struts.custom.i18n.resources" value="myapp" />
myapp为资源文件的基本名。
资源文件的命名格式如下:
baseName_language_country.properties
baseName_language.properties
baseName.properties
其中baseName是资源文件的基本名,我们可以自定义,但language和country必须是java支持的语言和国家。如:
中国大陆: baseName_zh_CN.properties
美国: baseName_en_US.properties
使用浏览器默认的locale信息
1、Tomcat会把请求中的locale信息放到与该请求对应的session中,2、struts2的静态国际化标签,找到请求对应的locale信息决定调用哪个文件,<s:text name=”login_submit”/>name 指定国际化资源文件的key
如果国际化资源文件中没有该key,就会自动把当前页面的key作为value来显示。(原样显示chinese)<s:text name=”chinese”/>
3、自动到struts2的struts.xml中通过常量找到基名。
4、通过基名+locale信息就能找到对应的国际化资源文件
通过action对象的方法完成不同语言的切换
在struts.xml中配置
<action name="cl" class="com.asm.ChangeLangAction" method= "changeLang">
<result>/login.jsp</result>
</action>
Action类中的方法
action对象的changeLang方法
public String changeLang() throws Exception {
Locale locale = null;
if (lang.equals("zh")) {
locale = Locale.CHINA; //利用常量创建locale对象
} else {
locale = Locale.US;
}
ServletActionContext.getRequest().getSession().setAttribute("WW_TRANS_I18N_LOCALE", locale);//将locale信息设置到session里面
return SUCCESS;
}
1、先到struts.xml的常量中找到基名
2、从请求中找到locale信息
3、通过基名和locale信息找到指定的国际化资源文件
4、从国际化资源文件中找到key对应的value进行显示
Struts2的动态国际化:
国际化资源文件中key使用占位符:login_fail=username error {0},and password error {1}
在页面中输出带占位符的国际化信息
<s:text name="longin_fai">
<s:param value="%{username}"></s:param>
<s:param value="%{password}"></s:param>
</s:text>
表单中的输入用户名为:Tom,密码为123
<s:text>输出结果为 username error Tom ,and password error 123
国际化资源文件有全局国际化资源文件也有局部国际化资源文件:
包下的国际化资源文件:
package_language_country.properties资源文件,package为固定写法,处于该包及子包下的action都可以访问该资源。当查找指定key的消息时,系统会先从package资源文件查找,当找不到对应的key时,才会从常量struts.custom.i18n.resources指定的资源文件中寻找。
Action下的国际化资源文件:
ActionClassName_language_country.properties资源文件查找,如果没有找到对应的key,然后沿着当前包往上查找基本名为package 的资源文件,一直找到最顶层包。如果还没有找到对应的key,最后会从常量struts.custom.i18n.resources指定的资源文件中寻找。
局部的国际化资源文件不需要基名,全局的国际化资源文件都需要基名。
全局国际化资源文件拿到基名的方式:
1、通过标签拿到基名<s:i18n name=”myApp”>
2、没有标签就到struts.xml的常量中找到基名
Struts2的流程:
1、用户发请求先经过三个过滤器 ActionContextCleanUp,OtherFilters,(SiteMesh etc) FilterDispatcher(拦截器)
2、FilterDispatcher会将请求交给ActionMapper,ActionMapper判断是否处理当前请求
3、如果处理,FilterDispatcher就会创建ActionProxy代理对象,
4、ActionProxy代理对象创建ConfigurationManager对象,从struts.xml中读取配置信息
5、读取信息后会知道有哪些包,需要创建哪些Action对象,但是先不创建这些对象
6、创建ActionInvocation对象,
7、ActionInvocation对象根据请求找到对应包下的Action创建Action对象,调用拦截器把表单中的数据封装到action对象的属性中,然后将action对象封装到ognl值栈中
8、在封装表单参数的过程中,如果出现异常会调用处理异常的拦截器
9、在执行真正的方法之前会调用自己写的拦截器,以及格式验证的的拦截器
10、最后执行Action相应的方法 默认为execute
11、将执行结果 result返回给ActionInvocation
12、ActionInvocation拿到结果信息后,到struts.xml文件中转到相应的jsp页面。页面中处理结果信息,浏览器中显示结果信息。
OGNL值栈:
值栈的生存周期和request相同,请求一来就创建,请求结束就销毁。
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts 2框架使用OGNL作为默认的表达式语言。
值栈的结构:
值栈的数据结构主要有stack和valueStack
值栈Valuestack中主要是list,存放Action对象
栈Stack中主要是map,存放对象
OGNL中常用的map:
Parameters ,request,session,application,attribute
值栈的功能:
调用对象里的方法,属性
一个请求对应一个值栈,值栈中有个map,会把请求的参数名和参数值自动到封装到map中
创建Action对象后会自动的把action放到值栈中
调用静态方法
操作集合对象
Stack Context:
ValueStack:有个map集合parameters,封装表单中的参数名和参数值
Struts2重写了request,复写了getAttribute()方法
表单:<input type=”text” name=”user.username”>
Action类: 有个User user 属性,收集时会将user.username 设到Action类中的User对象的username 属性上。
创建Action对象时,会创建User对象,将表单中的数据封装到user对象中。
所有表单中提交的参数名和参数值都会进到值栈中 parameters Map
在Action类中创建的三个集合对象会放到值栈中的值栈 ValueStack中,因为这些集合对象属于Action对象。
值栈 list
栈是 map
Action对象存放在值栈(valueStack)中
使用#号,从栈中(Map)取出数据:
获取Request属性:<s:property value="#request.req"/><br>
获取Session属性:<s:property value="#session.ses"/><br>
获取parameters属性:<s:property value="#parameters.mes"/>
说明:我们获取这些对象都用了#,因为这些对象都是存在一般的Context Map中,而不是存在值栈中。
显示值栈的详细信息:
在jsp页面中使用 <s:debug></s:debug>
需要在struts.xml文件中配置 常量来struts.devMode,以及allowStaticMethodAccess(允许使用静态方法)
<s:property value=”user.username”/>到list集合的valueStack中看哪个Action对象包含User属性,调用user属性对应的user对象的getUsername()方法
${user.username}先通过EL表达式输出,发现没有就通过request getAttribute输出,发现还是没有就到list集合的valueStack中看哪个Action对象包含User属性,调用user属性对应的user对象的getUsername()方法
<s:property value=”get()”/>看那个Action包含get方法直接调用Action 对象的get方法
<s:property value=”user.get()”/>先到值栈中找到属性名为user的Action,然后调用user属性所指user对象的get方法
<s:property value=”@cn.struts2.LoginAction@getSta()”/> 到值栈中找到这个类LoginAction的getSta()静态方法,然后调用这个静态方法
<s:property value=”map.m1”/>找到map集合中key= m1的value值
<s:property value=”map.keys”/>找到map集合中所有key的值
<s:property value=”map.values”/>找到map集合中所有value的值
使用#号,从栈中(Map)取出数据:
在Action类中:
ActionContext ctx = ActionContext.getContext();//获取包含当前HttpServletRequest的属性(attribute)的Map
ctx.put("req", "Req属性");//向包含当前HttpServletRequest的属性(attribute)的Map设值(栈里和request内置对象中设值)
ctx.getSession().put("ses", "Ses属性");//拿到session表示的Map,将值设到map中(栈),然后设到session内置对象中
在jsp页面中:
获取Request属性:<s:property value="#request.req"/><br>//到stackContext中找到request的map,然后找到map中 可以为req对应的value值。
${req} :从内置对象中取出值,因为往值栈中设值的时候也会在内置对象中设值。
获取Session属性:<s:property value="#session.ses"/><br>
${session.ses}:如果在内置对象中没有找到key为ses对应的value,就会输出空串,因为struts2只复写了request,并没有复写session。所以无法继续从值栈中找到对应的值。
获取parameters属性:<s:property value="#parameters.mes"/>
说明:我们获取这些对象都用了#,因为这些对象都是存在一般的Context Map中,而不是存在值栈中。
Struts2+Spring3.0+Hibernate3.x
1、搭建集成环境
2、导入jar包(struts2-spring-plugin.jar以及其他的jar包)
3、持久层:HIbernate完成,Spring替hibernate生成sessionFactory, hibernate配置hbm文件生成对应的表以及表的关系
4、视图层:struts2
5、控制层:Spring
6、业务层:
启动tomcat:
1、读取web.xml文件,创建了监听器对象,对application对象的创建和消耗做监听
2、监听器实现了ServletContextListener接口
3、读取全局初始化参数,创建application内置对象,将全局初始化参数放到application内置对象中
4、监听器监听到application内置对象的创建,调用contextInitialized 方法,通过注入事件对象拿到application,取得application中的初始化信息,创建BeanFactory
5、读取数据源 dataSource(数据库基本信息)
6、通过注入数据源,以及hbm,是否建表和显示sql等信息来生成sessionFactory
7、通过sessionFactory注入session对象(HibernateTemplate和线程绑定)
8、通过sessionFactory工厂生成事务管理器 HiberanateTransactionManager(根据事务传播特性决定是否使用事务)
9、根据事务管理器配置事务传播特性(应用在方法上,决定什么样的方法怎么使用事务)
10、配置Aop,(指定事务边界)
11、以注入的方式,创建持久对象(把hibernateTemplate直接注入),从工厂中拿到的对象为目标对象,不是代理对象,因为持久层不符合pointcut
12、以注入的方式创建业务层对象(把持久层对象注入),工厂会为业务对象创建代理对象,因为它符合pointcut
<创建代理对象的目的是给方法织入advice,advice决定哪些方法怎么使用事务>
13、Struts2的action交给beanFactory创建,并且指定为多例 scope=prototype并发性高,(注入业务层对象)
14、在web.xml文件中配置hibernate的懒加载过滤器OpenSessionInviewFilter(结果拦截时关闭session,使用cglib代理)
15、在web.xml中创建struts2拦截器 StrutsPrepareAndExecuteFilter
16、Struts.xml放在src下,配置常量,包,以及action ,result
Struts.xml中的配置信息
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
标识action对象由bean工厂创建,不会再由struts2创建
<constant name="struts.objectFactory" value="spring"></constant>
<package name="myLogin" namespace="/" extends="struts-default">
testController:和beanFactory中配置Action的id相同
<action name="reg" class="testController">
<result name="loginSuccess">/success.jsp</result>
<result name="loginFailure">/failure.jsp</result>
</action>
</package>