MVC框架及Struts2介绍
Struts2核心
Struts2拦截器
Struts2值栈和OGNL表达式
Struts2标签库
1 拦截器
1.1 拦截器
拦截器(Interceptor):Struts2拦截器是在访问某个Action的方法之前或之后进行拦截,并且拦截器是可插拔的,可以通过xml配置实现。拦截器是AOP(面向切面编程)的一种实现。在Struts2中对Action的功能的实现都是通过拦截器完成的,比如参数接收、读取原生API、文件上传、异常统一处理等。
拦截器栈(Interceptor Stack):将多个拦截器按一定的顺序组合成一条拦截器链。
1.2 拦截器特点
特点
1.简化Action:拦截器能把很多功能从Action中独立出来,分散到不同功能拦截器中,简化了Action代码;
2.功能简单:拦截器和Action各自的功能更简单清晰;
3.代码模块化:将不同功能代码封装到不同拦截器中,形成模块化管理,对不同Action根据功能需要配置相应拦截器;
4.功能重用性:提高了拦截器实现的功能的重用性,同时实现了装配式和可插拔式的结构,使得整个系统结构更灵活。
1.3 拦截器执行原理
1.客户端发送请求;
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中的配置找到对应的返回结果。返回结果是一个JSP或其他页面(也可以是其他的Action链)。JSP页面展现可使用Struts2框架中的标签(该过程会涉及ActionMapper);
9.在上述过程中所有的对象(Action、Interceptors、Results等)都由xwork容器中的ObjectFactory创建。
2 默认拦截器
2.1 默认拦截器
在struts-default.xml文件中,已经设置好了默认拦截器,在<package>继承struts-default时,就继承了预设值的默认拦截器。如果不继承struts-default就无法完成接收参数等功能。
struts-default.xml文件中拦截器有三种配置:
定义好的所有拦截器<interceptor>
<interceptors>
<interceptor name="alias" class="..." />
<interceptor name="autowiring" class="..." />
...
</interceptors>
每一个拦截器都封装了一个Interceptor类
定义好的所有拦截器栈<interceptor-stack>
<interceptors>
<interceptor-stack name="basicStack">
<interceptor-ref name="exception" />
...
<interceptor-ref name="deprecation" />
</interceptor-stack>
<interceptor-stack name="validationWorkflowStack">
<interceptor-ref name="basicStack" />
...
<interceptor-ref name="workflow" />
</interceptor-stack>
...
</interceptors>
默认的拦截器(拦截器栈)<default-interceptor-ref>
<package>
<interceptors>
...
</interceptors>
<default-interceptor-ref name="defaultStack" />
</package>
2.2 给Action设置拦截器
在Action中设置拦截器:
<package name="default" namespace="/" extends="struts-default">
<action name="user" class="cn.wenwen.action.UserAction" method="execute">
<!-- 给Action设置拦截器 -->
<interceptor-ref name="i18n">
<param name="excludeParams">^action:.*,^method:.*</param>
</interceptor-ref>
</action>
</package>
如果在Action中设置了拦截器,则默认的拦截器就会失效。
2.3 设置拦截器栈
在<package>包中声明一个拦截器栈,然后在Action中引用。
<package name="default" namespace="/" extends="struts-default">
<!-- 声明一个拦截器栈 -->
<interceptors>
<interceptor-stack name="mystack">
<interceptor-ref name="i18n" />
<interceptor-ref name="basicStack" />
</interceptor-stack>
</interceptors>
<action name="user" class="cn.wenwen.action.UserAction" method="execute">
<!-- Action中引用拦截器栈的name -->
<interceptor-ref name="mystack" />
</action>
</package>
引用了设置的拦截器栈后,默认的拦截器就会失效。
3 自定义拦截器
在登录功能和权限判断的时候就需要自定义拦截器。
3.1 创建拦截器
创建一个拦截器然后实现Intercptor接口或者继承AbstractInterceptor,通常直接继承。
public class LoginInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// 登录判断
Object user = ActionContext.getContext().getSession().get("USER_IN_SESSION");
if (user != null) {
// 如果已经登录就放行
return invocation.invoke();
} else {
// 没有登录也要返回一个视图
return "login";
}
}
}
如果作用域中能获取登录的用户,调用invocation.invoke()方法放行,如果不存在用户,跳转到login登录页面。
3.2 struts.xml中配置拦截器
简单配置
<struts>
<package name="default" namespace="/" extends="struts-default">
<!-- 配置拦截器 -->
<interceptors>
<!-- 创建拦截器 -->
<interceptor name="login" class="cn.wenwen.login.LoginInterceptor" />
<!-- 创建拦截器栈 -->
<interceptor-stack name="loginStack">
<interceptor-ref name="login" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<!-- 全局视图:没有访问权限跳转登录页面 -->
<global-results>
<result name="login" type="redirect">
/login.jsp
</result>
</global-results>
<!-- 登录Action -->
<action name="login" class="cn.wenwen.login.LoginAction"
method="execute">
<!-- 登录成功通过Action跳转到主页面 -->
<result name="success" type="redirectAction">
main
</result>
<!-- 登录失败,携带失败原因跳回登录页面 -->
<result name="login">
/login.jsp
</result>
</action>
<!-- 主页面Action -->
<action name="main" class="cn.wenwen.login.MainAction">
<!-- 引用自定义拦截器栈 -->
<interceptor-ref name="loginStack" />
<result name="success">
/main.jsp
</result>
</action>
</package>
</struts>
配置中,如果有很多Action都需要拦截器判断权限,每一个都需要引用自定义的拦截器。这时可以将自定义的拦截器栈设置为默认的,登录的Action不能做权限判断,则使用原始默认的替换掉自定义默认拦截器。
高级配置
通常对于项目而言,分前台和后台。前台不需要登录或权限判断就能直接访问操作,后台需要登录和权限判断以后才能访问操作。可以通过拦截器来实现登录和权限判断,将不需要做权限判断的放入一个<package>中使用默认拦截器,将需要做拦截判断的放入另一个<package>中使用自定义默认拦截器。
<struts>
<!-- 不需要做权限判断的前台页面 -->
<package name="default" namespace="/" extends="struts-default">
......
</package>
<!-- 需要做权限判断的后台页面 -->
<package name="system" namespace="/system" extends="struts-default">
<!-- 自定义拦截器栈 -->
<interceptors>
<interceptor name="login" class="cn.wenwen.login.LoginInterceptor" />
<interceptor-stack name="loginStack">
<interceptor-ref name="login" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<!-- 配置自定义默认拦截器 -->
<default-interceptor-ref name="loginStack" />
<!-- 全局视图:没有访问权限跳转登录页面 -->
<global-results>
<result name="login" type="redirect">
/login.jsp
</result>
</global-results>
<!-- 登录Action -->
<action name="login" class="cn.wenwen.login.LoginAction"
method="execute">
<!-- 覆盖自定义默认拦截器 -->
<interceptor-ref name="defaultStack" />
<!-- 登录成功 -->
<result name="success" type="redirectAction">
main
</result>
<!-- 登录失败 -->
<result name="login">
/login.jsp
</result>
</action>
<!-- 主页面Action -->
<action name="main" class="cn.wenwen.login.MainAction">
<result name="success">
/main.jsp
</result>
</action>
</package>
</struts>