1. 拦截器:Struts2的“万能管家”
如果你用过Struts2,肯定知道它的核心是Action,但你可能不知道,真正在幕后默默干活、掌控全局的,其实是拦截器。我刚开始接触Struts2的时候,也觉得Action就是一切,直到项目里需要加权限验证、记录操作日志,才发现原来拦截器才是那个“万能管家”。
简单来说,拦截器就像是你家小区的保安。每次有访客(HTTP请求)要进你家门(执行Action),保安都会先拦下来检查一下:看看你是不是业主(权限验证),登记一下你的来访时间(日志记录),甚至帮你把快递也拿上来(数据预处理)。等所有检查都通过了,才放行让你进门。Struts2的整个请求处理流程,就是被这样一串“保安”(拦截器链)给包裹起来的。
这设计妙在哪呢?它把那些每个请求都要做的、但又跟核心业务逻辑没直接关系的“杂事”给抽离出来了。比如权限检查,你不可能在每个Action的execute方法里都写一遍登录验证的代码吧?那样代码又臭又长,还容易出错。用拦截器,你只需要写一次,然后配置一下,所有需要保护的Action就自动拥有了这个功能。这就是Struts2基于WebWork核心带来的最大优势——面向切面编程(AOP)思想的落地,让业务控制器能和Servlet API完全解耦,写起代码来清爽多了。
2. 解剖拦截器:从接口到执行链
要自己造一个“保安”,你得先了解保安的招聘标准(接口)和工作流程(执行链)。
2.1 拦截器的三种“体质”
在Struts2里,创建一个拦截器主要有三种方式,对应着三种不同的“体质”:
-
实现
Interceptor接口:这是最原始、最全面的方式。这个接口定义了三个方法:public interface Interceptor extends Serializable { void init(); // 拦截器初始化时调用,只执行一次 void destroy(); // 拦截器销毁时调用,只执行一次 String intercept(ActionInvocation invocation) throws Exception; // 每次请求都执行的核心方法 }如果你需要一些昂贵的资源(比如数据库连接池)只在拦截器启动时初始化一次,然后用在整个应用生命周期,那就得用这种方式,在
init()里初始化,在destroy()里关闭。 -
继承
AbstractInterceptor类:这是最常用、最省事的方式。它已经帮我们实现了init()和destroy()方法(空实现)。我们只需要重写intercept这一个方法就行了。上面提到的保安登记日志这种简单功能,用它正合适。public class SimpleLoggerInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("请求开始时间:" + new Date()); // 放行,执行后续拦截器或Action String result = invocation.invoke(); System.out.println("请求结束时间:" + new Date()); return result; } } -
继承
MethodFilterInterceptor类:这是个“智能保安”。有时候,我们不是对所有“访客”都要进行同样严格的检查。比如登录接口本身就不需要检查登录状态。这个类允许我们指定哪些方法需要拦截,哪些方法直接放行。public class AuthInterceptor extends MethodFilterInterceptor { @Override protected String doIntercept(ActionInvocation invocation) throws Exception { // 检查Session中是否有用户信息 User user = (User) ServletActionContext.getRequest().getSession().getAttribute("currentUser"); if (user == null) { // 未登录,跳转到登录页面 return "login"; } // 已登录,放行 return invocation.invoke(); } }在配置文件中,你可以用
<param name="excludeMethods">login,register</param>来排除登录和注册方法,非常灵活。
2.2 拦截器链:保安团队的协作
一个Struts2请求,很少只经过一个拦截器。默认情况下,Struts2使用一个叫defaultStack的拦截器栈,里面包含了几十个拦截器,负责参数封装、类型转换、验证、文件上传等等。它们像流水线上的工人,按顺序工作。
这个顺序是由struts-default.xml(在Struts2的核心JAR包里)定义好的。当你在struts.xml中引用defaultStack时,就意味着你的Action会依次经过所有这些拦截器的处理。ActionInvocation对象的invoke()方法,就是流水线的传送带,它负责调用下一个拦截器,直到最后一个拦截器调用完,才去执行真正的Action方法。
理解这个链式调用顺序至关重要。比如,负责封装请求参数的params拦截器必须在你自己的拦截器之前执行,否则你在拦截器里就拿不到Action已经初始化好的数据。我踩过的坑是,曾经自定义了一个拦截器,没仔细配置顺序,结果它跑到params前面去了,导致里面要用的数据全是null,排查了半天。
3. 实战一:构建坚不可摧的权限控制拦截器
光说不练假把式,咱们现在就来手把手打造一个实战级的权限拦截器。假设我们有一个内容管理系统,有些页面只允许管理员看,有些操作需要编辑权限。
3.1 设计思路与核心代码
我们的权限拦截器需要做这几件事:
- 检查用户是否登录(Session里有没有用户对象)。
- 如果已登录,获取用户的角色和权限列表。
- 匹配当前请求的Action或方法,判断用户是否有权限访问。
- 有权限就放行,没权限就跳转到无权限提示页面或登录页。
这里我们采用更灵活的MethodFilterInterceptor,因为像“首页”、“关于我们”这种公开Action,根本不需要拦截。
package com.yourproject.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import com.yourproject.model.User;
import com.yourproject.model.Permission;
import org.apache.struts2.ServletActionContext;
import javax.servlet.http.HttpServletRequest;
import java.util.Set;


3445

被折叠的 条评论
为什么被折叠?



