SSH : Spring + Struts2 + Hibernate
三层架构(表示层,业务逻辑层,数据访问层) MVC模式 (Model View Controller)
分层原则:单向依赖,接口耦合
1、Struts2 = Struts + Webwork
2、搭建struts2开发环境
a>、到www.apache.org下载开发包
b>、以 apps/struts-blank.war为蓝本
c>、复制WEB-INF/lib下的jar包至工程构建路径
d>、在web.xml中添加struts2的前端控制器
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
e>、在src目录中创建一个名称为struts.xml的配置文件
3、让配置文件产生提示
4、让一个DIV页面居中
#continer {
text-align : left;
width : 1000px;
height : 400px;
border : 1px solid red;
/*居中显示*/
position : absolute;
left : 50%;
margin-left : -500px;
}
-------- 或 -------------------
#continer {
text-align : left;
width : 1000px;
height : 400px;
margin : 0px auto; /*firefox居中*/
}
body {
padding : 0px;
margin : 0px;
text-align : center; /*IE居中*/
}
5、控制器的职责
/**
* 控制器的处理流程
* 1、获取请求参数
* 2、验证请求参数
* 3、处理用户请求(访问业务组件)
* 4、处理异常
* 5、为视图准备数据(将数据放入模型[model])
* 6、选择视图生成响应
*/
6、Action不是Servlet
7、Struts2的Action每次请求创建一个
8、输入校验
a>、Action继承ActionSupport,覆盖validate()方法
b>、在参数格式不满足条件是调用addFieldError();
c>、只有调用过addFieldError()方法,代表验证失败,如果验证失败不会再执行execute()方法
d>、校验失败时action的执行结果为input,需要在struts.xml中添加name为input的result
e>、在jsp页面中要显示验证失败的错误消息,需要使用struts2的标签<s:fielderror/>
9、struts2框架自身可能产生5种result
success
none
error
input
login
10、捕获请求参数
a>、将请求参数设置为Action的属性
<tr>
<td width="70" align="right">用户名:</td>
<td>
<input type="text" name="username" />
</td>
</tr>
<tr>
<td align="right">密码:</td>
<td>
<input type="password" name="password" />
</td>
</tr>
public class Reg1Action extends ActionSupport {
private String username;
private String password;
}
b>、直接将请求参数封装为对象
<tr>
<td width="70" align="right">用户名:</td>
<td>
<input type="text" name="user.username" />
</td>
</tr>
<tr>
<td align="right">密码:</td>
<td>
<input type="password" name="user.password" />
</td>
</tr>
public class Reg1Action extends ActionSupport {
private User user;
}
c>、使用模型驱动(ModelDriven)
<tr>
<td width="70" align="right">用户名:</td>
<td>
<input type="text" name="username" />
</td>
</tr>
<tr>
<td align="right">密码:</td>
<td>
<input type="password" name="password" />
</td>
</tr>
public class Reg1Action extends ActionSupport implements ModelDriven<User> {
private User user;
public User getModel() {
if(user == null) {
user = new User();
}
return user;
}
}
默认情况下,请求参数是绑定至Action对象的属性,如果Action实现了ModelDriven接口,请求参数将不再绑定至Action,而是
getModel()方法返回的对象上
11、Action如何向JSP传递数据
a>、使用Action的属性
private String msg;
public String getMsg() {
return msg;
}
@Override
public String execute() throws Exception {
msg = "用户注册成功!";
return "message";
}
b>、使用ActionContext
public String execute() throws Exception {
new UserServiceBean().save(user); // 执行保存
ActionContext.getContext().put("msg", "@@用户注册成功");
return "message";
}
12、访问Servlet api
HttpServletRequest :
getParameter() : 获取请求参数
getParameterValues()
getHeader("")
getContextPath()
getCookies()
setAttrbute(key,value) : 共享数据
getRequestDispatcher().forward() : 转发
HttpServletResponse
addHeader() : 添加响应头
addCookie() : 添加Cookie
sendRedirect() : 重定向
HttpSession
get/setAttribute()
invalidate() : 销毁session
ServletContext
get/setAttribute()
getRealPath()
a>、如果只是为了共享数据,可以使用Map
aa>、直接获取
Map<String, Object> session = context.getSession(); //获取用于存储数据的map对象
Map<String, Object> application = context.getApplication();
String company = (String) session.get("company");
session.put("data", "a session scope data");
application.put("data", "a application scope data");
bb>、依赖注入
public class ServletApi2Action extends ActionSupport implements RequestAware,SessionAware,ApplicationAware {
private Map<String,Object> request;
private Map<String,Object> session;
private Map<String,Object> application;
@Override
public void setRequest(Map<String, Object> request) {
System.out.println("call setRequest()");
this.request = request;
}
@Override
public void setApplication(Map<String, Object> application) {
this.application = application;
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
}
b>、如果要执行其它操作,则必须使用原生对象
aa>、直接获取
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
HttpSession session = request.getSession();
ServletContext application = ServletActionContext.getServletContext();
bb>、依赖注入
public class ServletApi4Action extends ActionSupport implements
ServletRequestAware, ServletResponseAware, ServletContextAware {
private HttpServletRequest request;
private HttpServletResponse response;
private HttpSession session;
private ServletContext application;
@Override
public void setServletContext(ServletContext application) {
this.application = application;
}
@Override
public void setServletResponse(HttpServletResponse arg0) {
this.response = arg0;
}
@Override
public void setServletRequest(HttpServletRequest arg0) {
this.request = arg0;
this.session = request.getSession();
}
}
13、一个Action处理多个请求
a>、一个Action类有多个与execute()相似签名的方法,每个方法处理一个请求
public class UserAction extends ActionSupport {
public String save() throws Exception {
return "message";
}
public String update() throws Exception {
return "message";
}
public String delete() throws Exception {
return "message";
}
}
b>、从struts2.3开始不可使用
<a href="userMgr!save.action">添加用户</a>
<a href="userMgr!update.action">修改用户</a>
<a href="userMgr!delete.action">删除用户</a>
c>、动态方法调用
<!-- {1}代表第一个*号的值 -->
<action name="user_*" class="com.sxt.struts2.web.action.UserAction" method="{1}">
<result name="message">/WEB-INF/jsp/message.jsp</result>
</action>
<a href="user_save.action">添加用户</a>
<a href="user_update.action">修改用户</a>
<a href="user_delete.action">删除用户</a>
d>、更复杂的示例
User_add.action,Employee_delete.action,Dept_query.action
<action name="*_*" class="xxx.action.{1}Action" method="{2}">
<result name="input">/WEB-INF/jsp/{1}/{2}.jsp</result>
</action>
14、struts.xml中的配置项
<struts>
<bean class=""/> <!--注册bean组件-->
<!-- 注册未知处理器 -->
<unknown-handler-stack>
<unknown-handler-ref name=""></unknown-handler-ref>
</unknown-handler-stack>
<include file="user.xml"/> <!-- 包含另一个配置文件-->
<!-- 修改struts2工作中的一些设定 ,可修改的设定来自于struts2-core-2.x.xx.jar中org.apache.struts2包中default.properties-->
<constant name="struts.i18n.encoding" value="utf-8"></constant> <!-- post请求参数的字符集 -->
<!-- 抽象包中不能定义action -->
<!-- 一个action请求路径由 package的namespace + action的name属性组成 -->
<package name="default" extends="struts-default" abstract="false" namespace="/system/base">
<!-- 注册result解析器类 -->
<result-types>
<result-type name="sxt" class="com.sxt.struts2.result.TestResult"></result-type>
</result-types>
<!-- 注册拦截器 -->
<interceptors>
<interceptor name="test" class="com.sxt.struts2.web.interceptor.TestInterceptor"></interceptor>
<!-- 用于将多个拦截器组合成一个 -->
<interceptor-stack name="testStack">
<interceptor-ref name="test"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!-- 指定默认的拦截器 -->
<default-interceptor-ref name="testStack"/>
<!-- 指定默认的Action类 <action name="" > -->
<!-- <default-class-ref class=""></default-class-ref> -->
<!-- 指定默认的action -->
<default-action-ref name="default"/>
<!-- 全局result设定,可以被该包中的所有action使用 -->
<global-results>
<result name="message">/WEB-INF/jsp/message.jsp</result>
</global-results>
<!-- 全局的异常处理声明 -->
<global-exception-mappings>
<exception-mapping result="error" exception="java.lang.Exception"/>
</global-exception-mappings>
<action name="test">
<result>/WEB-INF/jsp/message.jsp</result>
</action>
<action name="result" class="com.sxt.struts2.web.action.ResultAction">
<!-- 当action的执行结果为a时,由sxt所对应的解析器类生成响应 -->
<result name="a" type="sxt">
<param name="location">/WEB-INF/jsp/message.jsp</param>
</result>
<!-- 声明Action需要使用哪些拦截器,一旦使用该元素,则默认拦截器不再有效 -->
<!--
<interceptor-ref name="test"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
-->
</action>
</package>
</struts>
15、result的设定 (决定如何生成响应)
<!-- 转发至一个jsp -->
<result name="a" type="dispatcher">
<param name="location">/WEB-INF/jsp/message.jsp</param>
</result>
<!-- 重定向至一个jsp -->
<result name="b" type="redirect">
<param name="location">/WEB-INF/jsp/message.jsp</param>
</result>
<!-- 转发至一个Action -->
<result name="c" type="chain">
<param name="namespace">/aaa</param>
<param name="actionName">other</param>
</result>
<!-- 重定向至一个Action -->
<result name="d" type="redirectAction">
<param name="namespace">/aaa</param>
<!--动态传参 -->
<param name="actionName">other?username=${name}</param>
</result>
<!-- 文本响应 -->
<result name="e" type="plainText">
<!-- Content-Type : text/plain;charset=utf-8 -->
<param name="location">/WEB-INF/jsp/message.jsp</param>
<param name="charSet">utf-8</param>
</result>
a>、转发操作,向下一个组件传值,直接将值存入ActionContext
ActionContext.getContext().put(key,value);
b>、重定位操作向下一个组件传值需要使用请求参数
16、拦截器(Interceptor)
a>、编写一个类,实现Interceptor接口或继承AbstractInerceptor,完成String interceptor(ActionInvocation ai)方法
public class TestInterceptor extends AbstractInterceptor /*implements Interceptor*/ {
/**
* 返回值为result
*/
@Override
public String intercept(ActionInvocation ai) throws Exception {
System.out.println("before...............");
String result = ai.invoke(); //放行
System.out.println("after.............\n");
return result;
}
}
b>、在struts.xml注册拦截器
<interceptors>
<interceptor name="test" class="com.sxt.struts2.web.interceptor.TestInterceptor"/>
</interceptors>
c>、在action元素中声明该action所需要的拦截器
<action name="aaa" class="xxx.XxxAction">
<!-- 声明Action需要使用哪些拦截器,一旦使用该元素,则默认拦截器不再有效 -->
<interceptor-ref name="test"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
17、国际化
a>、实现步骤
aa>、将页面中要显示的文字用特定的key编号后写到国际化资源文件中(可以有很多份,每份对应一个Locale,key相同,对应的文字不相同)
Message.properties,Message_zh_CN.properties,Message_en_US.properties
bb>、在struts.xml中注册国际化资源
<constant name="struts.custom.i18n.resources" value="Message"/>
cc>、在jsp页面中,使用s:text标签输出国际化消息
<s:text name="login.title"/>
b>、用户切换语言
defaultStack中有一个名字为i18n的拦截器,它会检查请求参数中是否有一个名字为request_locale的请求参数,
如果有,则将该参数的值为转换为对应的Locale对象,并存入session,作为国际化的依据
<a href="i18n_login.action?request_locale=zh_CN">中文</a>
<a href="i18n_login.action?request_locale=en_US">English</a>
c>、在Action中要获取国际化消息使用getText(key)方法
d>、struts2的国际化资源文件分为四种类型
aa>、全局,在struts.xml注册的
bb>、包范围,在action所在包中名字package的国际化资源文件: package.properties,package_zh_CN.properties
cc>、Action范围,在action所在包中名字为Action名字的国际化资源文件: LoginAction.properties
dd>、临时的,在使用时使用<s:i18n/>标签绑定的国际化资源文件
<s:i18n name="com.sxt.struts2.resource.Hello">
<li> <s:text name="i18n.key2"></s:text> </li>
</s:i18n>
ee>、优先级为 : Action范围 > 包范围 > 全局范围
18、文件上传
a>、在struts2框架中,文件上传操作是由defaultStack栈中一个名称为fileupload的拦截器完成的,该拦截器会检查请求是否是
一个文件上传请求,如果是就会自动完成上传,并保存至服务器端临时目录中的一个临时文件中
b>、前置条件
<form action="upload.action" method="post" enctype="multipart/form-data">
<input type="file" name="photo" />
</form>
c>、Action开发
public class UploadAction extends ActionSupport {
private File photo; // 指向了上传至服务端的临时文件
private String photoFileName; // 上传文件的文件名
private String photoContentType; // 上传文件的MIME类型
@Override
public String execute() throws Exception {
//文件上传
if(photo != null) {
ServletContext context = ServletActionContext.getServletContext();
String path = context.getRealPath("/WEB-INF/photos/" + photoFileName); //获取要保存文件的真实路径
File dest = new File(path);
//文件复制
FileUtils.copyFile(photo, dest);
}
return "message";
}
}
d>、控制文件上传的大小(默认最大2M)与类型
aa>、在default.properties中有一个常量
struts.multipart.maxSize=2097152 (产生actionError,使用<s:actionerror/>输出错误消息)
<constant name="struts.multipart.maxSize" value="20971520"></constant>
bb>、fileupload拦截器也定义了一个参数maximumSize (产生fieldError,使用<s:fielderror/>输出错误消息)
<action name="upload" class="com.sxt.struts2.web.action.UploadAction">
<result name="input">/WEB-INF/jsp/file_upload.jsp</result>
<interceptor-ref name="defaultStack" >
<param name="fileUpload.maximumSize">10485760</param>
</interceptor-ref>
</action>
cc>、如果上传失败,Action的执行结果为input,所以在action中应配置一个名称为input的result
dd>、控制上传文件的类型,修改fileUpload拦截器的allowedTypes参数的值
ee>、错误消息的本地化,在应用国际化资源文件中,重新定义以下消息key的值
struts.messages.error.file.too.large=\u4E0A\u4F20\u6587\u4EF6\u8D85\u8FC7\u6700\u5927\u5927\u5C0F!
struts.messages.error.content.type.not.allowed=\u6587\u4EF6\u683C\u5F0F\u4E0D\u6B63\u786E!
struts.messages.error.uploading =\u6587\u4EF6\u4E0A\u4F20\u51FA\u9519!
struts.messages.upload.error.SizeLimitExceededException=\u4E0A\u4F20\u6587\u4EF6\u8D85\u8FC7\u6700\u5927\u5927\u5C0F!
19、文件下载
a>、使用StreamResult即可
<action name="download" class="com.sxt.struts2.web.action.DownloadAction">
<result name="success" type="stream">
<!-- 设定Content-Type响应头的值 -->
<param name="contentType">application/octet-stream</param>
<!-- 设定通访问Action的哪个属性(类型为InputStream)可以得到要下载文件的数据 -->
<param name="inputName">fileData</param>
<!-- 设定Content-Disposition响应头的值,产生下载对话框 -->
<param name="contentDisposition">attachment; filename=${filename}</param>
</result>
</action>
b>、 Action中应有一个public InputStream getFileData(),在该方法中返回要下载文件的数据
public InputStream getFileData() {
if (filename == null) {
return null;
}
ServletContext context = ServletActionContext.getServletContext();
return context.getResourceAsStream("/WEB-INF/photos/" + filename);
}
c>、中文文件名的处理
aa>、获取请求参数时进行转码
public void setFilename(String filename) {
if (filename != null) {
try {
filename = new String(filename.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
this.filename = filename;
}
bb>、产生下载对话框中的中文文件名:attachment; filename=${filename}
public String getFilename() {
if (filename == null) {
return null;
}
//判断浏览器
HttpServletRequest request = ServletActionContext.getRequest();
String agent = request.getHeader("User-Agent");
try {
if(agent.contains("Firefox")) { //火狐
return "=?UTF-8?B?" + new BASE64Encoder().encode(filename.getBytes("utf-8")) + "?=";
} else {
return URLEncoder.encode(filename, "utf-8");
}
} catch (UnsupportedEncodingException e) {
return filename;
}
}
20、类型转换
a>、所有的请求参数都是String或String[]类型,但服务器端可能期望是其它类型,如Date,int等,这就涉及到类型转换,
Struts2框架可以自动处理类型转换,只要有对应的类型转换器(TypeConverter)
b>、类型转换失败,action执行结果为input,并会以xwork.default.invalid.fieldvalue为key产生fielderror,可以在input视图使用<s:fielderror/>输出类型转换失败的错误消息
c>、如果要改变错误消息,只需要在国际化资源文件中重新定义该key的值
d>、如果目标类型(User)所应的转换器不存在,则不能转换,此时可以开发自己的类型转换器
aa>、编写一个类实现TypeConverter接口,一般继承StrutsTypeConverter
public class AddressTypeConverter extends StrutsTypeConverter /*implements TypeConverter*/ {
/**
* 字符串转对象
*/
@Override
public Object convertFromString(Map context, String[] values, Class toClass) {
/*if(toClass == Address.class) {
}*/
if(values == null || values.length == 0) {
return null;
}
String sAddr = values[0]; //获取请求参数的值
//将请求参数转换为Address类的对象
String[] aAddr = sAddr.split(",");
if(aAddr.length != 3) {
throw new IllegalArgumentException("请求参数addr格式不正确!");
}
return new Address(aAddr[0], aAddr[1], aAddr[2]);
}
/**
* 对象转字符串
*/
@Override
public String convertToString(Map context, Object o) {
if(o == null) {
return null;
}
Address addr = (Address)o;
return "province = " + addr.getProvince() + ",city = " + addr.getCity() + ",area= " + addr.getArea();
}
}
bb>、注册类型转换器
aaa>、局部的,只对某一Action有效,在Action所在包中定义一个名字为ActionName-conversion.properties
addr = com.sxt.struts2.web.converter.AddressTypeConverter
bbb>、全局的,对所有Action都有效,在classpath的根下定义一个名字为xwork-conversion.properties的文件
com.sxt.struts2.model.Address = com.sxt.struts2.web.converter.AddressTypeConverte
=====================================================================================================================
21、输入校验(服务器端的校验)
a>、覆盖validate()方法,编码校验
aa>、一个Action只处理一个请求,只需要覆盖validate()方法
bb>、一个Action处理多个请求,不同的请求有不同的校验规则,对应不同的验证方法
处理请求的方法为 save(),则对应的验证请求参数的方法为 validateSave()
如果有validate()方法,则任何一个请求都会执行
b>、使用验证框架,写配置文件实现校验(Action中不写validate()方法)
aa>、一个Action只处理一个请求
在Action类所在包装创建一个名称为: Action类名-validation.xml(如:RegAction-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 name="username"> <!-- 请求参数 -->
<!-- 验证规则,可用规则来自于xwork-core.xxx.xx.jar中com.opensymphony.xwork2.validator.validators包下default.xml -->
<field-validator type="requiredstring">
<message>@@用户名不能为空!</message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">5</param>
<param name="maxLength">10</param>
<message>@@用户名的长席必须在5~10位之间</message>
</field-validator>
</field>
</validators>
bb>、一个Action处理多个请求,不同的请求有不同的校验规则
假设Action类名为: Reg4Action,添加操作请求的action的名字为reg4_save.action,则验证文件的名称为:Reg4Action-reg4_save-validation.xml
22、OGNL : Object Graph Navigation Language(对象图导航语言)
a>、ognl类似于EL,其作用就是进行表达式求值,主要用于struts2的标签
JSP的开发: EL + JSTL
OGNL + struts2 tag
b>、ognl中的变量求值
<s:property value="a + b"/>
aa>、EL表达式中的变量值来自于四个作用域
bb>、OGNL表达式中的数据存储 ValueStack与Stack Context(在struts2中为ActionContext)
aaa>、ValueStack中存入的是对象
bbb>、Stack Context是一个Map结构,存放的是一些key -> value格式的数据
ccc>、变量名以#开头,以变量名为key,从Stack Context中取数据
ddd>、变量名不以#开头,从ValueStack栈顶对象开始,依次检栈中对象是否有该属性,有则返回该属性的值,如果栈中对象都没有该属性,再以变量名为key,从Stack Context中找
<s:property value="username"/> : 先检查栈顶对象是否有username属性,有则返回该属性的值,若没有,检查栈的下一个对象
eee>、当请求到来,Struts2框架会把请求的Action对象放在值栈的栈顶
c>、当每次请求到来时,Struts2框架会为本次请求初始化一个ActionContext对象,
并将与请求处理相关的数据(请求参数,Action对象,作用域中的数据)全部存储在ActionContext中
d>、Stack Context默认有以下key的数据
request : 所有request作用域的数据
session : 所有session作用域的数据
application : 所有application作用域的数据
attr : 以上三个作用域的数据
parameters : 所有请求参数
action : 当前请求的Action对象
e>、在struts2中为什么EL表达式可以访问Action的属性
因为在struts2框架中,request对象被做了包装
正常情况下: request.getAttribute() 只能获取 request.setAttribute()的值
改写之后,使得request.getAttribute()不但可以访问request.setAttribute()的数据,还可以访问ValueStack与stack context中的数据,查找过程等同于不加#
简单的说在struts2中: ${requestScope.username} = request.getAtribute("username") + <s:property value="username"/>
22、Struts2的标签库
a>、非UI标签
<s:head/> : 导入标签库所需的css,js
<s:fielderror/> : 输出addFieldError()的消息
<s:actionerror/> : 输出addActionError()的消息
<s:actionmessage/> : 输出addActionMessage()的消息
<s:i18n name="com.sxt.resource.Message"/> : 绑定国际化资源文件
<s:text /> : 输出国际化消息
<s:property value="ognl"/> : 输出一个OGNL表达式的求值结果,不指定value属性,输出栈顶对象
<s:debug /> : 查看ValueStack与Stack Context中数据
<s:a action="tag2" namespace="/">UI标签示例</s:a> : 生成一个超链接
<s:a value="/index.jsp?username=%{#user1.username}">首页</s:a> : %{} : 强制将其中的内容作为ognl表达式解析
<s:action name="tag3" executeResult="false"/> : 调用一个Action
<s:bean name="com.sxt.struts2.bean.User" var="user1" /> :创建一个bean对象,指定var,将创建的对象放入Stack context中,不指定,放入ValueStack,并在标签结束是从ValueStack中删除
<s:param name="username">张三</s:param> : 设置参数值
<s:param name="username" value="#username"/> : value属性是一个ongl表达式
<s:date /> : 格式化日期
<s:number /> : 格式化数字
<s:include value="/index.jsp" /> : 包含另一个页面
<s:push/> : 将一个对象放至ValueStack的栈顶,并在标签结束时删除
<s:set /> :向stack context或作用域中放值
<s:url /> : 生成一个url
<s:if/> <s:elseif/> <s:else/> : 分支选择
<s:iterator/> : 循环
b>、UI标签
<s:form>
<s:hidden />
<s:textfield/>
<s:password/>
</s:form>