本文结合spingboot的源码描述一下spring中运用到的几种点典型的设计模式。

工厂模式

本身也能产生bean, 例如其中一个实现类:```AbstractFactoryBean```,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

getObject()方法源码如下:

```java
@Override
public final T getObject() throws Exception {
//单例从缓存中获取或者暴露引用(用来解决循环引用)
if (isSingleton()) {
return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
}
else {
//创建实例
return createInstance();
}
}

其最终创建实例的方法是createInstance, 该方法由其子类工厂去实现, 具体的子类有如下几种:

例如mybatis的SqlSessionFactoryBean就是实现了该接口。

这里有必要说明一下BeanFactoryFactoryBean的区别:

  • BeanFactory是Spring工厂中的顶层规范,Spring中的容器都是它的具体实现, 例如常见的ApplicationContext
  • FactoryBean是一个能生产或修饰对象生成的工厂Bean, 它本身也是一个bean, 由BeanFactory管理

单例模式

从Spring容器中获取的bean, 默认情况下是单实例的, 其具体实现在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean), 方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) { //使用锁保证多线程同步竞争
//如果此bean正在加载,则不处理
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//记录在缓存中,earlysingletonObjects和singletonFactories互斥(用来解决循环依赖)
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

策略模式

策略模式用于封装系列的算法,这些算法通常被封装在一个 Context 类中,客户端程序可以自由选择其中一种算法,或让 Context 为客户端选择一个最佳的算法 —— 使用策略模式的优势是为了支持算法的自由切换。

Spring中的Resource接口是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。

Resouce接口本身并没有提供任何访问底层资源的逻辑, 针对不同的资源, spring提供了不同的实现类来负责对应的资源访问逻辑。具体如下:

  • UrlResource: 访问网络资源
  • ClassPathResource: 访问类加载路径里资源
  • FileSystemResource: 访问文件系统里资源
  • ServletContextResource: 访问相对于 ServletContext 路径里的资源
  • InputStreamResource: 访问输入流资源
  • ByteArrayResource: 访问字节数组资源

具体spring该使用哪个实现类, 其中就用到了策略模式的思想。

Spring提供了如下两个接口:

  • ResourceLoader: 获取一个Resouce实例
  • ResourceLoaderAware: 获取一个ResourceLoader的引用

当 Spring 应用需要进行资源访问时,实际上并不需要直接使用 Resource 实现类,而是调用 ApplicationContext 实例的 getResource () 方法来获得资源,ApplicationContext 将会负责选择 Resource 的实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来,这就体现了策略模式的优势。

下图是ApplicationContext接口, 可以看到它继承了ResourceLoader接口, 同时也就拥有了该接口的所有方法。

此处 Spring 框架的 ApplicationContext 不仅是 Spring 容器,而且它还是资源访问策略的 “决策者”,也就是策略模式中 Context 对象,它将为客户端代码 “智能” 地选择策略实现。

当 ApplicationContext 实例获取 Resource 实例时,系统将默认采用与 ApplicationContext 相同的资源访问策略。