责任链模式在我们的日常开发中会经常遇到,比如我们在servlet中的Filter过滤器,就是采用责任链模式来进行处理的,Mybatis中SqlSession下的四大对象Execute(执行器), StatementHandler(语句处理器),ResultHandler(结果集处理器)以及ParameterHandler(参数处理器)也是采用责任链模式来完成对数据的关系映射生成完整的sql和映射响应结果,还有很多很多,在这里就不一一累述了,今天主要记录一下使用java来优雅的实现责任链模式。
1. 首先我们需要先了解一下什么是责任链模式,责任链模式简单而言就是:对一个请求创建一个对象链,每个对象依序检查此请求,并对其进行处理,或者将它传递到对象链中的下一个对象进行处理,依次处理直到链尾。(也就是说当你想对一个请求在需要满足一定条件下进行多次不同的处理时,可采用此模式)
2. 首先我们定义一个抽象类Handler:
/**
* 处理器抽象类
*/
public abstract class Handler {
public abstract void setNextHandler(Handler handler);
public abstract void handler(MyRequest request);
}
3. 我们定义一个处理器的优先级注解:
@Inherited // 允许子类继承父类中的注解
@Documented // 将注解包含在Javadoc中
@Target(value = ElementType.TYPE) // 注解可以用于什么地方
@Retention(value = RetentionPolicy.RUNTIME) // 表示需要在什么级别保存该注解信息
public @interface HandlerAnnotation {
int offset() default 0;
}
4. 我们定义三个实现类:
(1)FirstHandler
@HandlerAnnotation(offset = 1)
public class FirstHandler extends Handler {
private Handler handler;
@Override
public void setNextHandler(Handler handler) {
this.handler = handler;
}
@Override
public void handler(MyRequest request) {
System.out.println("我是第一个责任链的第一个处理类,处理完成,流转到下一个处理类");
if (Objects.isNull(handler)) {
System.out.println("处理链已经到底了");
System.out.println(request);
}
this.handler.handler(request);
}
}
(2)SecondHandler
@HandlerAnnotation(offset = 2)
public class SecondHandler extends Handler {
private Handler handler;
@Override
public void setNextHandler(Handler handler) {
this.handler = handler;
}
@Override
public void handler(MyRequest request) {
System.out.println("我是责任链模式中的第二个处理类,处理完成,流转到下一个处理类");
if (Objects.isNull(handler)) {
System.out.println("处理链已经到底了");
System.out.println(request);
}
handler.handler(request);
}
}
(3) ThirdHander
@HandlerAnnotation(offset = 3)
public class ThirdHandler extends Handler {
private Handler handler;
@Override
public void setNextHandler(Handler handler) {
this.handler = handler;
}
@Override
public void handler(MyRequest request) {
System.out.println("我是第三个处理类,处理完成后,流转到下一个处理类");
if (Objects.isNull(handler)) {
System.out.println("处理链已经到底了");
System.out.println(request);
}
handler.handler(request);
}
}
4. 我的包路径如下所示:
5. 在这里我们可以直接在TestChain类中测试处理链模式:
/**
* 测试责任链模式
* 当你想让一个及以上的对象有机会能够处理某个请求的时候,就是用此模式
*/
public class TestChain {
public static void main(String[] args) throws IOException {
FirstHandler firstHandler = new FirstHandler();
SecondHandler secondHandler = new SecondHandler();
ThirdHandler thirdHandler = new ThirdHandler();
firstHandler.setNextHandler(secondHandler);
secondHandler.setNextHandler(thirdHandler);
MyRequest request = new MyRequest();
request.setName("bibi");
request.setAge(12);
firstHandler.handler(request);
}
}
执行结果如下:
这里,我们就简单完成了对责任链模式的简单实现,但是在实际开发中,我们如果编写这样的代码是不符合开发规则的,而且难以维护和处理,如果我们有几十个处理器,我们通过上面这种方式来处理显然是不合理的,而且如果当我们想要修改责任链中处理器的执行顺序时,我们也必须要修改代码,虽然一定程度上实现了对请求者和接受者之间的解耦,但是上面这种做法是不是和实际开发的,因此我对此做了优化处理,我们编写一个对责任链的统一处理器类HandlerChain:
/**
* 处理器链管理类
*/
public class HandlerChain {
// 责任链中的处理器优先级排序集合
private List<Integer> offsets = new ArrayList<>();
// 责任链的处理类集合
private final Map<Integer,Handler> handlerList = new LinkedHashMap<>();
public HandlerChain(String packageName) {
try {
generateInstances(packageName);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 扫描指定的包名,获取包下所有的类并创建实例
* @param packageName
* @throws IOException
*/
public void generateInstances(String packageName) throws IOException {
List<Class<?>> allClass = getAllClass(packageName);
if (CollectionUtils.isEmpty(allClass)) {
return;
}
// 获取处理器优先级集合
offsets = allClass.stream().map(t -> t.getAnnotation(HandlerAnnotation.class).offset()).sorted().collect(Collectors.toList());
// 通过反射机制来生成处理器实例
allClass.forEach(clazz -> {
HandlerAnnotation annotation = clazz.getAnnotation(HandlerAnnotation.class);
try {
handlerList.put(annotation.offset(), (Handler) clazz.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
// 指定处理器的下一个处理器实例
handlerList.forEach( (k, v) -> {
int size = offsets.size() - 1;
int index = offsets.indexOf(k);
if (size > index) {
v.setNextHandler(handlerList.get(offsets.get(index + 1)));
}
});
}
/**
* 扫描指定包,返回包下所有类的类对象集合
* @param packageName
* @return
* @throws IOException
*/
private List<Class<?>> getAllClass(String packageName) throws IOException {
List<Class<?>> list = new ArrayList<>();
// 获取类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
while (resources.hasMoreElements()) {
File file = new File(resources.nextElement().getFile());
// 获取当前文件夹下的所有文件信息
File[] files = file.listFiles();
// 获取类的全限类名
Arrays.asList(files).forEach( f -> {
if (f.getName().endsWith(".class")) {
try {
String s = f.getName();
String[] split = s.split("\\.");
list.add(Class.forName(packageName + "." + split[0]));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
});
}
return list;
}
/**
* 处理器执行类(总是从链首开始执行)
* @param request
*/
public void handler(MyRequest request) {
handlerList.get(offsets.get(0)).handler(request);
}
我们在TestChain类中测试一下:
/**
* 测试责任链模式
* 当你想让一个及以上的对象有机会能够处理某个请求的时候,就是用此模式
*/
public class TestChain {
public static void main(String[] args) throws IOException {
MyRequest request = new MyRequest();
request.setName("bibi");
request.setAge(12);
HandlerChain handlerChain = new HandlerChain("com.feifei.demo.chain.handler");
handlerChain.handler(request);
}
}
运行结果如下所示:
我们修改下处理器的执行顺序,将FirstHandler中的offset改为2,SecondHandler中的offset改为1,我们查询运行结果:
在这里,我们就实现了对责任链处理器的统一管理。当然,这个程序中还有很多细节需要优化,比如说,执行顺序不应该出现相等的情况已经部分对象的执行需要加入非空判断等等,但是大体的实现机制就是这样,后续会对其完善。