struts2-第一天
框架搭建
1.jar包的准备
2.配置web.xml,配置struts2核心控制器,接收所有的请求(可在dispatcher.ng.filter包下拷贝,把.class去掉)。
3.拷贝struts.xml和log4j到src目录下。
3.1写个package包,给个名字:user,包就相当于一个模块,再添加extends="struts-default"
3.2里面写action标签,写name和实体类
3.3里面写result标签,写name和页面
执行的过程:请求url满足了/*,就交给了核心控制器,它启动会把struts.xml文件的配置全部读到内存中,用一个对象来保存,会在这<action>里面找有没有login的配置,接着会找到对应的类,当action配置里没有配method属性,它就执行业务逻辑控制器类里的execute(),方法完成后会返回字符串,这个字符串会交给核心控制器,再找<result>里对应的字符串,匹配就会用这个页面生成响应到客户端。
struts2-core-2.3.15.1.jar里org/apaqi/struts有配置文件default.properties,
1.更改请求后缀<constant name="struts.action.extension" value="do">
2.开启开发模式<constant name="struts.devMode" value="true">
ActionSupport类
struts.xml里,<package>配置extends="struts-default"的好处是,如果action里没有配置class属性,将使用struts-default.xml(在核心jar包里)中配置的默认类com.opensymphony.xwork2.ActionSupport(最后面),如果没有配置method,就使用execute(),如果result里没有配置name,默认是success
请求参数接收
一、标量值方式接收参数
struts对于提交到action的参数,会当作这个action有这个属性,就会调用这个参数set方法来接收(保存)提交的参数。
二、复合类型方式接收参数(常用)
三、模型驱动方式获取参数(少用)
1.业务逻辑控制器类要实现ModelDriven<?>接口
2.预先创建user,并提供方法返回user
3.代码不需要写getset方法
四、servlet API方式获取参数(需掌握)
1.业务逻辑控制器类要继承ActionSupport
2.通过ServletActionContext.getRequest()得到HttpServletRequest
3.就可以用以前getParameter()
Servlet APi 获取
一、ServletActionContext类(重点)
ServletActionContext.getRequest().getSession()
二、Struts2Aware拦截器IOC模式(重点)
1.实现ServletRequestAware接口,重写setServletRequest(HttpServletRequest request),把参数赋值给成员变量HttpServletRequest request,后面就可以使用request。
三、四、ActionContext类的get方法
Action动态方法
需要开启动态方法调用支持,<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
一、login!login.do叹号方式.注意:用感叹号动态调用时,配置文件的action是不写!的,在提交地址那里写,如不写!指定调用的方法,默认调用execute方法。
二、login_*统配
命名空间
1.命名空间前面加/
规则是:1.1如果命名空间存在,进入命名空间搜索action,action存在就执行,不存在就报异常
1.2如果命名空间不存在,自动删除最后的命名空间,再进行搜索
2.加命名空间后,提交地址要加前面那段(不能/开头),设置了后缀加后缀,但配置那里的action的name属性不加后缀(这里必须注意)。
3.当配置文件struts.xml的<package/>用了命名空间时,响应页面一般要加/。而提交地址要加命名空间.
六、类型转换
2013-1-15默认会转换?月份有0吗?
自定义类型转换器的实现
方式1.继承DefaultTypeConverter类
1.1如上,UserAction类要继承ActionSupport类(如不继承,无法显示异常等)
1.2重写convertValue方法
1.3在action包下新建一个同名的UserAction-conversion.properties,文件内容:user.loginTime属性 = org.fkjava.converter.MyConverter转换器
全局的跟struts.xml配置文件放在一起
方式2.继承StrutsTypeConverter类(抽象类)(推荐使用)
--有两个要实现的方法,主要实现前台到后台convertFromString(Map context,String[] values,Class toType)方法,第一个参数是上下文,第二个是提交的实际数据,第三个是提交数据的类型
方法实现逻辑:判断values不为空后,如果写的是转换时间的转换器,判断toType==Date.class,一旦涉及到时间对象,simpleDateFormat,从字符串转换为Date类型用sdf.parse(values[0])
全局:在src下xwork-conversion.properties,java类型=自定义转换器
转换错误显示:
1.默认:
1.1<%@taglib prefix="s" uri="/struts-tags"%>,<s:fielderror>
2.局部类型转换失败自定义信息
2.1在action包下新建一个UserAction.properties文件,内容是invalid.fieldvalue.属性名(表单提交的name属性user.loginTime)=提示语句
2.2throw new TypeConstraintException("日期类型转换失败!");
3.全局类型转换失败自定义信息
3.1 src根目录下,fkjava.properties国际化资源文件:xwork.default.invalid.fieldvalue={0},提示信息
3.2 struts.xml下配置常量name="struts.custom.i18n.resources" value="fkjava"/>
七、后台验证
如果希望使用struts2提供的验证框架,必须extends ActionSupport
调用步骤:
1.先类型转换
2.验证
3.调用目标action方法
方式一:代码方式实现输入验证
a.对action所有方法进行验证
方式1:重写ActionSupport中的validate方法
//针对本action的所有action方法进行验证
public void validate(){
System.out.println("-------validate()--------");
if(user == null || "".equals(user.getUserName())){
/**一旦addFieldError()加了东西,就会返回INPUT*/
this.addFieldError("user.userName","请输入用户名");
}
}
--输出错误信息:<s:fielderror/>
b.对单个方法进行验证,只需要在这个方法的后面加你要验证的方法,如login方法要验证,validateLogin()
方式二:配置xml方式实现输入验证(比较常用)
xml文件名写法:NoticeAction-addNotice-validation.xml针对一个action请求做后台验证(中间是<action name=""/>里name的值)
NoticeAction-validation.xml针对整个NoticeAction处理类都做后台验证
例子:
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN"
"">
<validators>
<field name="user.userName">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>请输入用户名3!</message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">4</param>
<param name="maxLength">6</param>
<message>请输入${minLength}~${maxLength}范围的用户名3</message>
</field-validator>
</field>
</validators>
国际化
表单:
1.写fkjava.properties国际化文件,依次再写其他语言的properties。说明:里面写的就是需要替换的字眼
2.用struts2标签库写表单(先引入),表单里的每一项多一项key的属性,写的就是properties里面的key
3.如果想动态改变语言环境,加一个带request_locale参数的表单,如直接提交到jsp不行,就配置一个最简单的action把表单提交到action,再<result>到jsp
页面:
1.使用i18n标签,name=资源文件前缀,<s:text name=资源文件中的key>,再里层标签<s:param>${username}</s:param>
<s:i18n name="fkjava">
<s:text name="welcome">
<s:param>${user.userName}</s:param>
</s:text>
</s:i18n>
代码中:
1.put之后,利用ActionContext.getContext.put("msgs",this.getText("国际化资源里的key")),页面上取出:${msgs}
result的type属性
服务器重定向
1.dispatcher(默认),转到资源(jsp html 其他视图技术)
2.chain,转到一个action,参数有actionName,nameSpace,method等。
客户端重定向(参数不会自动传递,如需传递,在actionName拼接,或加<param name="user.userPass">123456</param>)
动态跳转:定义动态结果页面(如果登录名等于tom,跳到tom.html)定义成员变量typePath,并提供getset方法
全局result
1.包里配置<global-results>。当局部没有找到返回的字符串时,会去找全局,如果全局也找不到就生成默认错误响应。
2.如果局部全局都有,局部起作用。
异常处理
1.局部<exception-mapping result="result字符串" exception="java异常"/>
2.全局<global-exception-mappings>
<exception-mapping result="" exception=""/>
注意:当局部异常处理找不到才会找全局
3.异常信息显示
方式一:引用struts标签库,<s:property value="exception.message">和<s:property value="exceptionStack">
方式二:直接EL表达式:${exception.message}和${exceptionStack}
拦截器
自定义拦截器
1.写一个类继承AbstractInterceptor,重写intercept方法。
2.invocation.invoke()把控制权限交给下一个拦截器,没有的话交给action,返回值String就是提交给action执行的方法执行完以后返回的字符串
3.配置拦截器在package里,引用(使用)是在action里。
4.一旦自定了拦截器,默认的就不起作用。
5.拦截器的定义顺序决定了调用的顺序,最好先配置好默认的拦截器
6.如果有一堆的拦截器要用,可以配置一个拦截器栈
7.如果在一个package中不想每一个action都写引用拦截器,可以在package里定义默认拦截器<default-interceptor-ref name=""/>(本包有效)
方法拦截器
1.写一个类继承MethodFilterIntercepter,重写doIntercept方法。
2.引用时多一项参数<param name="includeMethods">login,logout</param>,指定哪些会调用拦截器(白名单)
3.如果白名单和黑名单冲突,白取胜(邪不压正)。
内置拦截器
timer:统计action方法执行耗时的拦截器
token:防止表单重复提交
1.在表单提交的action上调用默认拦截器<interceptor-ref name="token"/>
2.配置当出现重复提交时到什么页面<result name="invalid.token">error.html</result>
3.页面上用struts标签库,在form里加上<s:token/>,其他不变
登录检查
1.自定义一个拦截器
String result = "login";
if(flag){
HttpSession session = ServletActionContext.getRequest().getSession();
User u = (User)session.getAttribute("loginUser");
if(u != null){
result = invocation.invoke();
}
}else{
result = invocation.invoke();
}
return result;
2.配置(声明)拦截器
3.调用拦截器(先调用系统默认),里面还可以给拦截器参数(拦截器的成员变量要有getset方法)
freemarker
使用要引入一个jar包和要在web.xml文件中做一个配置。
文件上传下载(注意这里:配置跟default.properties有关!)开始是在struts2-core-2.3.15.1.jar这个jar文件里的
一、单个文件上传
上传的form表单的method属性必须是post,enctype必须是multipart/form-data
1.页面的表单<form action="upload.do" method="post" enctype="multipart/form-data">
<input type="file" name="headImg">
<input type="submit" value="上传"/>
</form>
这时要配置action:
<package name="user" extends="struts-default" >
<action name="upload" class="org.fkit.action.UserAction" method="upload">
<result name="upload">success.jsp</result>
<result name="input">error.jsp</result>
</action>
</package>
注意这里action配置没有命名空间,如果有,表单提交action要加命名空间。??
2.UserAction里
2.1增加属性
private File headImg;//接收文件用的属性(可以跟表单的不一致,因调的是set方法)
private String headImgFileName;
private String headImgContentType;
//三个属性要增加getset方法
说明:这样写才能获取到文件名和文件类型
2.2.upload方法里
//获取服务器的资源(存储)路径
String path = ServletActionContext.getServletContext().getRealPath("p_w_picpaths");
File file = new File(path,headImgFileName);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
FileUtils.copyFile(headImg, file);
2.3如想方便查看上传的文件,可设置上传路径:双击tomcat,在ServerLocations那里,
选中间那一栏(使用tomcat安装路径)。
二、多个文件上传
属性改为数组,循环new File,其他基本不用变。
上传约束配置
1.全局大小限制:struts.xml里,配置整个工程:<constant name="struts.multipart.maxSize" value="数值">
2.局部大小限制:局部的大小限制必须小于全局大小限制!在对应的action下配置<interceptor-ref name="fileUpload"/>拦截器,拦截器里可以设置参数:
<param name="maximumSize">数值</param>
注意点:建议把默认的拦截器放在上传约束拦截器下面。
3.类型约束:<param name="allowedTypes">p_w_picpath/pjpeg,p_w_picpath/x-png</param>
4.后缀约束:<param name="allowedExtensions">.jpg,.png,.bmp</param>
三、下载
1.struts-user.xml文件配置下载的action:
<package name="user" namespace="/" extends="struts-default">
<action name="download" class="org.fkjava.action.UserAction">//没配method默认执行execute方法
<result name="success" type="stream">
<param name="contentType">application/octet-stream</param>
<param name="inputName">fileStream(要与Action类getset方法对应)</param>
<param name="contentDisposition">p_w_upload;filename="${fileName}"</param>//对应action类fileName属性get方法
</result>
</action>
</package>
2.在UserAction类增加getFileStream方法
public InputStream getFileStream(){
return ServletActionContext.getServletContext().getResourceAsStream("/p_w_picpaths/"+fileName);
}
还要增加fileName属性,提供getset方法,在页面上增加这个参数。
3.中文乱码:在取流的方法里,return之前加上这句(getResource里的变量毫无疑问改成s):
String s = new String(fileName.getBytes("ISO-8859-1"),"UTF-8");
4.下载页面:
<a href="download.do?fileName=mm.jpg">下载1</a>
OGNL表达式
<s:debug/>标签:用来调试程序
值栈(valueStack):取值栈里的值:${user.userName}或者<s:property value="user.userName"/>
栈(stack)中获取数据要加#号:<s:property value="#request.r"/>或者${requestScope.r}或者${r}(先找小范围,再找大范围,如果小范围找到,不会再找大范围)
访问对象属性中还是对象:<s:property value="user.course.name"/>即:对象.对象.属性
访问方法:调用值栈中对象的普通方法:<s:property value="user.sayHello()"/>
开启允许静态方法调用:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
取值栈的一个例子:
<td><s:radio theme="simple" list="#{'1':'男','2':'女'}" id="sex" name="vo.sex" value="%{#request.vo.sex}"></s:radio></td>
Struts2标签
1.控制标签
1.1-iterator标签操作Map:
方式一:操作简单的Map:
value是json数据,var是临时变量,显示用<s:property value="#var里的变量.key">,<s:property value="#var里的变量.value">
方式二:后台准备数据,
Map<String, Object> request = ac.getContextMap();
Map<String, String> maps = new HashMap<>();
maps.put("A","中国");
maps.put("B","美国");
maps.put("C","英国");
request.put("maps",maps);
页面:value="#request.maps",其他不变
1.2—iterator标签操作List:
方式一:这时value不用#号{'a','b','c'},显示就去掉.key和.value,其他一样。
方式二:List<User> userList = new ArrayList<>();
userList.add(new User("admin",12,new Date()));
userList.add(new User("admin2",13,new Date()));
request.put("userList",userList);
页面:value="#request.userList" var="l",显示<s:property value="#l.userName"/>
iterator标签进行数据过滤操作
?获取满足条件的所有数据
value="#request.userList.{?#this.userPass %2 != 0}"//意思是判断当前迭代对象的userPass与2取余不为0就输出
^获取满足条件的第一条数据
value="#request.userList.{^#this.userPass %2 != 0}"
$获取满足条件的最后一条数据
value="#request.userList.{$#this.userPass %2 != 0}"
if...else if...else
<s:if test="attr.number % 7 == 0">
输出语句
</s:if>
<s:elseif test="attr.number % 5 == 0">
输出语句
</s:elseif>
<s:else>
输出语句
</s:else>
append集合合并标签:把两个list拼接在一起放在newList里
<s:append var="newList">
<s:param value="{1,2,3}"/>
<s:param value="#userList"/>
</s:append>
<s:iterator value="#newList" var="nl">
<s:property value="#nl"/><br>
</s:iterator>
generator字符串分割标签:分割完是一个数组g
<s:generator separator="," val="'a,b,c'" var="g">
<s:iterator value="#g" var="nl">
<s:property value="#nl"/><br>
</s:iterator>
subset获取子集合
方式一:在里面迭代
<s:subset source="{1,2,3,4,5}" start="2" count="2">
<s:iterator var="s">
<s:property value="#s"/><br>
</s:iterator>
</s:subset>
方式二:在其他地方迭代
<s:subset source="{1,2,3,4,5}" start="2" count="2" var="subs"/>
<s:iterator value="#attr.subs" var="s">
<s:property value="#s"/><br>
</s:iterator>
2.数据标签
set标签:指定把某值用var的变量名字设置到某个范围
<s:set value="要存的值" var="myVar" scope="session"/>
<s:property value="#session.myVar"/>
push标签:将某个值置于值栈的顶部,标签结束后,将从值栈中移除。
<s:push value="'push中的数据'">
<s:property/>
</s:push>
bean标签:可以在页面创建一个对象
<s:bean name="org.fkit.bean.User" var="u">
<s:param name="userName">tom</s:param>
<s:param name="userPass">123456</s:param>
</s:bean>
<s:property value="#u.userName"/>--<s:property value="#u.userPass"/>
date标签:
<%
request.setAttribute("now",new java.util.Date());
%>
<s:date name="#request.now" format="yyyy年MM月dd日HH时mm分ss秒"/>
include标签
================================
4、struts2的处理流程
a、browser-->struts2控制器-->查找action(在配置文件找)-->(回到)struts2控制器-->拦截器1,2,3...-->动作Action-->(request)struts2控制器-->查找视图(配置文件找)-->struts2控制器-->视图-->browser
一个请求在Struts2框架中的处理大概分为以下几个步骤
1 客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5 ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6 ActionProxy创建一个ActionInvocation的实例。
7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper
在上述过程中所有的对象(Action,Results,Interceptors,等)都是通过ObjectFactory来创建的。
5、Struts2配置文件
a、可以给每个模块定义一个包(Package元素的namesapce属性)
b、继承某个package(Package元素的extends属性)
c、为了让大型应用的struts.xml更好管理,可以把它分成几个较小的文件ps:
<include file="user.xml"></include>
6、拦截器是什么?struts-default.xml
是将控制器不做的事情封装在另外一个地方
a标签路径最好是通过绝对路径来:request.getContextPath()
接收参数三种方式:
1.Action属性来接收
2.用VO来封装表单属性
3.利用ModelDriven模型驱动来接收
要实现 implements ModelDriven<T>接口,Action里面重写getModel()方法。
==========填充下拉列表==========
第一种:
<s:select list="#{1:'男',2:'女'}" name="employee.sex" headerKey="0" headerValue="--请选择性别--"></s:select>
第二种:
先定义private List<Map<String,Object>> jobs;
<s:select list="jobs" name="employee.job.id" headerKey="0" headerValue="--请选择职位--" listKey="id" listValue="name"></s:select>
第三种:
$.ajax({
url : "${ctx}/dept/loadDeptAjax.action",
type:"post",
dataType:"json",
async : true,
success:function(data){
//获取选中的
var deptId = "${employee.dept.id}";
alert();
$.each(data,function(){
$("<option/>").val(this.id).text(this.name).attr("selected",this.id == deptId).appendTo("#deptSelect");
});
}
});
List<Map<String,Object>>dept = deptMapper.find(null);
========================Struts2========================
namespace
1、命名空间一般是以“/”开头
2.当namespace=""时,它会匹配所有的。
request.getScheme()+request.getServletName()+request.getServletPort()+path;
动态方法调用(DMI):
意思就是不配置method属性,在请求的时候这样:user!add
<action name="student*" class="xx" method="{1}">
<result >/student{1}_success.jsp</result>
</action>
<action name="*_*" class="{1}Action" method="{2}">
<result >/{1}_{2}_success.jsp</result>
</action>
参数接收:
1.普通类型
2.用DTO来接收
public class UserDTO{
private String name;
private String pwd;
private String cpwd;
}
3.模型驱动:
public class UserAction implements ModelDriven<User>{
private User user = new User();
public String add(){
return SUCCESS;
}
public User getModel(){
return user;
}
}
参数校验:
addFieldError("name","name is error!");
页面:
<s:fielderror fieldName="name" theme="simple"/>
(Map)ActionContext.getContext().get("request");
session = ActionContext.getContext().getSession();
在页面上拿,要用#key这种方式。
Result的配置
<result type="chain">
forward到另外一个action
全局result
<global-results>
<result name ="mainpage">/main.jsp</result>
</global-results>
动态结果集
<result>${r}</result>
遍历操作:
<s:iterator value="{1,2,3}">
</s:iterator>
<s:iterator value="{'aaa','bbb','ccc'}" var="x">
<s:property value="#x.toUpperCase()"> |
</s:iterator>
<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" />
</s:iterator>
遍历map
<s:iterator value="#{1:'aaa',2:'bbb',3:'ccc'}" >
<s:property value="key"> | <s:property value="value">
</s:iterator>
<s:iterator value="#{1:'aaa',2:'bbb',3:'ccc'}" var="x">
<s:property value="#x.key"> | <s:property value="#x.value"/>
</s:iterator>
struts2错误标签自动有样式:<s:fielderror /> errorMessage
可以.fielderror ul li{ list-style-type:none; }解决
struts2Filter -> 拦截器 -> xxAction
↓
struts2Filter <- 拦截器 <- xxAction
默认拦截器都配置在struts-default.xml
拦截器也是配置在struts.xml文件里,
<package name="webpub" extends="struts-default" namespace="/">
<interceptors>
<!-- 配置拦截器与拦截器栈 -->
<interceptor name="" class=""></interceptor>
<interceptor-stack name="customStack">
<interceptor-ref name="exceptionFilter"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="authority"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 配置默认的拦截器(拦截器栈) -->
<default-interceptor-ref name="customStack"></default-interceptor-ref>
<!-- 当我们在配置Action的时候,如果没有为某个Action指定具体的class值时,系统将自动引用<default-class-ref>标签中所指定的类。在Struts2框架中,系统默认的class为ActionSupport,该配置我们可以在xwork的核心包下的xwork-default.xml文件中找到。有特殊需要时,可以手动指定默认的class -->
<default-class-ref class=""></default-class-ref>
<!-- 当配置里的action都对应不上的时候,最后就会执行这个 -->
<default-action-ref name=""></default-action-ref>
</package>