拦截器---登录状态检查Filter不是struts2的解决方案
Struts2默认启用了大量通用功能的拦截器,只要配置Action的package继承了struts-default包,这些拦截器就会起到作用
拦截器的基本原理
责任链模式: 责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
优点:
降低系统的耦合度,简化对象的相互连接,同时增强对象指派职责的灵活性,增加新处理类很方便
缺点:
不能保证请求一定被接收,且长的职责链系统性能会受到一定影响,且代码调试不方便
拦截接口
public interface Interceptor{
public void intercept(ActionInvocation ai); }
当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器
1、定义拦截器类
public class CheckLoginInterceptor implements Interceptor { @Override public void destroy() { System.out.println("CheckLoginInterceptor.destroy()"); }
@Override public void init() {当服务器启动时,自动创建拦截器对象,当拦截器创建后自动执行init方法,这个方法在整个生命周期中运行且只运行一次 System.out.println("CheckLoginInterceptor.init()"); }
@Override public String intercept(ActionInvocation invocation) throws Exception {用于执行拦截处理,多线程的 System.out.println("CheckLoginInterceptor.intercept()"); //前置处理,这里可以对请求信息进行处理 String res=invocation.invoke();//继续向后执行 //后置处理,这里可以执行资源回收之类的处理 return res; } } |
2、在struts.xml中声明拦截器
<interceptors> <interceptor name="checkLogin" class="com.yan.action.interceptors.CheckLoginInterceptor"></interceptor> </interceptors> |
3、引用拦截器
<action name="*" class="com.yan.action.AdminUserAction" method="{1}"> <interceptor-ref name="checkLogin"/> 声明引用特定的拦截器时,系统的默认拦截器失效;如果没有引用拦截器时Struts2框架提供了默认拦截器 <param name="rowsPerPage">2</param> <result>/WEB-INF/content/user/{1}.jsp</result> </action> |
<default-interceptor-ref name="defaultStack"/>
<interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> … … </interceptor-stack> |
<action name="*" class="com.yan.action.AdminUserAction" method="{1}"> <interceptor-ref name="checkLogin"/> <interceptor-ref name="defaultStack"/> <param name="rowsPerPage">2</param> <result>/WEB-INF/content/user/{1}.jsp</result> </action> |
public class CheckLoginInterceptor implements Interceptor { @Override public void destroy() { System.out.println("CheckLoginInterceptor.destroy()"); }
@Override public void init() { System.out.println("CheckLoginInterceptor.init()"); }
@Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("CheckLoginInterceptor.intercept()"); ActionContext ac = invocation.getInvocationContext(); if (ac != null) { Map<String, Object> session = ac.getSession(); if (session != null && session.containsKey("loginUser")) { String res = invocation.invoke();// 继续向后执行 // 后置处理,这里可以执行资源回收之类的处理 return res; } } return Action.LOGIN; } }
|
<action name="*" class="com.yan.action.AdminUserAction" method="{1}"> <interceptor-ref name="checkLogin"/> <interceptor-ref name="defaultStack"/> <param name="rowsPerPage">2</param> <result>/WEB-INF/content/user/{1}.jsp</result> <result name="login">/WEB-INF/content/user/login.jsp</result> </action> |
?拦截器 --filter
Action执行前或执行后动态添加方法的组件技术,也可以不执行Action
public interface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation) throws Exception;
}
简化开发
public abstract class AbstractInterceptor implements Interceptor {
public void init() { }
public void destroy() { }
public abstract String intercept(ActionInvocation invocation) throws Exception;
}
日志拦截
执行修改时记录修改信息
id user_id pdate(current_timestamp) ipaddr action params
需要记忆的系统拦截器
alias在不同请求之间将请求参数在不同名字件转换,请求内容不变
exception将异常定位到一个页面,并准备要显示内容
fileUpload提供文件上传功能
i18n记录用户选择的locale
params将请求中的参数设置到Action中去,并进行相应的类型转换。
modelDriven如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。
conversionError将类型转换异常添加到Action的属性字段中。
store存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等。
validation使用actionName-validation.xml文件中定义的内容校验。
workflow调用Action的validate方法,一旦有错误返回,重新定位到INPUT页面
scope将Action状态存入session和application的简单方法。
servletConfig提供访问HttpServletRequest和HttpServletResponse等的方法以及以Map的方式访问。
staticParams从struts.xml文件中将中的中的内容设置到对应的Action中。
timer输出Action执行的时间
token通过Token来避免重复提交
解析servletConfig拦截器代码理解拦截器机制
public class ServletConfigInterceptor extends AbstractInterceptor implements StrutsStatics {
public String intercept(ActionInvocation invocation) throws Exception {
final Object action = invocation.getAction();
final ActionContext context = invocation.getInvocationContext();
if (action instanceof ServletRequestAware) {
HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
((ServletRequestAware) action).setServletRequest(request);
}
if (action instanceof ServletResponseAware) {
HttpServletResponse response = (HttpServletResponse) context.get(HTTP_RESPONSE);
((ServletResponseAware) action).setServletResponse(response);
}
if (action instanceof ParameterAware) {
((ParameterAware) action).setParameters((Map)context.getParameters());
}
if (action instanceof ApplicationAware) {
((ApplicationAware) action).setApplication(context.getApplication());
}
if (action instanceof SessionAware) {
((SessionAware) action).setSession(context.getSession());
}
if (action instanceof RequestAware) {
((RequestAware) action).setRequest((Map) context.get("request"));
}
if (action instanceof ServletContextAware) {
ServletContext servletContext = (ServletContext) context.get(SERVLET_CONTEXT);
((ServletContextAware) action).setServletContext(servletContext);
}
return invocation.invoke();
}
使用Timer拦截器记录输出Action执行的时间
使用Token拦截器避免重复提交
自定义拦截器AbstractInterceptor Interceptor
struts2拦截器的interceptor方法中,参数ActionInvocation可用来获取页面用户输入的信息
系统对于拦截器的调用,是通过ActionInvocation来实现的。
if (interceptors.hasNext()) {
Interceptor interceptor=(Interceptor)interceptors.next();
resultCode = interceptor.intercept(this);
} else {
if (proxy.getConfig().getMethodName() == null) resultCode = getAction().execute();
else resultCode = invokeAction(getAction(), proxy.getConfig());
}
注意:如果为Action指定了一个拦截器,则系统默认的拦截器栈将会失去作用。为了继续使用默认拦截器,所以上面配置文件中手动引入了默认拦截器
方法过滤拦截MethodFilterInterceptor
includeMethods excludeMethods
<interceptors>
<interceptor name="myInterceptor11" class="com.yan.MyInterceptor11">
<param name="hello">world</param>
</interceptor>
为拦截器增加一个名为hello值为world的属性
<interceptor name="myInterceptor22" class="com.yan.MyInterceptor22"/>
<interceptor name="myInterceptor33" class="com.yan.MyInterceptor33">
<param name="includeMethods">test,abc</param>
</interceptor>
</interceptors>
<action name="methodFilter" class="com.yan.MethodFilterAction" method="test">
<result name="success">/abc.jsp</result>
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="myInterceptor11"/>
<interceptor-ref name="myInterceptor22"/>
<interceptor-ref name="myInterceptor33">
<param name="includeMethods">abc</param>
</interceptor-ref>
</action>
1、默认的情况下,拦截器会拦截Action中的所有的方法,这里不包括setter或getter方法。这时就可以使用方法过滤拦截器来拦截指定的方法,这是一种更加细化的拦截器的配置方式。它可以细化到拦截具体的某个方法。而不是拦截某个Action,因为拦截Action是一种粗粒度的实现方式
2、使用includeMethods指明拦截器所要拦截的方法。使用excludeMethods指明拦截器不再拦截的方法。这里excludeMethods和includeMethods是在MethodFilterInterceptor类中定义的成员变量。而且只要includeMethods进来的方法就一定会被拦截,而不管是否已经把它excludeMethods在外了,也就是说includeMethods的优先级要高于excludeMethods。也可以使用<param name="includeMethods"/>在上面定义拦截器的时候指定全局性过滤的方法
区别就是对方法的过滤有全局性和局部性区分。而当发生冲突时,则依照【就近原则】以局部性的配置为准
3、假设全局性过滤定义为<param name="includeMethods">test</param>。而在局部性过滤中定义为<param name="excludeMethods">test</param>。这种情况下<param name="includeMethods">test</param>将生效,即拦截Action中的test()方法。这个时候全局中配置的是拦截,局部中配置的是不拦截,二者并没有发生冲突,所以仍是以includeMethods优先级高。可以认为在局部的配置中,已经隐含的把<param name="includeMethods">test</param>继承过来了
修改Action返回值PreResultListener
Struts2中过滤器,拦截器之间有什么区别?
1、拦截器是基于java反射机制的,而过滤器是基于函数回调的。
2、过滤器依赖与servlet容器,而拦截器不依赖与servlet容器。
3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有请求起作用。
4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。
5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次。
1) 客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2) 这个请求经过一系列的过滤器Filter,这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,如SiteMesh Plugin
3) 接着StrutsPrepareAndExecuteFilter被调用,StrutsPrepareAndExecuteFilter询问ActionMapper来决定这个请求是否需要调用某个Action。它同时会创建ActionContext,是action上下文,ActionContext是一个容器,这个容易主要存储request、session、application、parameters等相关信息.ActionContext是一个线程的本地变量,这意味着不同的action之间不会共享ActionContext,所以也不用考虑线程安全问题。其实质是一个Map,key是标示request、session、……的字符串,值是其对应的对象
4) 如果ActionMapper决定需要调用某个Action,StrutsPrepareAndExecuteFilter把请求的处理交给ActionProxy。
5) ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类。
6) ActionProxy创建一个ActionInvocation的实例。ActionInvocation 实例使用命名模式来调用,回调Action的execute方法,该execute方法先获取用户请求参数,然后它会调用业务逻辑组件来处理用户的请求。在调用Action的过程前后,涉及到相关拦截器Intercepter的调用。
7) 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper。
在上述过程中所有的对象(Action,Results,Interceptors等)都是通过ObjectFactory来创建的。
将按照顺序逐一加载:default.properties,struts-default.xml,struts-plugin.xml,struts.properties,struts.xml,web.xml