常用来实现扩展特性的设计模式有:观察者模式、模板模式、职责链模式、策略模式等。今天,我们再剖析 Spring 框架为了支持可扩展特性用的 2 种设计模式:观察者模式和模板模式。
观察者模式在 Spring 中的应用 Java、Google Guava 都提供了观察者模式的实现框架。Java 提供的框架比较简单,只包含 java.util.Observable 和 java.util.Observer 两个类。Google Guava 提供的框架功能比较完善和强大:通过 EventBus 事件总线来实现观察者模式。实际上,Spring 也提供了观察者模式的实现框架。
Spring 中实现的观察者模式包含三部分:Event 事件(相当于消息)、Listener 监听者(相当于观察者)、Publisher 发送者(相当于被观察者)。
Event事件 1 2 3 4 5 6 7 8 9 10 11 12 13 public class DemoEvent extends ApplicationEvent { private final String message; public DemoEvent (Object source, String message) { super (source); this .message = message; } public String getMessage () { return message; } }
Listener监听者 1 2 3 4 5 6 7 public class DemoListener implements ApplicationListener <DemoEvent> { @Override public void onApplicationEvent (DemoEvent event) { String message = event.getMessage(); System.out.println(message); } }
Publisher发送者 1 2 3 4 5 6 7 8 9 10 11 12 13 @Component public class DemoPublisher { private final ApplicationContext applicationContext; public DemoPublisher (ApplicationContext applicationContext) { this .applicationContext = applicationContext; } public void publishEvent (DemoEvent demoEvent) { this .applicationContext.publishEvent(demoEvent); } }
从代码中,我们可以看出,框架使用起来并不复杂,主要包含三部分工作:定义一个继承 ApplicationEvent 的事件(DemoEvent);定义一个实现了 ApplicationListener 的监听器(DemoListener);定义一个发送者(DemoPublisher),发送者调用 ApplicationContext 来发送事件消息。
其中,ApplicationEvent 和 ApplicationListener 的代码实现都非常简单,内部并不包含太多属性和方法。实际上,它们最大的作用是做类型标识之用(继承自 ApplicationEvent 的类是事件,实现 ApplicationListener 的类是监听器)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public abstract class ApplicationEvent extends EventObject { private static final long serialVersionUID = 7099057708183571937L ; private final long timestamp = System.currentTimeMillis(); public ApplicationEvent (Object source) { super (source); } public final long getTimestamp () { return this .timestamp; } } public class EventObject implements java .io.Serializable { private static final long serialVersionUID = 5516075349620653480L ; protected transient Object source; public EventObject (Object source) { if (source == null ) throw new IllegalArgumentException ("null source" ); this .source = source; } public Object getSource () { return source; } public String toString () { return getClass().getName() + "[source=" + source + "]" ; } } public interface ApplicationListener <E extends ApplicationEvent > extends EventListener { void onApplicationEvent (E var1) ; }
在前面讲到观察者模式的时候,我们提到,观察者需要事先注册到被观察者(JDK 的实现方式)或者事件总线(EventBus 的实现方式)中。那在 Spring 的实现中,观察者注册到了哪里呢?又是如何注册的呢?
Spring把观察者注册到了 ApplicationContext 对象中。这里的 ApplicationContext 就相当于 Google EventBus 框架中的“事件总线”。ApplicationContext 这个类并不只是为观察者模式服务的。它底层依赖 BeanFactory(IOC 的主要实现类),提供应用启动、运行时的上下文信息,是访问这些信息的最顶层接口。
ApplicationContext 只是一个接口,具体的代码实现包含在它的实现类 AbstractApplicationContext 中。我把跟观察者模式相关的代码,摘抄到了下面。我们只需关注它是如何发送事件和注册监听者就好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 public abstract class AbstractApplicationContext extends ... { private final Set<ApplicationListener<?>> applicationListeners; public AbstractApplicationContext () { this .applicationListeners = new LinkedHashSet (); } public void publishEvent (ApplicationEvent event) { this .publishEvent(event, (ResolvableType)null ); } public void publishEvent (Object event) { this .publishEvent(event, (ResolvableType)null ); } protected void publishEvent (Object event, ResolvableType eventType) { Object applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent)event; } else { applicationEvent = new PayloadApplicationEvent (this , event); if (eventType == null ) { eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType(); } } if (this .earlyApplicationEvents != null ) { this .earlyApplicationEvents.add(applicationEvent); } else { this .getApplicationEventMulticaster().multicastEvent( (ApplicationEvent)applicationEvent, eventType); } if (this .parent != null ) { if (this .parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext)this .parent).publishEvent(event, eventType); } else { this .parent.publishEvent(event); } } } public void addApplicationListener (ApplicationListener<?> listener) { Assert.notNull(listener, "ApplicationListener must not be null" ); if (this .applicationEventMulticaster != null ) { this .applicationEventMulticaster.addApplicationListener(listener); } else { this .applicationListeners.add(listener); } } public Collection<ApplicationListener<?>> getApplicationListeners() { return this .applicationListeners; } protected void registerListeners () { Iterator var1 = this .getApplicationListeners().iterator(); while (var1.hasNext()) { ApplicationListener<?> listener = (ApplicationListener)var1.next(); this .getApplicationEventMulticaster().addApplicationListener(listener); } String[] listenerBeanNames = this .getBeanNamesForType(ApplicationListener.class, true , false ); String[] var7 = listenerBeanNames; int var3 = listenerBeanNames.length; for (int var4 = 0 ; var4 < var3; ++var4) { String listenerBeanName = var7[var4]; this .getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } Set<ApplicationEvent> earlyEventsToProcess = this .earlyApplicationEvents; this .earlyApplicationEvents = null ; if (earlyEventsToProcess != null ) { Iterator var9 = earlyEventsToProcess.iterator(); while (var9.hasNext()) { ApplicationEvent earlyEvent = (ApplicationEvent)var9.next(); this .getApplicationEventMulticaster().multicastEvent(earlyEvent); } } } }
从上面的代码中,我们发现,真正的消息发送,实际上是通过 ApplicationEventMulticaster 这个类来完成的。这个类的源码我只摘抄了最关键的一部分,也就是 multicastEvent() 这个消息发送函数。它通过线程池,支持异步非阻塞、同步阻塞这两种类型的观察者模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public void multicastEvent (ApplicationEvent event) { this .multicastEvent(event, this .resolveDefaultEventType(event)); } public void multicastEvent (final ApplicationEvent event, ResolvableType eventType) { ResolvableType type = eventType != null ? eventType : this .resolveDefaultEventType(event); Iterator var4 = this .getApplicationListeners(event, type).iterator(); while (var4.hasNext()) { final ApplicationListener<?> listener = (ApplicationListener)var4.next(); Executor executor = this .getTaskExecutor(); if (executor != null ) { executor.execute(new Runnable () { public void run () { SimpleApplicationEventMulticaster.this .invokeListener(listener, event); } }); } else { this .invokeListener(listener, event); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public void multicastEvent (ApplicationEvent event) { this .multicastEvent(event, this .resolveDefaultEventType(event)); } public void multicastEvent (final ApplicationEvent event, ResolvableType eventType) { ResolvableType type = eventType != null ? eventType : this .resolveDefaultEventType(event); Iterator var4 = this .getApplicationListeners(event, type).iterator(); while (var4.hasNext()) { final ApplicationListener<?> listener = (ApplicationListener)var4.next(); Executor executor = this .getTaskExecutor(); if (executor != null ) { executor.execute(new Runnable () { public void run () { SimpleApplicationEventMulticaster.this .invokeListener(listener, event); } }); } else { this .invokeListener(listener, event); } } }
借助 Spring 提供的观察者模式的骨架代码,如果我们要在 Spring 下实现某个事件的发送和监听,只需要做很少的工作,定义事件、定义监听器、往 ApplicationContext 中发送事件就可以了,剩下的工作都由 Spring 框架来完成。实际上,这也体现了 Spring 框架的扩展性,也就是在不需要修改任何代码的情况下,扩展新的事件和监听。
模板模式在 Spring 中的应用 我们来看下一下经常在面试中被问到的一个问题:请你说下 Spring Bean 的创建过程包含哪些主要的步骤。这其中就涉及模板模式。它也体现了 Spring 的扩展性。利用模板模式,Spring 能让用户定制 Bean 的创建过程。
Spring Bean 的创建过程,可以大致分为两大步:对象的创建和对象的初始化。
对象的创建是通过反射来动态生成对象,而不是 new 方法。不管是哪种方式,说白了,总归还是调用构造函数来生成对象,没有什么特殊的。对象的初始化有两种实现方式。一种是在类中自定义一个初始化函数,并且通过配置文件,显式地告知 Spring,哪个函数是初始化函数。我举了一个例子解释一下。如下所示,在配置文件中,我们通过 init-method 属性来指定初始化函数。
1 2 3 4 5 6 7 8 9 10 11 public class DemoClass { public void initDemo () { } } <bean id="demoBean" class="com.xzg.cd.DemoClass" init-method="initDemo" ></bean>
这种初始化方式有一个缺点,初始化函数并不固定,由用户随意定义,这就需要 Spring 通过反射,在运行时动态地调用这个初始化函数。而反射又会影响代码执行的性能,那有没有替代方案呢?
Spring 提供了另外一个定义初始化函数的方法,那就是让类实现 Initializingbean 接口。这个接口包含一个固定的初始化函数定义(afterPropertiesSet() 函数)。Spring 在初始化 Bean 的时候,可以直接通过 bean.afterPropertiesSet() 的方式,调用 Bean 对象上的这个函数,而不需要使用反射来调用了。我举个例子解释一下,代码如下所示。
1 2 3 4 5 6 7 8 9 10 public class DemoClass implements InitializingBean { @Override public void afterPropertiesSet () throws Exception { } } <bean id="demoBean" class="com.xzg.cd.DemoClass" ></bean>
尽管这种实现方式不会用到反射,执行效率提高了,但业务代码(DemoClass)跟框架代码(InitializingBean)耦合在了一起。框架代码侵入到了业务代码中,替换框架的成本就变高了。所以,我并不是太推荐这种写法。
实际上,在 Spring 对 Bean 整个生命周期的管理中,还有一个跟初始化相对应的过程,那就是 Bean 的销毁过程。我们知道,在 Java 中,对象的回收是通过 JVM 来自动完成的。但是,我们可以在将 Bean 正式交给 JVM 垃圾回收前,执行一些销毁操作(比如关闭文件句柄等等)。
销毁过程跟初始化过程非常相似,也有两种实现方式。一种是通过配置 destroy-method 指定类中的销毁函数,另一种是让类实现 DisposableBean 接口。因为 destroy-method、DisposableBean 跟 init-method、InitializingBean 非常相似。
实际上,Spring 针对对象的初始化过程,还做了进一步的细化,将它拆分成了三个小步骤:
初始化前置操作
初始化
初始化后置操作。
其中,中间的初始化操作就是我们刚刚讲的那部分,初始化的前置和后置操作,定义在接口 BeanPostProcessor 中。BeanPostProcessor 的接口定义如下所示:
1 2 3 4 5 6 public interface BeanPostProcessor { Object postProcessBeforeInitialization (Object var1, String var2) throws BeansException; Object postProcessAfterInitialization (Object var1, String var2) throws BeansException; }
我们再来看下,如何通过 BeanPostProcessor 来定义初始化前置和后置操作?
我们只需要定义一个实现了 BeanPostProcessor 接口的处理器类,并在配置文件中像配置普通 Bean 一样去配置就可以了。Spring 中的 ApplicationContext 会自动检测在配置文件中实现了 BeanPostProcessor 接口的所有 Bean,并把它们注册到 BeanPostProcessor 处理器列表中。在 Spring 容器创建 Bean 的过程中,Spring 会逐一去调用这些处理器。
通过上面的分析,我们基本上弄清楚了 Spring Bean 的整个生命周期(创建加销毁)。针对这个过程,下面有张过程图:
一般情况,模板模式是需要定义一个包含模板方法的抽象模板类,以及定义子类实现模板方法。
这里的模板模式的实现,并不是标准的抽象类的实现方式,而是有点类似 Callback 回调的实现方式,也就是将要执行的函数封装成对象(比如,初始化方法封装成 InitializingBean 对象),传递给模板(BeanFactory)来执行。