定义:在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这条链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。
典型案例:Java Web 的 Filter、Spring MVC 的 DispatcherServlet、Spring Security 的各种过滤器、Easy Rules。。。
我们来模仿Java Web的Filter:
在本例中,按顺序调用一条链上的每一个对象,执行完后由该对象决定是否传递到下一个对象。
1、定义一个过滤链类,泛型表示传递的数据对象的类型。类里面定义了这条链上的过滤器集合,并提供增加的方法;还定义了线程独享的下一个要执行的过滤器所在集合位置变量。核心的是doFilter方法,先获取下一个要执行的过滤器所在集合位置,当过滤器集合里面存在该位置对应的过滤器时,就获取过滤器并执行它的doFilter方法,并将下一个要执行的过滤器所在集合位置指向下一个过滤器。
public class FilterChain<T> {
/**
* 过滤器集合
*/
private List<Filter<T>> filters = new ArrayList<>();
/**
* 线程隔离的下一个要执行的过滤器所在集合位置
*/
private static final ThreadLocal<Integer> NEXT_FILTER_NO = new ThreadLocal<Integer>() {
protected Integer initialValue() {
return 0;
}
};
/**
* 执行下个过滤器
* @param t
*/
public void doFilter(T t) {
Integer no = NEXT_FILTER_NO.get();
if (no < filters.size()) {
NEXT_FILTER_NO.set(no + 1);
filters.get(no).doFilter(t, this);
}
}
/**
* 添加过滤器,支持链式调用
* @param filter
* @return 当前FilterChain
*/
public FilterChain<T> addFilter(Filter<T> filter) {
this.filters.add(filter);
return this;
}
}
2、定义一个过滤器接口,泛型表示传递的数据对象的类型。提供了一个doFilter方法,参数是传递的数据对象和过滤链对象引用。
public interface Filter<T> {
void doFilter(T t, FilterChain<T> filterChain);
}
两个实现类,分别用于判断用户是否已经登录和是否是管理员(不要在意具体逻辑)。
public class LoginFilter implements Filter<User> {
public void doFilter(User user, FilterChain filterChain) {
if (user.isLogin()) {
System.out.println("欢迎您!");
filterChain.doFilter(user);
} else {
System.out.println("您尚未登录!");
}
}
}
public class RoleFilter implements Filter<User> {
public void doFilter(User user, FilterChain<User> filterChain) {
if (user.isManager()) {
System.out.println("管理员您好!");
filterChain.doFilter(user);
} else {
System.out.println("您不是管理员!");
}
}
}
3、定义用户对象,为了测试方便,特意定义了是否登录isLogin和是否是管理员isManager两个变量。同样,不要太在意细节。
public class User {
private String name;
private boolean isLogin;
private boolean isManager;
public User(String name, boolean isLogin, boolean isManager) {
super();
this.name = name;
this.isLogin = isLogin;
this.isManager = isManager;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isLogin() {
return isLogin;
}
public void setLogin(boolean login) {
isLogin = login;
}
public boolean isManager() {
return isManager;
}
public void setManager(boolean manager) {
isManager = manager;
}
}
4、测试代码
public class ChainTest {
public static void main(String[] args) throws InterruptedException {
User user1 = new User("wen", false, false),
user2 = new User("jia", true, false),
user3 = new User("www", false, true),
user4 = new User("w", true, true);
Filter<User> loginFilter = new LoginFilter(),
roleFilter = new RoleFilter();
FilterChain<User> filterChain = new FilterChain<>();
filterChain.addFilter(loginFilter).addFilter(roleFilter);
new Thread(() -> filterChain.doFilter(user1)).start();
Thread.sleep(500);
System.out.println("-----------------");
new Thread(() -> filterChain.doFilter(user2)).start();
Thread.sleep(500);
System.out.println("-----------------");
new Thread(() -> filterChain.doFilter(user3)).start();
Thread.sleep(500);
System.out.println("-----------------");
new Thread(() -> filterChain.doFilter(user4)).start();
}
}
首先创建了4个用户对象,他们分别有不同的是否登录和是否是管理员的属性值。然后分别创建登录过滤器和角色过滤器的实例。接着创建过滤链对象,并添加两个过滤器进去。最后,用不同线程(需要考虑到执行过程导致的过滤链的下一个要执行的过滤器所在集合位置相互影响的问题)对不同的用户执行过滤链。
5、运行结果
您尚未登录!
-----------------
欢迎您!
您不是管理员!
-----------------
您尚未登录!
-----------------
欢迎您!
管理员您好!
优点
非常显著的优点是将请求和处理分开,请求者不用知道是谁处理的,处理着不用知道请求的全貌,两者解耦,提高系统灵活性。
缺点
1、链条长度带来的性能问题。
2、逻辑复杂度升高,调试困难。