Java、Google Guava都提供了观察者模式的实现框架。Java提供的框架比较简单,只包含java.util.Observable和java.util.Observer两个类。Google Guava提供的框架功能比较完善和强大:通过EventBus事件总线来实现观察者模式。实际上,Spring也提供了观察者模式的实现框架。
Spring中实现的观察者模式包含三部分:Event事件(相当于消息)、Listener监听者(相当于观察者)、Publisher发送者(相当于被观察者)。我们通过一个例子来看下,Spring提供的观察者模式是怎么使用的。
// Event事件
public class DemoEvent extends ApplicationEvent {
private String message;
public DemoEvent(Object source, String message) {
super(source);
}
public String getMessage() {
return this.message;
}
}
// Listener监听者
@Component
public class DemoListener implements ApplicationListener {
@Override
public void onApplicationEvent(DemoEvent demoEvent) {
String message = demoEvent.getMessage();
System.out.println(message);
}
}
// Publisher发送者
@Component
public class DemoPublisher {
@Autowired
private ApplicationContext applicationContext;
public void publishEvent(DemoEvent demoEvent) {
this.applicationContext.publishEvent(demoEvent);
}
}
框架使用起来并不复杂,主要包含三部分工作:定义一个继承ApplicationEvent的事件(DemoEvent);定义一个实现了ApplicationListener的监听器(DemoListener);定义一个发送者(DemoPublisher),发送者调用ApplicationContext来发送事件消息。
ApplicationContext只是一个接口,具体的代码实现包含在它的实现类AbstractApplicationContext中。真正的消息发送,实际上是通过ApplicationEventMulticaster这个类来完成的。也就是multicastEvent()这个消息发送函数。
借助Spring提供的观察者模式的骨架代码,如果我们要在Spring下实现某个事件的发送和监听,只需要做很少的工作,定义事件、定义监听器、往ApplicationContext中发送事件就可以了,剩下的工作都由Spring框架来完成。实际上,这也体现了Spring框架的扩展性,也就是在不需要修改任何代码的情况下,扩展新的事件和监听。
利用模板模式,Spring能让用户定制Bean的创建过程。Spring Bean的创建过程,可以大致分为两大步:对象的创建和对象的初始化。
实际上,这里的模板模式的实现,并不是标准的抽象类的实现方式,而是有点类似我们前面讲到的Callback回调的实现方式,也就是将要执行的函数封装成对象(比如,初始化方法封装成InitializingBean对象),传递给模板(BeanFactory)来执行。
在Spring MVC中,定义一个Controller最常用的方式是,通过@Controller注解来标记某个类是Controller类,通过@RequesMapping注解来标记函数对应的URL。不过,定义一个Controller远不止这一种方法。我们还可以通过让类实现Controller接口或者Servlet接口,来定义一个Controller。
// 方法一:通过@Controller、@RequestMapping来定义
@Controller
public class DemoController {
@RequestMapping("/employname")
public ModelAndView getEmployeeName() {
ModelAndView model = new ModelAndView("Greeting");
model.addObject("message", "Dinesh");
return model;
}
}
// 方法二:实现Controller接口 + xml配置文件:配置DemoController与URL的对应关系
public class DemoController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
ModelAndView model = new ModelAndView("Greeting");
model.addObject("message", "Dinesh Madhwal");
return model;
}
}
// 方法三:实现Servlet接口 + xml配置文件:配置DemoController类与URL的对应关系
public class DemoController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello World.");
}
}
在应用启动的时候,Spring容器会加载这些Controller类,并且解析出URL对应的处理函数,封装成Handler对象,存储到HandlerMapping对象中。当有请求到来的时候,DispatcherServlet从HanderMapping中,查找请求URL对应的Handler,然后调用执行Handler对应的函数代码,最后将执行结果返回给客户端。但是,不同方式定义的Controller,其函数的定义(函数名、入参、返回值等)是不统一的。如上示例代码所示,方法一中的函数的定义很随意、不固定,方法二中的函数定义是handleRequest()、方法三中的函数定义是service()(看似是定义了doGet()、doPost(),实际上,这里用到了模板模式,Servlet中的service()调用了doGet()或doPost()方法,DispatcherServlet调用的是service()方法)。DispatcherServlet需要根据不同类型的Controller,调用不同的函数。
Spring定义了统一的接口HandlerAdapter,并且对每种Controller定义了对应的适配器类。这些适配器类包括:RequestMappingHandlerAdapter、SimpleControllerHandlerAdapter、SimpleServletHandlerAdapter等。
Spring AOP是通过动态代理来实现的。熟悉Java的同学应该知道,具体到代码实现,Spring支持两种动态代理实现方式,一种是JDK提供的动态代理实现方式,另一种是Cglib提供的动态代理实现方式。针对不同的被代理类,Spring会在运行时动态地选择不同的动态代理实现方式。这个应用场景实际上就是策略模式的典型应用场景。
在策略模式中,策略的创建一般通过工厂方法来实现。对应到Spring源码,AopProxyFactory是一个工厂类接口,DefaultAopProxyFactory是一个默认的工厂类,用来创建AopProxy对象。
策略模式的典型应用场景,一般是通过环境变量、状态值、计算结果等动态地决定使用哪个策略。
Spring Cache提供了一套抽象的Cache接口。使用它我们能够统一不同缓存实现(Redis、Google Guava…)的不同的访问方式。Spring中针对不同缓存实现的不同缓存访问类,都依赖这个接口,比如:EhCacheCache、GuavaCache、NoOpCache、RedisCache、JCacheCache、ConcurrentMapCache、CaffeineCache。
为了管理多个缓存,Spring还提供了缓存管理功能。不过,它包含的功能很简单,主要有这样两部分:一个是根据缓存名字(创建Cache对象的时候要设置name属性)获取Cache对象;另一个是获取管理器管理的所有缓存的名字列表。
组合模式主要应用在能表示成树形结构的一组数据上。树中的结点分为叶子节点和中间节点两类。对应到Spring源码,EhCacheManager、SimpleCacheManager、NoOpCacheManager、RedisCacheManager等表示叶子节点,CompositeCacheManager表示中间节点。叶子节点包含的是它所管理的Cache对象,中间节点包含的是其他CacheManager管理器,既可以是CompositeCacheManager,也可以是具体的管理器,比如EhCacheManager、RedisManager等。
CompositeCacheManger中的getCache()、getCacheNames()两个函数的实现都用到了递归。这正是树形结构最能发挥优势的地方。
缓存一般都是配合数据库来使用的。如果写缓存成功,但数据库事务回滚了,那缓存中就会有脏数据。为了解决这个问题,我们需要将缓存的写操作和数据库的写操作,放到同一个事务中,要么都成功,要么都失败。
实现这样一个功能,Spring使用到了装饰器模式。TransactionAwareCacheDecorator增加了对事务的支持,在事务提交、回滚的时候分别对Cache的数据进行处理。TransactionAwareCacheDecorator实现Cache接口,并且将所有的操作都委托给targetCache来实现,对其中的写操作添加了事务功能。这是典型的装饰器模式的应用场景和代码实现。
在Spring中,工厂模式最经典的应用莫过于实现IOC容器,对应的Spring源码主要是BeanFactory类和ApplicationContext相关类(AbstractApplicationContext、ClassPathXmlApplicationContext、FileSystemXmlApplicationContext…)。