从IO 到框架(6)-Struts2

第一个正式框架Struts2,对"从IO 到框架(4)-Servlet + JDBC (Idea Maven)" 的第二次迭代。

0)三大块框架


1)Struts2 官方流程图

ActionMapper (ActionMapping) 负责识别当前的请求需不需要struts2 处理(过滤掉静态资源);

Interceptors 有18个(在栈中),做表单数据封装等(代替request.getParameter() );拦截器等组件位于struts-default 包。


2)Hello World, Struts 2 与原生Servlet 的对比:

    1. web.xml 配置:struts2 每个项目只配一个filter,而每个servlet 都需要配一个servlet 及其映射。

    StrutsPrepareAndExecuteFilter 中的init() 方法会解析struts.xml, 

<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>

    2. struts.xml (通过Class.forName() 反射获得Action 类的Class 对象) 和Action 类(很好地解耦了) 共同完成Servlet 实现类的控制功能。如下图Struts 2的Facets 没设置的话,“struts-default" 会报错。


    配置文件支持动态方法调用和使用通配符(wildcard) 的功能,但不安全,所以不推荐使用。

public class HelloAction {
	public String hello(){
		System.out.println("HelloAction.hello()");
		return "success";
	}
}
<package name="default" namespace="/" extends="struts-default">
        <!-- 默认的action(也需要定义),如果访问的action找不到就会执行默认的action -->
	<default-action-ref name="defaultAction"/>
        <action name="defaultAction" class="com.qf.action.UserAction" method="defaultAction">
		<result>ok.jsp</result>
	</action>
        <action name="hello" class="com.qf.action.HelloAction" method="hello">
            <result>ok.jsp</result>
        </action>
</package>

    3. Action 类可以实现Action 接口,实现默认的execute() 方法;

        最常用的是继承ActionSupport类,此类有表单验证(validate())、国际化(getText())、Token 等功能。返回值用类名点出来,可避免手误: Action.SUCCESS。

    4. 调用:struts2 只指向方法即可跳转(简化事件分发),servlet 需要指明servlet类名以及方法。

    struts2 每次请求都会创建一个新的Action 对象,属于多例模式;而servlet 是单例模式。

<a href="hello">Hello Struts2</a>
<a href="HelloServlet?method=hello">Hello Servlet</a>

    5. 简化页面提交参数获取 3种方式:

  • 在Action 类中声明与表单中参数名称相同的成员变量,并为其生成getter & setter 方法,即可在Action 类中直接得到页面参数;而servlet 需要用request 获取参数。
  • (常用) 参数多时封装实体类,action中定义对象来接收,表单中写 "对象.属性"。
  • (常用) 模型驱动(常用)
    a)实现ModeDriver<Entity>接口,复写里面的方法,该方法返回对象
    b)表单中直接写对象的属性
    c)原理:调用loign方法之前先调用getMode(),把该方法的返回值压倒栈顶,然后由拦截器来给栈顶
    的对象赋值。赋值成功后才会调用login方法

    6. Session 向页面传参:

		ActionContext context = ActionContext.getContext();
		Map<String, Object> session = context.getSession();
		session.put("user", user);
		Map<String, Object> request = (Map<String, Object>) context.get("request");
		request.put("msg", "登录成功!");

        ActionContext 也可获取原生request:

		HttpServletRequest req = (HttpServletRequest) context.get(StrutsStatics.HTTP_REQUEST);

        也可用ServletActionContext 直接获得Servlet API (常用):

		HttpServletRequest req = ServletActionContext.getRequest();
		req.getSession();
		ServletActionContext.getResponse();
		ActionContext context1 = ServletActionContext.getContext();

        还可用以下实现XxxAware 接口的方式获得Servlet API:

public class ServletAction extends ActionSupport implements ServletRequestAware,ServletResponseAware,ServletContextAware{
	private HttpServletRequest req;
	private ServletContext servletContext;
	private HttpServletResponse httpServletResponse;

	public String hello(){
		req.setAttribute("msg", "这是requset对象");
		System.out.println("req:"+req);
		System.out.println("servletContext:"+servletContext);
		System.out.println("httpServletResponse:"+httpServletResponse);
		return Action.SUCCESS;
	}
	
	@Override
	public void setServletRequest(HttpServletRequest httpServletRequest) {
		this.req = httpServletRequest;	
	}

	@Override
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}

	@Override
	public void setServletResponse(HttpServletResponse httpServletResponse) {
		this.httpServletResponse = httpServletResponse;
	}
}

        对比Servlet:

		HttpSession session = request.getSession();
		session.setAttribute("user", user);
                request.setAttribute("msg", "登录成功!");

    7. Servlet 需要用reqeust 或response 实现转发或重定向。

        跳转的简化,配置name 默认是"success",默认type 是转发;

        可通过请求参数动态设置Result,OGNL 通过值栈(数据载体,类似作用域) 取值;

        也可设置全局result, 对这个包下面的所有的action都起作用,先找action下面result,如果找不到就找全局;

        配置Result 中的type - 跳转(页面/action) 的方式:

1) dispatcher: 默认值,只能转发到页面,不能转发到action;用到底层的转发和重定向
2) redirect: 重定向,可以到页面,也可以到action;用到底层的转发和重定向
3) chain: 转发到某个action,不能转发到页面;传递参数和值struts2 提供
4) redirectAction:只能重定向到action,不能重定向到页面;不传递参数和值struts2 提供
5) stream: 下载的时候使用

3)ActionContext

a)这个对象是ThreadLocal来管理的(用ThreadLocal管理的对象是线程安全的,每个线程都有独自的一份互不干扰)
b)该对象是个map结构

c)每次请求都会创建一个新的ActionContext

4)ValueStack 值栈
a)可以当成数据载体
b)方法
1)push:压到栈顶 (如下图例中将user对象压到栈顶)
2)set:压到栈顶的是map
3)peek:查看栈顶,并返回对象
4)pop:弹出栈顶对象并返回
c)值栈对象是放在Request作用域中的,重定向后值栈中的数据就消失了

1)request.getAttribute("struts.valueStack")


5)Struts 标签和OGNL 配合使用

    1. 标签
1) UI标签
2) 非UI标签
a) 逻辑标签
b) 迭代
c) 时间
d) 包含
e) 数据展示标签
    2. OGNL
1) 对象导航语言,工作在视图层,用来取代页面中的java 脚本,简化数据的访问操作。
2) 符号
a) #: 访问非根元素的时候需要加#
b) %: struts2标签不认识OGNL表达式的时候需要用%包起来
c) $: 动态参数,可以配置文件访问值栈中的内容
3) 在使用的时候一般都是stuts2的标签和OGNL结合使用
4) 用OGNL访问域对象中的数据
a)#request.msg --> ${requestScope.msg};

b)#attr.msg -- > ${msg}

注: attr 用于按request > session > application顺序访问其属性(attribute)
        #attr.userName相当于按顺序在以上三个范围(scope)内读取userName属性,直到找到为止。

6)类型转换器 typeConverter
a)stuts2基本类型都会自动转,不需要我们处理
b)自定类对象类型的时候就需要到类型转换器
1)先定义一个类型转换器
a)继承StrutsTypeConverter
1)从字符串转成对象,表单中name要写对象的名称
2)从对象转成字符串,获取也要对象的名称
b)配置类型转换器
1)全局
1)位置:放在src下面
2)命名:xowrk-conversion.properties
3)内容:对象的全类名=类型转换器的全类名
2)局部
1)位置:和actino放在一起
2)命名:ActionName-conversion.properties
3)内容:对象=类型转换器的全类名
c)自定义类型转换器要和struts2标签一起使用


7)用编程校验(尽量放在前台校验,减少服务器压力)

1)Action要继承ActionSupport
2)校验失败后默认的返回值input,一般都是跳转到原页面
3)如何校验
a)复写父类中的validate方法
1)这个方法对所有的方法都起作用
b)validateXxxx(validateAddUser)
1)只对addUser方法起作用
c)错误的添加
1)调用父类中addFieldError("属性名","错误信息")
    用配置文件校验
1)配置文件
1)命名:<ActionClassName>-<aliasName>-validation.xml
a)aliasName是action名字不是方法的名字
2)位置:和action放在一起
3)内容
1)约束文件
2)校验器(struts2提供的)
a)xwork-core.jar/com.opensymphony.xwork2.validator.validators/default.xml
2)校验失败后默认的返回值input,一般都是跳转到原页面

8)拦截器 
1)概念
a) 是struts2提供的一个组件
b) 拦截器和servlet没有关系
c) 拦截器只拦截action
2)如何定义拦截器
a) 写一个类继承AbstractInterceptor, 复写里面的方法
3)配置
1) <interceptors>
a) <interceptor>
1)name:拦截器的名字
2)class:拦截器的全类名
b) <interceptor-stack>
1)name:拦截器栈的名字
2)<interceptor-ref>:可以引入拦截器,也可以引入拦截器栈
2) <default-interceptor-ref>
a) 引入要执行的拦截器(栈)
4) 应用:记录Action执行时间,用到AOP 思想;登录拦截。

9)国际化

    1. Java 国际化

1) 简称i18n
2) Locale:本地化
a) zn_CN
b) en_US
3) ResourceBundle:可以通过这个类来实现国际化
a) 资源文件
1) 命名:resource_zn_CN.properties
2) 位置:放在src下面
4) MessageFormat:可以设置动态参数
    2. struts2中的国际化
1. 配置baseName <constant name="struts.custom.i18n.resources" value="resource"></constant>
2. 继承ActionSupport,否则无法调用父类中的getText()
3. 页面国际化 -两种方式
1) label:通过ONGL表达式 %{getText('username')} 调用ActinoSupport类中的getText()
2) key:直接写资源文件中的key,代替label
4. 后台提示信息国际化
1) 调用父类中的getText("key")

10)上传

    1.文件上传

1.表单中要修改enctype属性 enctype="multipart/form-data"
2.action中定义三个属性
1)myFile:上传的文件对象(临时)
2)myFileFileName(上传的文件名称)
3)myFileContentType(上传的文件类型)
3. action 方法中先上传到一个临时文件,再拷贝
    2.校验 -以下两项都需要
1.文件上传的拦截器(fileUpload),在action 里设置
1)文件类型 
a)去tomcat的web.xml里面去找,赋值mimeType过来
b) 可国际化拦截的提示信息,覆盖原英文提示信息。
Idea 双shift(右上角放大镜) 查找到struts-messages.properties, 在中文资源文件重写。
2)文件大小
2.引入默认的拦截器栈 (给action中的属性赋值是由默认拦截器做的)
    3.批量上传
1.表单里面定义多个上传控件
2.action中的接收成员变量都换成数组,记得setter getter

11)文件下载 -两种方式差不多
1. 普通流程
a)先准备被下载下载的文件
b)把文件转成流(ips)
c)设置响应头 -注意只能响应一次,再return "success" 的话会报错;这里也了解了Action 里的方法
是可以返回void的,相应地struts.xml 可以没有结果集。
1)处理文件中文的问题 fileName = new String(fileName.getBytes("utf-8"), "iso-8859-1");
或者 fileName = URLEncoder.encode(fileName, "utf-8");

response.setContentType("application/x-msdownload");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

d) 字节IO流的拷贝
2.struts2中的文件下载
1)把文件流和文件名称设置到值栈中,即设为成员变量
2)<result>中的type属性配置的是stream,result 下级param节点设置以下两个参数
a)设置流的名称

b)设置响应头

12)ajax和Action交互
解决乱码:response.setContentType("text/html;charset=utf8");
1.导入json的插件包 struts2-json-plugin
2.包要继承json-default
3.把对象装到集合里面,把集合放入的值栈中,setter getter

4.返回类型是json

1)需要指定要装换的数据
<param name="root">userList</param>
浏览器打开开发者模式,可以在js(jQuery) 中加断点"debugger" 来调试

13)token
1)能解决表单重复提交的问题
不允许提交后 后退,原封不动再提交,但后退刷新再填再提交是允许的。
2)如何使用
a)表单中添加token标签
b)再添加一个拦截器,并加上拦截器栈 "defaultStack"
c)如果验证到是重复提交默认的返回值invalid.token
3)原理
1)先到服务端获取key(key也在服务端有一份)
2)表单的提交的时候先判断key是否有效
a)如果有效就执行action,删除key

b)如果无效就说明已经是重复提交

14)全局的异常处理 -很实用但实际生产用Spring,机制类似
1. 在package 里面配:<global-exception-mapping>
<exception-mapping> 两个属性
a)result:result的name
b)exception:异常的全类名
2.捕获机制
1)先找当前的异常,如果找到了就跳转到对应的页面,如果没有配置就找该异常的父类,跳转到对应的页面
如果父类没有找到就报异常信息暴露给用户
15)模块化开发,struts配置文件分成多个模块,再包含进总配置文件
<include file="user.xml"></include>








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章