Struts2
一、架构体系、运行流程
架构体系:
基于Model2的MVC架构模式设计
运行流程:
1. 请求发送给 StrutsPrepareAndExecuteFilter
2.StrutsPrepareAndExecuteFilter 询问 ActionMapper: 该请求是否是一个 Struts2 请求(即是否返回一个非空的 ActionMapping 对象)
3. 若 ActionMapper 认为该请求是一个 Struts2 请求,则 StrutsPrepareAndExecuteFilter 把请求的处理交给 ActionProxy
4. ActionProxy通过 Configuration Manager询问框架的配置文件,确定需要调用的 Action 类及Action 方法
5. ActionProxy创建一个 ActionInvocation 的实例,并进行初始化
6.ActionInvocation 实例在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
7. Action 执行完毕,ActionInvocation 负责根据 struts.xml 中的配置找到对应的返回结果。调用结果的 execute 方法,渲染结果。在渲染的过程中可以使用Struts2 框架中的标签。
8. 执行各个拦截器 invocation.invoke() 之后的代码
9. 把结果发送到客户端
二、拦截器与拦截器栈
1)拦截器的工作原理
Struts 2中定义了拦截器的接口以及默认实现,实现了Interceptor接口或继承了AbstractInterceptor的类可以作为拦截器。接口中的init()方法在拦截器被创建后立即被调用,它在拦截器的生命周期内只被调用一次,可以在该方法中对相关资源进行必要的初始化。每拦截一个请求,intercept()方法就会被调用一次。destory()方法将在拦截器被销毁之前被调用, 它在拦截器的生命周期内也只被调用一次。
2)自定义拦截器
1、编写一个Java类继承或实现接口(调用invocation.invoke())
2、在Struts.xml文件中配置自定义的拦截器
项目中使用过的有权限拦截器、执行时间拦截器、令牌拦截器等。
3)拦截器
1、Parameters 拦截器:把表单字段映射到 ValueStack 栈的栈顶对象的各个属性中. 如果某个字段在模型里没有匹配的属性, Param 拦截器将尝试 ValueStack 栈中的下一个对象
2、ModelDriven拦截器:如果Action类实现了ModelDriven接口,改拦截器将把ModelDriven接口的getModel()方法返回的对象置于栈顶。
3、prepare 拦截器:负责准备为 getModel() 方法准备 model
运行顺序:3->2->1
4)拦截器栈
paramsPrepareParamsStack从字面上理解来说, 这个stack的拦截器调用的顺序为:首先 params,然后 prepare,接下来 modelDriven,最后再 params
Struts 2.0的设计上要求 modelDriven 在 params 之前调用,而业务中prepare要负责准备model,准备model又需要参数,这就需要在 prepare之前运行params拦截器设置相关参数,这个也就是创建paramsPrepareParamsStack的原因。
流程如下:
1. params拦截器首先给action中的相关参数赋值,如id
2. prepare拦截器执行prepare方法,prepare方法中会根据参数,如id,去调用业务逻辑,设置model对象
3. modelDriven拦截器将model对象压入valuestack,这里的model对象就是在prepare中创建的
4. params拦截器再将参数赋值给model对象
5. action的业务逻辑执行
5)拦截器和过滤器的区别
①拦截器是基于Java反射机制的,而过滤器是基于接口回调的。
②过滤器依赖于Servlet容器,而拦截器不依赖于Servlet容器。
③拦截器只能对Action请求起作用,而过滤器可以对所有请求起作用。
④拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。
三、值栈
1)值栈的原理和生命周期
Value-Stack贯穿整个 Action 的生命周期,保存在request作用域中,所以它和request的生命周期一样。当Struts 2接受一个请求时,会创建ActionContext、Value-Stack和Action对象,然后把Action存放进Value-Stack,所以Action的实例变量可以通过OGNL访问。由于Action是多实例的,和使用单例的Servlet不同, 每个Action都有一个对应的Value-Stack,Value-Stack存放的数据类型是该Action的实例,以及该Action中的实例变量,Action对象默认保存在栈顶。
2)内部体系
OgnlValueStack:root是栈结构,可以实现后进先出
在 ValueStack 对象的内部有两个逻辑部分:
ObjectStack:Struts 把 Action 和相关对象压入 ObjectStack 中
ContextMap:Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中. 实际上就是对 ActionContext 的一个引用
Struts 会把下面这些映射压入 ContextMap 中
parameters: 该 Map 中包含当前请求的请求参数
request: 该 Map 中包含当前request 对象中的所有属性
session: 该 Map 中包含当前session 对象中的所有属性
application:该 Map 中包含当前application 对象中的所有属性
attr: 该 Map 按如下顺序来检索某个属性: request, session, application
四、web.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>
五、Struts.xml配置说明
1、配置 Struts 可以受理的请求的扩展名
2、配置为开发模式
3、配置国际化资源文件
4、修改当前 Struts 应用的主题
5、继承struts-default或json-default(支持json返回类型)
6、配置使用 paramsPrepareParamsStack 作为默认的拦截器栈
7、配置异常和处理页面
8、配置global-results
9、配置各种action
<struts>
1、<constantname="struts.action.extension"value="action,do,"></constant>
2、<constantname="struts.devMode" value="true" />
3、<constantname="struts.custom.i18n.resources"value="i18n"></constant>
4、<constant name="struts.ui.theme"value="simple"></constant>
5、<package name="default"namespace="/" extends="struts-default">
6、<interceptors>
<interceptor-stackname="yuhui">
<interceptor-refname="paramsPrepareParamsStack">
<paramname="prepare.alwaysInvokePrepare">false</param>
</interceptor-ref>
</interceptor-stack>
<interceptorname="hello"class="com.yuhui.interceptors.MyInterceptor"></interceptor>
</interceptors>
<default-interceptor-refname="yuhui" />
7、<global-exception-mappings>
<exception-mappingresult="error"exception="java.lang.Exception"></exception-mapping>
</global-exception-mappings>
8、<global-results>
<result name="index"type="redirect">/index.jsp</result>
<resultname="error">/error.jsp</result>
<result name="ulogin"type="redirect">/ulogin.jsp</result>
<result name="stream"type="stream">
<paramname="inputName">inputStream</param>
</result>
</global-results>
9、<action name="user_*"class="userAction" method="{1}">
<result name="goURL"type="redirect">${#session.goURL}</result>
</action>
</package>
</ struts>
六、其他
1)Token(令牌)机制,防止重复提交
Struts使用Token机制,来防止恶意的破坏和重复提交问题,也就是点击后退后在再提交,这是Struts无法发现的,在form中生成一个token码,在session中也保存有一个同样的token码,当表单提交后,判断两个token码向等后,就会改变session中的这个token码,当然在用回退后,form的token码是不会变的,在提交,还会判断两个token码是否相等,如果不等就会抛出异常,证明这是过时的垃圾数据。
作用:Token机制可以解决表单的重复提交;
产生token的两种方式:<1>. form表单的post请求,使用隐藏域<input type="hidden" name="token"value="${token}">;
<2>. 直接使用超级连接<html:link action="" trasantion="true">,可以直接在连接后面添加token值。
2)Struts的入口类
Struts1:ActionServlet是Struts1的入口类,所有Struts请求都经由该类转发处理
Struts2:StrutsPrepareAndExecuteFilter,所有Action都要继承ActionSupport
3)Struts 2中如何访问HttpServletRequest、HttpSession和ServletContext三个域对象
有两种方式:
①通过ServletActionContext的方法获得;
②通过ServletRequestAware、SessionAware和ServletContextAware接口注入。
4)OGNL、EL——${},SPEL——#{}
5)Struts 2中的Action并没有直接收到用户的请求,那它为什么可以处理用户的请求,又凭什么知道一个请求到底交给哪个Action来处理?
Struts2的核心过滤器接收到用户请求后,会对用户的请求进行简单的预处理(例如解析、封装参数),然后通过反射来创建Action实例,并调用Action中指定的方法来处理用户请求。
要决定请求交给哪一个Action来处理有两种方式:1利用配置文件:可以在配置文件中通过<action>标签配置和请求对应的Action类以及要调用的方法;2利用约定:Struts2中可以使用约定(convention)插件,例如约定xxx总是对应XxxAction,这是对约定优于配置理念的践行。
6)Struts 2中,Action通过什么方式获得用户从页面输入的数据,又是通过什么方式把其自身的数据传给视图的?
Action从页面获取数据有三种方式:
①通过Action属性接受参数
②通过域模型获取参数
③通过模型驱动获取参数 (ModelDriven<T>)
Action将数据存入值栈(Value Stack)中,视图可以通过表达式语言(EL)从值栈中获取数据。