1.1 Struts2中的拦截器
1.1.1 Struts2的拦截器
Struts2 拦截器在访问某个 Action 方法之前或之后实施拦截, Struts2 拦截器是可插拔的, 拦截器是 AOP 的一种实现.
1.1.2 Struts2的执行原理:
1.1.3 自定义拦截器:
登录的案例:
* 数据封装:
* 数据校验:
* 国际化信息:
* 拦截器:
编写拦截器:
* 实现Interceptor接口或者继承AbstractInterceptor
* 重写intercept方法.
注册拦截器:
* 两种方式:
1.配置拦截器:
<interceptors>
<!-- 定义拦截器 -->
<interceptor name="privilege" class="cn.itcast.interceptor.PrivilegeInterceptor"/>
</interceptors>
在<action>中指定使用那些拦截器:
<action name="book_*" class="cn.itcast.action.BookAction" method="{1}">
<result name="input">/jsp/login.jsp</result>
<!-- 使用拦截器 -->
<!-- 使用自定义的拦截器之后,Struts2中的默认拦截器栈就不执行了. 必须手动的执行默认拦截器栈.-->
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="privilege"/>
</action>
2.配置拦截器栈:
<interceptors>
<!-- 定义拦截器 -->
<interceptor name="privilege" class="cn.itcast.interceptor.PrivilegeInterceptor"/>
<!-- 定义拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="privilege"/>
</interceptor-stack>
</interceptors>
在<action>的标签中引用自定义的拦截器栈:
<action name="book_*" class="cn.itcast.action.BookAction" method="{1}">
<result name="input">/jsp/login.jsp</result>
<!-- 引用自定义的拦截器栈就可以了 -->
<interceptor-ref name="myStack"/>
</action>
1.2 Struts2文件上传
1.2.1 文件上传原理:
文件上传:将客户端本地文件 写 到服务器上.
文件上传的组件:
* JSPSmartUpload:适用Model1阶段,适用在JSP+JavaBean年代.
* FileUpload:Apache下的,文件上传的组件.
* Servlet3.0:完成文件的上传.
* Struts2:完成文件的上传.
文件上传必要的条件:
* 1.请求的方式需要是POST.
* 2.表单的表单项中需要<input type=”file”>文件项,而且文件项需要有name属性,且name属性必须有值
* <input type=”file” name=”upload”>
* 3.表单的enctype属性需要是multipart/form-data
1.2.2 Struts2中的文件上传:
页面中:
<h1>文件上传</h1>
<form action="${ pageContext.request.contextPath }/upload.action" method="post" enctype="multipart/form-data">
文件上传:<input type="file" name="upload"><br/>
<input type="submit" value="上传">
</form>
Action中如何进行接收:
* 只需要在Action中,提供三个属性,属性的名称需要跟页面的文件项的name属性有关:
* 提供一个文件类型的属性:用于接收上传文件.
* private File upload;
* 提供一个字符串类型的属性:用于接收上传的文件类型.
* private String uploadContentType;
* 提供一个字符串类型的属性:用于接收上传的文件名称.
* private String uploadFileName;
1.2.3 文件上传过程中的错误处理:
* 上传一个超过2MB的文件
1.配置INPUT逻辑视图:
2.在JSP中使用<s:actionerror/>回显错误信息.
* Request exceeded allowed size limit! Max size allowed is: 2,097,152 but request was: 68,800,543!---文件大小超过限制.
3.文件大小的设置在常量中:
struts.multipart.parser=jakarta----Struts2底层解析文件上传使用解析器.
struts.multipart.saveDir=----文件上传过程中产生临时文件,临时文件存放的路径.
struts.multipart.maxSize=2097152----文件上传的总大小.
文件上传的功能是由谁完成的?----拦截器.
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
查看拦截器的源代码:
* 拦截器中提供了一些属性:
* 设置文件上传的大小:
public void setMaximumSize(Long maximumSize) {
this.maximumSize = maximumSize;
}
* 设置文件上传文件的类型:
public void setAllowedTypes(String allowedTypes) {
allowedTypesSet = TextParseUtil.commaDelimitedStringToSet(allowedTypes);
}
* 设置文件上传文件的扩展名:
public void setAllowedExtensions(String allowedExtensions) {
allowedExtensionsSet = TextParseUtil.commaDelimitedStringToSet(allowedExtensions);
}
* 在struts.xml中配置fileUpload拦截器的参数:
<action name="upload" class="cn.itcast.action.demo1.UploadAction">
<result name="input">/demo1/upload.jsp</result>
<!-- 引用fileUpload拦截器 -->
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">2097152</param>
<param name="fileUpload.allowedExtensions">.doc,.jpg</param>
</interceptor-ref>
</action>
多文件上传:
提供三个属性的数组:
private File[] upload;
private String[] uploadContentType;
private String[] uploadFileName;
1.3 文件的下载:
1.3.1 文件的下载:
文件的下载:将服务器上的文件 传输 到客户端.
文件下载:
* 1.超链接形式:
* 2.编码的方式:
* 设置两个头和一个流:
* Content-Type:文件的MIME的类型
* Content-Disposition:浏览器支持该格式的文件,以下载形式提供下载.
* 提供一个文件的输入流:输出流的是固定的response.getOutputStream();
1.3.2 Struts2中的文件的下载:
* 结果页面类型
<result name=”” type=””>
* type:
* stream:----用来做文件的下载.
* 在struts.xml中进行配置:
<package name="download" extends="struts-default">
<action name="download" class="cn.itcast.action.demo2.DownloadAction">
<result name="success" type="stream">
<!-- 基于值栈结构: -->
<param name="contentType">${contentType}</param>
<param name="contentDisposition">attachment;filename=${fileName}</param>
<!-- <param name="inputStream">${inputStream}</param> -->
</result>
</action>
</package>
1.4 Struts2中OGNL表达式及值栈:
1.4.1 OGNL:
OGNL:表达式语言
* OGNL是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。
* 比EL强大很多倍.
* OGNL本身就是一个开源项目,在Struts2中引入OGNL作为其表达式.
OGNL功能:
1、支持对象方法调用,如xxx.doSomeSpecial();
2、支持类静态的方法调用和值访问,表达式的格式:
@[类全名(包括包路径)]@[方法名 | 值名],例如:
@java.lang.String@format('foo %s', 'bar')
或@tutorial.MyConstant@APP_NAME;
设置 struts.ognl.allowStaticMethodAccess=true
3、访问OGNL上下文(OGNL context)和ActionContext;访问值栈
4、支持赋值操作和表达式串联,如price=100, discount=0.8,
calculatePrice(),这个表达式会返回80;
5、操作集合对象。
1.4.2 值栈:
以前的操作:在Servlet中向jsp传递值!!!
* 向request/session/application域中存值.在JSP页面中从域中取值.
在Struts2中一般会将这个值保持在一个结构叫做值栈对象中.
* 在JSP页面中使用OGNL获取值.---Struts2的内部结构中有很多地方依赖了值栈.
问题:
什么是值栈?
值栈的内部结构?
值栈ValueStack 和 ActionContext关系? --- 值栈的创建
如何获得值栈对象?
向值栈保存数据?
在页面通过struts2标签 获取值栈内容?
EL为什么能访问值栈中的数据?
值栈:ValueStack(值栈): 贯穿整个 Action 的生命周期(每个 Action 类的对象实例都拥有一个ValueStack 对象). 相当于一个数据的中转站. 在其中保存当前 Action 对象和其他相关对象.
* Action生命周期:
* Servlet单例的.所有的请求访问LoginServlet,创建一个LoginServlet的对象.(线程安全的问题.)
* Action多例的.每一次访问LoginAction,服务器创建一个LoginAction的对象.(没有线程安全问题.)
* 每一个Action的对象的实例都会有一个ValueStack.
* Struts 框架把 ValueStack 对象保存在名为 “struts.valueStack” 的请求属性中,request中
* request.setAttribute(“struts.valueStack”,valueStack);
什么是值栈:
ValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类.贯穿整个 Action 的生命周期(每个 Action 类的对象实例都拥有一个ValueStack 对象). 相当于一个数据的中转站
值栈内部结构:
值栈中有两块存储空间:
* root:compoundRoot其实就是一个ArrayList.
* context:OgnlContext其实就是一个Map.context中有root的引用,request/session/application/parameters/attr对象引用.
值栈与ActionContext的关系:
每一个Action都有一个属于自己的ValueStack对象.ActionContext是Action的上下文.从ActionContext中获取到值栈对象.
* 执行doFilter的时候:
* prepare.createActionContext(request, response);---创建ActionContext.
* 跟踪这个方法:
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();---创建一个值栈对象
ctx = new ActionContext(stack.getContext());---将值栈的对象的上下文传递到ActionContext中.
* 所有从ActionContext中获得到ValueStack对象.
获得值栈对象:
// 1.通过ActionContext对象获得值栈
ValueStack stack1 = ActionContext.getContext().getValueStack();
// 2.通过request域对象获得值栈.
ValueStack stack2 = (ValueStack) ServletActionContext.getRequest()
.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
System.out.println(stack1 == stack2);
如何操作值栈对象:
操作值栈:
* 指的是操作root的那部分!!!
* 操作context需要操作request/session/application对象
调用方法操作:
* push(Object obj);
* set(String name,Object obj);
***** 但是一般情况下不手动调用push或set也可以!!!
* 因为Action这个类默认就在栈中!!!如果Action有一个属性呢?属性必然会在值栈中.操作的时候只需要在Action中提供一个属性.并且对这个属性提供get方法就可以在页面中获得了.
* 如果Action实现了ModelDriven接口的时候,Action默认不在栈顶了,ModelDriven中的对象会在栈顶!!!
页面中获取值栈的数据:
<s:property value=”OGNL表达式”/>
* <s:property value=”name”>
* <s:property value=”[0].top”>
EL获得值栈的数据:
public class StrutsRequestWrapper extends HttpServletRequestWrapper {
/**
* The constructor
* @param req The request
*/
public StrutsRequestWrapper(HttpServletRequest req) {
super(req);
}
/**
* Gets the object, looking in the value stack if not found
*
* @param s The attribute key
*/
public Object getAttribute(String s) {
if (s != null && s.startsWith("javax.servlet")) {
// don't bother with the standard javax.servlet attributes, we can short-circuit this
// see WW-953 and the forums post linked in that issue for more info
return super.getAttribute(s);
}
ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(s);
if (ctx != null) {
if (attribute == null) {
boolean alreadyIn = false;
Boolean b = (Boolean) ctx.get("__requestWrapper.getAttribute");
if (b != null) {
alreadyIn = b.booleanValue();
}
// note: we don't let # come through or else a request for
// #attr.foo or #request.foo could cause an endless loop
if (!alreadyIn && s.indexOf("#") == -1) {
try {
// If not found, then try the ValueStack
ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);
ValueStack stack = ctx.getValueStack();
if (stack != null) {
attribute = stack.findValue(s);
}
} finally {
ctx.put("__requestWrapper.getAttribute", Boolean.FALSE);
}
}
}
}
return attribute;
}
}
***** 增强request的getAttribute的方法:
* 如果域中有值从域中获取把值返回,如果域中没有这个值,从值栈中找是否有这个值,如果有就返回.