综合:spring中涉及到的设计模式
基础
工厂设计模式 :
Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
代理设计模式 :
Spring AOP 功能的实现。
单例设计模式 :
Spring 中的 Bean 默认都是单例的
模板方法模式 :
Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
包装器设计模式 :
我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。
这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
观察者模式:
Spring 事件驱动模型就是观察者模式很经典的一个应用。
适配器模式 :
Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
1,工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
2,单例模式:Bean默认为单例模式。
3,代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
4,模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
5,观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现--ApplicationListener。
模板方法
模板方法模式是一种行为设计模式,它定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的实现方式。
Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
一般情况下,我们都是使用继承的方式来实现模板模式,
但是 Spring 并没有使用这种方式,而是使用Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。
public abstract class Template {
//这是我们的模板方法
public final void TemplateMethod(){
PrimitiveOperation1();
PrimitiveOperation2();
PrimitiveOperation3();
}
protected void PrimitiveOperation1(){
//当前类实现
}
//被子类实现的方法
protected abstract void PrimitiveOperation2();
protected abstract void PrimitiveOperation3();
}
public class TemplateImpl extends Template {
@Override
public void PrimitiveOperation2() {
//当前类实现
}
@Override
public void PrimitiveOperation3() {
//当前类实现
}
}
简单工厂模式
又叫做静态工厂方法(StaticFactory Method)模式,但不属于23种GOF设计模式之一。
简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。
spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。
如下配置,就是在 HelloItxxz 类中创建一个 itxxzBean。
<beans>
<bean id="singletonBean" class="com.itxxz.HelloItxxz">
<constructor-arg>
<value>Hello! 这是singletonBean</value>
</constructor-arg>
</ bean>
<bean id="itxxzBean" class="com.itxxz.HelloItxxz" singleton="false">
<constructor-arg>
<value>Hello! 这是itxxzBean! </value>
</constructor-arg>
</bean>
</beans>
工厂方法模式
通常由应用程序直接使用new创建新的对象,为了将对象的创建和使用相分离,采用工厂模式,即应用程序将对象的创建及初始化职责交给工厂对象。
一般情况下,应用程序有自己的工厂对象来创建bean.如果将应用程序自己的工厂对象交给Spring管理,那么Spring管理的就不是普通的bean,而是工厂Bean。
就以工厂方法中的静态方法为例讲解一下:
import java.util.Random;
public class StaticFactoryBean {
public static Integer createRandom() {
return new Integer(new Random().nextInt());
}
}
建一个config.xm配置文件,将其纳入Spring容器来管理,需要通过factory-method指定静态方法名称:
<bean id="random"
class="example.chapter3.StaticFactoryBean" factory-method="createRandom"
scope="prototype"
/>
测试:
public static void main(String[] args) {
//调用getBean()时,返回随机数.如果没有指定factory-method,会返回StaticFactoryBean的实例,即返回工厂Bean的实例
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("config.xml"));
System.out.println("我是IT学习者创建的实例:"+factory.getBean("random").toString());
}
单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory。但没有从构造器级别去控制单例,这是因为spring管理的是是任意的java对象。
核心提示点:Spring下默认的bean均为singleton,可以通过singleton=“true|false” 或者 scope="?"来指定。
适配器模式
在Spring的Aop中,使用的Advice(通知)来增强被代理类的功能。
Spring实现这一AOP功能的原理就使用代理模式(1、JDK动态代理。2、CGLib字节码生成技术代理。)
对类进行方法级别的切面增强,即,生成被代理类的代理类, 并在代理类的方法前,设置拦截器,通过执行拦截器重的内容增强了代理方法的功能,实现的面向切面编程。
Adapter类接口:Target
public interface AdvisorAdapter {
boolean supportsAdvice(Advice advice);
MethodInterceptor getInterceptor(Advisor advisor);
} MethodBeforeAdviceAdapter类,Adapter
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
包装器模式
在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。
我们以往在spring和hibernate框架中总是配置一个数据源,
因而sessionFactory的dataSource属性总是指向这个数据源并且恒定不变,所有DAO在使用sessionFactory的时候都是通过这个数据源访问数据库。
但是现在,由于项目的需要,我们的DAO在访问sessionFactory的时候都不得不在多个数据源中不断切换,
问题就出现了:
如何让sessionFactory在执行数据持久化的时候,根据客户的需求能够动态切换不同的数据源?
我们能不能在spring的框架下通过少量修改得到解决?是否有什么设计模式可以利用呢?
首先想到在spring的applicationContext中配置所有的dataSource。这些dataSource可能是各种不同类型的,
比如不同的数据库:Oracle、SQL Server、MySQL等,
也可能是不同的数据源:比如apache 提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等。
然后sessionFactory根据客户的每次请求,将dataSource属性设置成不同的数据源,以到达切换数据源的目的。
spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。
基本上都是动态地给一个对象添加一些额外的职责。
代理模式
为其他对象提供一种代理以控制对这个对象的访问。 从结构上来看和Decorator模式类似,但Proxy是控制,更像是一种对功能的限制,而Decorator是增加职责。
spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxy和Cglib2AopProxy。
观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
spring中Observer模式常用的地方是listener的实现。如ApplicationListener。
策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
spring中在实例化对象的时候用到Strategy模式
Spring中如何使用责任链模式
关于责任链模式,其有两种形式,
一种是通过外部调用的方式对链的各个节点调用进行控制,从而进行链的各个节点之间的切换;
另一种是链的每个节点自由控制是否继续往下传递链的进度,这种比较典型的使用方式就是Netty中的责任链模式。
本文主要讲解我们如何在Spring中使用这两种责任链模式。
1. 外部控制模式
对于外部控制的方式,这种方式比较简单,链的每个节点只需要专注于各自的逻辑即可,而当前节点调用完成之后是否继续调用下一个节点,这个则由外部控制逻辑进行。
这里我们以一个过滤器的实现逻辑为例进行讲解,在平常工作中,我们经常需要根据一系列的条件对某个东西进行过滤,比如任务服务的设计,在执行某个任务时,其需要经过诸如时效性检验,风控拦截,任务完成次数等过滤条件的检验之后才能判断当前任务是否能够执行,只有在所有的过滤条件都完成之后,我们才能执行该任务。
那么这里我们就可以抽象出一个Filter接口,其设计如下
public interface Filter {
/**
* 用于对各个任务节点进行过滤
*/
boolean filter(Task task);
}
这里的Filter.filter()方法只有一个参数Task,主要就是控制当前task是否需要被过滤掉,其有一个boolean类型的返回值,通过该返回值以告知外部控制逻辑是否需要将该task过滤掉。对于该接口的子类,我们只需要将其声明为Spring所管理的一个bean即可:
// 时效性检验
@Component
public class DurationFilter implements Filter {
@Override
public boolean filter(Task task) {
System.out.println("时效性检验");
return true;
}
}
// 风控拦截
@Component
public class RiskFilter implements Filter {
@Override
public boolean filter(Task task) {
System.out.println("风控拦截");
return true;
}
}
// 次数限制校验
@Component
public class TimesFilter implements Filter {
@Override
public boolean filter(Task task) {
System.out.println("次数限制检验");
return true;
}
}
上面我们模拟声明了三个Filter的子类,用于设计一系列的控制当前task是否需要被过滤的逻辑,结构上的逻辑其实比较简单,主要就是需要将其声明为Spring所管理的一个bean。下面是我们的控制逻辑:
@Service
public class ApplicationService {
@Autowired
private Listfilters;
public void mockedClient() {
Task task = new Task(); // 这里task一般是通过数据库查询得到的
for (Filter filter : filters) {
if (!filter.filter(task)) {
return;
}
}
// 过滤完成,后续是执行任务的逻辑
}
}
在上述的控制逻辑中,对于过滤器的获取,只需要通过Spring的自动注入即可,这里注入的是一个List,也就是说,如果我们有新的Filter实例需要参与责任链的过滤,只需要将其声明为一个Spring容器所管理的bean即可。
这种责任链设计方式的优点在于链的控制比较简单,只需要实现一个统一的接口即可,其基本上能够满足大部分的逻辑控制,但是对于某些需要动态调整链的需求其就无能为力了。比如在执行到某个节点之后需要动态的判断是否执行下一个节点,或者说要执行某些分叉的节点等等。这个时候我们就需要将链节点的传递工作交由各个节点进行。
2. 节点控制模式
对于节点控制调用的方式,其主要有三个控制点:Handler,HandlerContext和Pipeline。Handler中是用于编写具体的业务代码的;HandlerContext则主要是用于对Handler进行包裹,并且用于控制进行下一个节点的调用的;Pipeline则主要是用于控制整体的流程调用的,比如对于任务的执行,其有任务的查询,任务的过滤和执行任务等等流程,这些流程整体的逻辑控制就是由Pipeline来控制的,在每个流程中又包含了一系列的子流程,这些子流程则是由一个个的HandlerContext和Handler进行梳理的。这种责任链的控制方式整体逻辑如下图所示:
从图中可以看出,我们将整个流程通过Pipeline对象进行了抽象,这里主要分为了三个步骤:查询task,过滤task和执行task。在每个步骤中,我们都使用了一系列的链式调用。图中需要注意的是,在每次调用链的下一个节点的时候,我们都是通过具体的Handler进行的,也就是说是否进行链的下一个节点的调用,我们是通过业务实现方来进行动态控制的。
关于该模式的设计,我们首先需要强调的就是Handler接口的设计,其设计如下所示:
public interface Handler {
/**
* 处理接收到前端请求的逻辑
*/
default void receiveTask(HandlerContext ctx, Request request) {
ctx.fireTaskReceived(request);
}
/**
* 查询到task之后,进行task过滤的逻辑
*/
default void filterTask(HandlerContext ctx, Task task) {
ctx.fireTaskFiltered(task);
}
/**
* task过滤完成之后,处理执行task的逻辑
*/
default void executeTask(HandlerContext ctx, Task task) {
ctx.fireTaskExecuted(task);
}
/**
* 当实现的前面的方法抛出异常时,将使用当前方法进行异常处理,这样可以将每个handler的异常
* 都只在该handler内进行处理,而无需额外进行捕获
*/
default void exceptionCaught(HandlerContext ctx, Throwable e) {
throw new RuntimeException(e);
}
/**
* 在整个流程中,保证最后一定会执行的代码,主要是用于一些清理工作
*/
default void afterCompletion(HandlerContext ctx) {
ctx.fireAfterCompletion(ctx);
}
}
这里的Handler接口主要是对具体的业务逻辑的一个抽象,对于该Handler主要有如下几点需要说明:
在前面图中Pipeline的每个层级中对应于该Handler都有一个方法,在需要进行具体的业务处理的时候,用户只需要声明一个bean,具体实现某个当前业务所需要处理的层级的方法即可,而无需管其他的逻辑;
每个层级的方法中,第一个参数都是一个HandlerContext类型的,该参数主要是用于进行流程控制的,比如是否需要将当前层级的调用链往下继续传递,这里链的传递工作主要是通过ctx.fireXXX()方法进行的;
每个层级的方法都有默认实现,默认实现方式就是将链的调用继续往下进行传递;
每个Handler中都有一个exceptionCaught()方法和afterCompletion()方法,这两个方法分别用于异常控制和所有调用完成后的清理的,这里的异常控制主要是捕获当前Handler中的异常,而afterCompletion()方法则会保证在所有步骤之后一定会进行调用的,无论是否抛出异常;
对于Handler的使用,我们希望能够达到的目的是,适用方只需要实现该接口,并且使用某个注解来将其标志为Spring的bean即可,而无需管整个Pipeline的组装和流程控制。通过这种方式,我们即保留了每个Spring提供给我们的便利性,也使用了Pipeline模式的灵活性。
上述流程代码中,我们注意到,每个层级的方法中都有一个HandlerContext用于传递链相关的控制信息,这里我们来看一下其源码:
@Component
@Scope("prototype")
public class HandlerContext {
HandlerContext prev;
HandlerContext next;
Handler handler;
private Task task;
public void fireTaskReceived(Request request) {
invokeTaskReceived(next(), request);
}
/**
* 处理接收到任务的事件
*/
static void invokeTaskReceived(HandlerContext ctx, Request request) {
if (ctx != null) {
try {
ctx.handler().receiveTask(ctx, request);
} catch (Throwable e) {
ctx.handler().exceptionCaught(ctx, e);
}
}
}
public void fireTaskFiltered(Task task) {
invokeTaskFiltered(next(), task);
}
/**
* 处理任务过滤事件
*/
static void invokeTaskFiltered(HandlerContext ctx, Task task) {
if (null != ctx) {
try {
ctx.handler().filterTask(ctx, task);
} catch (Throwable e) {
ctx.handler().exceptionCaught(ctx, e);
}
}
}
public void fireTaskExecuted(Task task) {
invokeTaskExecuted(next(), task);
}
/**
* 处理执行任务事件
*/
static void invokeTaskExecuted(HandlerContext ctx, Task task) {
if (null != ctx) {
try {
ctx.handler().executeTask(ctx, task);
} catch (Exception e) {
ctx.handler().exceptionCaught(ctx, e);
}
}
}
public void fireAfterCompletion(HandlerContext ctx) {
invokeAfterCompletion(next());
}
static void invokeAfterCompletion(HandlerContext ctx) {
if (null != ctx) {
ctx.handler().afterCompletion(ctx);
}
}
private HandlerContext next() {
return next;
}
private Handler handler() {
return handler;
}
}
在HandlerContext中,我们需要说明如下几点:
之前Handler接口默认实现的ctx.fireXXX()方法,在这里都委托给了对应的invokeXXX()方法进行调用,而且我们需要注意到,在传递给invokeXXX()方法的参数里,传入的HandlerContext对象都是通过next()方法获取到的。也就是说我们在Handler中调用ctx.fireXXX()方法时,都是在调用当前handler的下一个handler对应层级的方法,通过这种方式我们就实现了链的往下传递。
在上一点中我们说到,在某个Handler中如果想让链往下传递,只需要调用ctx.fireXXX()方法即可,也就是说,如果我们在某个Handler中,如果根据业务,当前层级已经调用完成,而无需调用后续的Handler,那么我们就不需要调用ctx.fireXXX()方法即可;
在HandlerContext中,我们也实现了invokeXXX()方法,该方法的主要作用是供给外部的Pipeline进行调用的,以开启每个层级的链;
在每个invokeXXX()方法中,我们都使用try…catch将当前层级的调用抛出的异常给捕获了,然后调用ctx.handler().exceptionCaught()方法处理该异常,这也就是我们前面说的,如果想处理当前Handler中的异常,只需要实现该Handler中的exceptionCaught()方法即可,异常捕获流程就是在这里的HandlerContext中进行处理的;
在HandlerContext的声明处,我们需要注意到,其使用了@Component和@Scope("prototype")注解进行标注了,这说明我们的HandlerContext是由Spring所管理的一个bean,并且由于我们每一个Handler实际上都由一个HandlerContext维护着,所以这里必须声明为prototype类型。
通过这种方式,我们的HandlerContext也就具备了诸如Spring相关的bean的功能,也就能够根据业务需求进行一些额外的处理了;
前面我们讲解了Handler和HandlerContext的具体实现,以及实现的过程中需要注意的问题,下面我们就来看一下进行流程控制的Pipeline是如何实现的,如下是Pipeline接口的定义:
public interface Pipeline {
Pipeline fireTaskReceived();
Pipeline fireTaskFiltered();
Pipeline fireTaskExecuted();
Pipeline fireAfterCompletion();
}
这里 主要是定义了一个Pipeline接口,该接口定义了一系列的层级调用,是每个层级的入口方法。如下是该接口的一个实现类:
@Component("pipeline")
@Scope("prototype")
public class DefaultPipeline implements Pipeline, ApplicationContextAware, InitializingBean {
// 创建一个默认的handler,将其注入到首尾两个节点的HandlerContext中,其作用只是将链往下传递
private static final Handler DEFAULT_HANDLER = new Handler() {};
// 将ApplicationContext注入进来的主要原因在于,HandlerContext是prototype类型的,因而需要
// 通过ApplicationContext.getBean()方法来获取其实例
private ApplicationContext context;
// 创建一个头结点和尾节点,这两个节点内部没有做任何处理,只是默认的将每一层级的链往下传递,
// 这里头结点和尾节点的主要作用就是用于标志整个链的首尾,所有的业务节点都在这两个节点中间
private HandlerContext head;
private HandlerContext tail;
// 用于业务调用的request对象,其内部封装了业务数据
private Request request;
// 用于执行任务的task对象
private Task task;
// 最初始的业务数据需要通过构造函数传入,因为这是驱动整个pipeline所需要的数据,
// 一般通过外部调用方的参数进行封装即可
public DefaultPipeline(Request request) {
this.request = request;
}
// 这里我们可以看到,每一层级的调用都是通过HandlerContext.invokeXXX(head)的方式进行的,
// 也就是说我们每一层级链的入口都是从头结点开始的,当然在某些情况下,我们也需要从尾节点开始链
// 的调用,这个时候传入tail即可。
@Override
public Pipeline fireTaskReceived() {
HandlerContext.invokeTaskReceived(head, request);
return this;
}
// 触发任务过滤的链调用
@Override
public Pipeline fireTaskFiltered() {
HandlerContext.invokeTaskFiltered(head, task);
return this;
}
// 触发任务执行的链执行
@Override
public Pipeline fireTaskExecuted() {
HandlerContext.invokeTaskExecuted(head, task);
return this;
}
// 触发最终完成的链的执行
@Override
public Pipeline fireAfterCompletion() {
HandlerContext.invokeAfterCompletion(head);
return this;
}
// 用于往Pipeline中添加节点的方法,读者朋友也可以实现其他的方法用于进行链的维护
void addLast(Handler handler) {
HandlerContext handlerContext = newContext(handler);
tail.prev.next = handlerContext;
handlerContext.prev = tail.prev;
handlerContext.next = tail;
tail.prev = handlerContext;
}
// 这里通过实现InitializingBean接口来达到初始化Pipeline的目的,可以看到,这里初始的时候
// 我们通过ApplicationContext实例化了两个HandlerContext对象,然后将head.next指向tail节点,
// 将tail.prev指向head节点。也就是说,初始时,整个链只有头结点和尾节点。
@Override
public void afterPropertiesSet() throws Exception {
head = newContext(DEFAULT_HANDLER);
tail = newContext(DEFAULT_HANDLER);
head.next = tail;
tail.prev = head;
}
// 使用默认的Handler初始化一个HandlerContext
private HandlerContext newContext(Handler handler) {
HandlerContext context = this.context.getBean(HandlerContext.class);
context.handler = handler;
return context;
}
// 注入ApplicationContext对象
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.context = applicationContext;
}
}
关于DefaultPipeline的实现,主要有如下几点需要说明:
DefaultPipeline使用@Component和@Scope("prototype")注解进行了标注,前一个注解用于将其声明为一个Spring容器所管理的bean,而后一个注解则用于表征DefaultPipeline是一个多例类型的,很明显,这里的Pipeline是有状态的。这里需要进行说明的是,"有状态"主要是因为我们可能会根据业务情况动态的调整个链的节点情况,而且这里的Request和Task对象都是与具体的业务相关的,因而必须声明为prototype类型;
上面的示例中,Request对象是通过构造Pipeline对象的时候传进来的,而Task对象则是在Pipeline的流转过程中生成的,这里比如通过完成fireTaskReceived()链的调用之后,就需要通过外部请求Request得到一个Task对象,从而进行整个Pipeline的后续处理;
这里我们已经实现了Pipeline,HandlerContext和Handler,知道这些bean都是被Spring所管理的bean,那么我们接下来的问题主要在于如何进行整个链的组装。这里的组装方式比较简单,其主要需要解决两个问题:
对于后续写业务代码的人而言,其只需要实现一个Handler接口即可,而无需处理与链相关的所有逻辑,因而我们需要获取到所有实现了Handler接口的bean;
将实现了Handler接口的bean通过HandlerContext进行封装,然后将其添加到Pipeline中。
这里的第一个问题比较好处理,因为通过ApplicationContext就可以获取实现了某个接口的所有bean,而第二个问题我们可以通过声明一个实现了BeanPostProcessor接口的类来实现。如下是其实现代码:
@Component
public class HandlerBeanProcessor implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext context;
// 该方法会在一个bean初始化完成后调用,这里主要是在Pipeline初始化完成之后获取所有实现了
// Handler接口的bean,然后通过调用Pipeline.addLast()方法将其添加到pipeline中
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof DefaultPipeline) {
DefaultPipeline pipeline = (DefaultPipeline) bean;
MaphandlerMap = context.getBeansOfType(Handler.class);
handlerMap.forEach((name, handler) -> pipeline.addLast(handler));
}
return bean;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.context = applicationContext;
}
}
这里我们整个链的维护工作就已经完成,可以看到,现在基本上已经实现了前面图中整个链式流程的控制。这里需要说明的一点是,上面的HandlerBeanProcessor.postProcessAfterInitialization()方法的执行是在InitializingBean.afterPropertySet()方法之后执行的,也就是说这里HandlerBeanProcessor在执行时,整个Pipeline是已经初始化完成了的。下面我们来看一下外部客户端如何进行整个链是流程的控制:
@Service
public class ApplicationService {
@Autowired
private ApplicationContext context;
public void mockedClient() {
Request request = new Request(); // request一般是通过外部调用获取
Pipeline pipeline = newPipeline(request);
try {
pipeline.fireTaskReceived();
pipeline.fireTaskFiltered();
pipeline.fireTaskExecuted();
} finally {
pipeline.fireAfterCompletion();
}
}
private Pipeline newPipeline(Request request) {
return context.getBean(DefaultPipeline.class, request);
}
}
这里我们模拟了一个客户端的调用,首先创建了一个Pipeline对象,然后依次调用其各个层级的方法,并且这里我们使用try…finally结构来保证Pipeline.fireAfterCompletion()方法一定会执行。如此我们就完成了整个责任链模式的构造。这里我们使用前面用到的时效性过滤的filter来作为示例来实现一个Handler:
@Component
public class DurationHandler implements Handler {
@Override
public void filterTask(HandlerContext ctx, Task task) {
System.out.println("时效性检验");
ctx.fireTaskFiltered(task);
}
}
关于这里的具体业务Handler我们需要说明的有如下几点:
该Handler必须使用@Conponent注解来将其声明为Spring容器所管理的一个bean,这样我们前面实现的HandlerBeanProcessor才能将其动态的添加到整个Pipeline中;
在每个Handler中,需要根据当前的业务需要来实现具体的层级方法,比如这里是进行时效性检验,就是"任务过滤"这一层级的逻辑,因为时效性检验通过我们才能执行这个task,因而这里需要实现的是Handler.filterTask()方法,如果我们需要实现的是执行task的逻辑,那么需要实现的就是Handler.executeTask()方法;
在实现完具体的业务逻辑之后,我们可以根据当前的业务需要看是否需要将当前层级的链继续往下传递,也就是这里的ctx.fireTaskFiltered(task);方法的调用,我们可以看前面HandlerContext.fireXXX()方法就是会获取当前节点的下一个节点,然后进行调用。如果根据业务需要,不需要将链往下传递,那么就不需要调用ctx.fireTaskFiltered(task);;
3. 小结
如此,我们就通过两种方式实现了责任链模式,而且我们实现的责任链模式都是符合"开-闭"原则的,也就是说后续我们要为链添加新的节点的时候,只需要根据规范实现相应的接口即可,而无需处理链的维护相关的工作。
关于第二种实现方式,这里我们并没有实现链节点的顺序控制功能,以及如何动态的添加或删除链的节点,更有甚者,如果控制每个Handler是单例的还是多例的。
当然,有了前面的框架,这些点实现起来也比较简单,这里权当起到一个抛砖引玉的作用,读者朋友可根据自己的需要进行实现。
参考:
必须掌握!你知道 Spring 中运用的 9 种设计模式吗 ?
Spring 的 Controller 是单例还是多例?多少人答对?
Spring 的 Controller 是单例还是多例?怎么保证并发的安全?
Spring 为啥默认把bean设计成单例的?这篇讲的明明白白的
Spring 的 Controller 是单例还是多例?怎么保证并发的安全
Spring 为啥默认把 bean 设计成单例的?这篇讲的明明白白
Spring 的 Controller 是单例还是多例?多少人答对?
Spring 的 Controller 是单例还是多例?怎么保证并发的安全