过滤器模式
今天试着用刚看的过滤器模拟了一些web开发中的拦截器的功能,感觉还真的非常有意思
模拟场景
现在一个人想获取绝密信息,我们的任务就是在他获取绝密信息之前,判断一下他有没有权利访问。如果有,则打印绝密信息,否则则反馈不能获取的原因。
类之间的关系
具体实现
- Controller.java
//模拟控制器类
public class Controller {
/**
* 模拟渲染出文本
*/
public void renderText(String msg) {
System.out.println(msg);
}
}
- SecretController.java
//模拟绝密消息的获取控制器
public class SecretController extends Controller {
/**
* 模拟获取绝密消息
*/
public void getSecretMsg() {
// 假装从数据库取得绝密消息
String secretMsg = "今天吃撑了!";
// 渲染到界面
renderText(secretMsg);
}
}
- Invocation.java
//负责所有控制器的调度
public class Invocation {
private InterceptorChain inters;
private Controller controller;
private Method method;
private String methodName;
//记录执行到哪个拦截器了
private int index = 0;
public Invocation(Controller controller, Method method, InterceptorChain inters) {
this.inters = inters;
this.controller = controller;
this.method = method;
this.methodName = method.getName();
}
//获取当前控制器
public Controller getController() {
return controller;
}
//获取方法名称
public String getMethodName() {
return methodName;
}
/**
* 继续执行后面的拦截器
* 如果无更多拦截器,则直接执行请求的方法
**/
public void invoke() {
if (index < inters.size()) {
inters.getInterceptor(index++).intercept(this);
} else if (index++ == inters.size()) {
//每次index++表示进入下一个拦截器
try {
method.invoke(controller);
} catch (SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
- LoginInterceptor.java
//检测当前用户是否已经登录了
public class LoginInterceptor implements Interceptor {
@Override
public void intercept(Invocation invocation) {
// 获取当前要请求的Controller
Controller con = invocation.getController();
boolean isLogin = true;
// 检查是否登录
if (isLogin) {
// 通过登录检查,继续
invocation.invoke();
} else {
// 写出未登录信息
con.renderText("您还没有登录!!!");
}
}
}
- AuthInterception.java
//检测当前用户是否有权利执行方法
public class AuthInterception implements Interceptor {
@Override
public void intercept(Invocation invocation) {
// 获取当前要请求的ControllerMethodName
Controller con = invocation.getController();
// 当前要执行的方法名称
String methodName = invocation.getMethodName();
boolean isPrivileged = true;
// 检查是否有权限
if (isPrivileged) {
// 通过权限检查,继续
invocation.invoke();
} else {
// 没有权限执行
con.renderText("您没有权限使用:" + invocation.getMethodName());
}
}
}
- Interceptor.java
//拦截器接口
public interface Interceptor {
/** 拦截 **/
void intercept(Invocation invocation);
}
- InterceptorChain.java
//拦截器链
public class InterceptorChain implements Interceptor {
private List<Interceptor> interceptorList = new ArrayList<>();
private int index = 0;
public int size() {
return interceptorList.size();
}
public Interceptor getInterceptor(int index) {
if (index < 0 || index > interceptorList.size() - 1)
throw new RuntimeException("数组越界 index=" + index);
return interceptorList.get(index);
}
/**
* 添加拦截器
*
* @param interceptor
* @return
*/
public InterceptorChain addInterceptor(Interceptor interceptor) {
interceptorList.add(interceptor);
return this;
}
/**
* 本身也是个拦截器
*/
@Override
public void intercept(Invocation invocation) {
if (index < size()) {
interceptorList.get(index++).intercept(invocation);
} else if (index++ == size()) {
invocation.invoke();
}
}
}
- Test.java
//测试类
public class Test {
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
// 初始化控制器以及拦截器,拦截器链
Controller secretController = new SecretController();
InterceptorChain ic = new InterceptorChain();
InterceptorChain ic2 = new InterceptorChain();
Interceptor inter1 = new LoginInterceptor();
Interceptor inter2 = new AuthInterception();
ic.addInterceptor(inter1);
ic2.addInterceptor(inter2);
ic.addInterceptor(ic2);
String secretMsg = "getSecretMsg";
// 模拟请求绝密数据
Invocation invocation = new Invocation(secretController, secretController.getClass().getMethod(secretMsg), ic);
invocation.invoke();
}
}
运行结果图
还有几个小问题注意
- Q:为什么InterceptorChain要实现Interceptor接口?
A:InterceptorChain本身页是个拦截器,这样设计可以可以要InterceptorChain加入InterceptorChain里面,实现拦截器组加入拦截器组里面的功能。 - Q:拦截器拦截的顺序,和加入的顺序有什么关系?
A:拦截器加入的顺序,就是拦截的顺序。 - Q:如果权限不够,怎样阻碍拦截器的传递呢?
A:只要不用invoke
方法就不会向下传递了,也就是接下来的拦截器,包括访问方法也不会执行了。 - Q:那这个拦截器只是在方法执行之前,也就是Before可以截取的,那这么在方法执行以后,也就是After可以做一些操作呢?
A:在intercept
方法中使用invoke
之前就是Before,之后就是After了。