Spring作为Java世界当之无愧的No.1开源框架,其架构设计非常优秀,使用很多设计模式。
注:
又叫做静态工厂方法模式,不属于GoF23种设计模式之一,是编程中的一种惯用法。
实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。工厂类通常只有一个静态方法,根据不同的参数返回不同的产品实例。
优点:简单易用,适合产品数量少且变化不频繁的场景。
缺点:工厂类的职责过重,增加类的复杂度,一旦产品种类增多或产品变更,工厂类的代码将难以维护。
适用场景:当产品数量较少,且客户端只需知道工厂类即可创建产品时。
Spring中的BeanFactory就是一个例子。根据传入的一个唯一标识来获得Bean对象,但是否是在传入参数后创建还是传入参数前创建,则根据具体情况来定。
Bean容器的启动阶段:
PropertyPlaceholderConfigurer(Spring 5.2后废弃),在配置数据库的dataSource时使用到的占位符的值,就是它注入进去的。
容器中Bean的实例化阶段:
实例化阶段主要是通过反射或CGLIB对Bean进行实例化,此阶段Spring又暴露很多的扩展点:
FactoryMethod,工厂方法模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实例化延迟到其子类。
Spring里典型案例就是FactoryBean。实现FactoryBean接口的Bean是一类叫做factory的Bean,Spring会在使用getBean()调用获得该Bean时,会自动调用该Bean的getObject()方法,所以返回的不是factory这个Bean,而是bean.getOjbect()
方法的返回值。
AbstractFactory,提供一个接口,用于创建一系列相关或依赖的对象,而无需指定它们的具体类。
优点:
缺点:产品族扩展非常方便,但扩展单个产品的种类较为复杂,需要修改抽象工厂的接口。
应用场景:
区别
参考单例模式(Java)。
Spring依赖注入Bean实例默认是单例的。Spring的依赖注入,包括lazy-init
方式,都是发生在AbstractBeanFactory.getBean
方法里,getBean
方法调用doGetBean
方法,doGetBean
方法调用DefaultSingletonBeanRegistry.getSingleton
进行Bean的创建:
public Object getSingleton(String beanName) {
// 参数true设置标识允许早期依赖
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock:检查缓存中是否存在实例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 如果为空,则锁定全局变量并进行处理
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 如果此bean正在加载,则不处理
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 当某些方法需要提前初始化时,则会调用addSingleFactory方法将对应的ObjectFactory初始化策略存储在singletonFactories
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 调用预先设定的getObject方法
singletonObject = singletonFactory.getObject();
// 记录在缓存中,earlysingletonObjects和singletonFactories互斥
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
全静态方法实现的工具类和单例模式的工具类的区别
Prototype
Builder,有好几个不同的翻译(叫法),构建器,建造者,建设者,构造方法,构造函数。是创建对象模式三剑客(工厂方法模式、构造器模式、原型模式)其一,用于简化复杂对象的构造。
构造器隐藏对象构造的复杂性,内部静态类支持链式方法的调用。参考Builder模式。
Spring里提供的BeanDefinitionBuilder就是一个例子,提供几个方法,为AbstractBeanDefinition抽象类的相关实现设置值,如作用域,工厂方法,属性等。
Proxy,为其他对象提供一种代理以控制对这个对象的访问。
Proxy和Decorator
从结构上来看比较类似,但Proxy是控制,强调的是对功能的限制,而Decorator是增加职责。
Spring核心功能AoP基于代理,如JdkDynamicAopProxy,Cglib2AopProxy。
参考代理模式。
Adapter。参考设计模式之Decorator装饰者、Facade外观、Adapter适配器(Java)。
Decorator
Facade
Bridge
Flyweight
Composite,
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
优点:封装性好、复用性好、屏蔽细节、便于维护。
缺点:继承只能是单个的,重构时会遇到困难
Spring源码中使用最多的设计模式应该就是模板方法,不过和GoF不是严格一样。参考Spring系列之模版方法汇总。
Command,命令模式允许将请求封装在一个对象内并附加一个回调动作。请求被封装在命令对象之下,而请求的结果被发送到接收者。命令本身不是由调用者执行。
Mediator
Observer,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
Spring的事件机制(也叫事件驱动模型)使用的是观察者模式。ApplicationListener就是一个典型的案例。
事件机制包括三个部分(角色):
java.util.EventListener
。实现事件监听者接口中一些或全部方法的类就是事件监听者。具体到Spring里,ApplicationEvent抽象类是事件,继承自JDK的EventObject,所有事件都需要继承ApplicationEvent,且通过构造器参数source得到事件源。实现类ApplicationContextEvent表示ApplicaitonContext的容器事件。
ApplicationListener接口就是事件监听器,继承自JDK的EventListener,所有的监听器都要实现这个接口,接口的onApplicationEvent
方法接受一个ApplicationEvent或其子类对象作为参数,在方法体中可以通过对不同Event类的判断来进行相应的处理,当事件触发时所有监听器都会收到消息。
ApplicationContext接口是事件源,继承ApplicationEventPublisher接口,是Spring中的全局容器,即应用上下文。
State。
定义:在不同的状态下,对同一行为有不同的响应。状态模式把对象的行为包装在不同的状态中,每一个状态的对象都有一个相同的抽象状态基类,并实现基类对应的方法。这样当一个对象的状态发生改变时,其行为也会随之改变。
使用场景:当一个对象的行为受其对应的状态的影响时。如:手机的飞行模式有开启和关闭两个状态,飞行模式关闭时可发短信打电话,飞行模式开启时,不能发短信打电话。
如在淘宝购物时,在用户未登录状态下只能进行商品的浏览,如果点击购买按钮,则会跳转到注册/登录界面;登陆后才可进行商品购买操作。
Memento
Visitor
Spring提供的BeanDefinitionVisitor,用于解析Bean元数据并将其解析为 String(例如:具有作用域或工厂方法名称的XML属性)或 Object(例如构造函数定义中的参数)。已解析的值在与分析的Bean关联的BeanDefinition实例中进行判断设置。visitBeanDefinition方法源码:
public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
if (beanDefinition.hasPropertyValues()) {
visitPropertyValues(beanDefinition.getPropertyValues());
}
if (beanDefinition.hasConstructorArgumentValues()) {
ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
visitIndexedArgumentValues(cas.getIndexedArgumentValues());
visitGenericArgumentValues(cas.getGenericArgumentValues());
}
}
Iterator
Strategy,定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换,使得算法可独立于客户端而变化。策略模式是指有一定行动内容的相对稳定的策略名称。
优点:
缺点:
Spring提供的InstantiationStrategy接口就使用策略模式:
// 负责创建与根Bean定义对应的实例。由于可以使用各种方法来创建实例,故使用策略模式,包括使用CGLIB动态创建子类以支持方法注入。
public interface InstantiationStrategy {
// 返回给定Bean工厂中满足给定名称的Bean实例
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) throws BeansException;
// 返回给定Bean工厂中满足给定名称的Bean实例,通过给定的构造函数来创建
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, Constructor<?> ctor, Object... args) throws BeansException;
// 返回给定Bean工厂中满足给定名称的Bean实例,通过给定的工厂方法创建
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, @Nullable Object factoryBean, Method factoryMethod, Object... args) throws BeansException;
// 6.0版本后,确定给定Bean定义的实际类(在运行时实例化)
default Class<?> getActualBeanClass(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
return bd.getBeanClass();
}
}
InstantiationStrategy有两个实现类,SimpleInstantiationStrategy和CglibSubclassingInstantiationStrategy。
SimpleInstantiationStrategy里重写的instantiate方法则说明策略模式的具体使用情况:
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
constructorToUse = clazz.getDeclaredConstructor();
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
} catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
} else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
另外,Spring的Resource接口提供强大的资源访问能力,Spring框架本身大量使用Resource接口来访问底层资源。
Resource 接口介绍
source 接口是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。
Resource 接口源码如下:
public interface Resource extends InputStreamSource {
boolean exists();
default boolean isReadable() {
return exists();
}
default boolean isOpen() {
return false;
}
default boolean isFile() {
return false;
}
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
default byte[] getContentAsByteArray() throws IOException {
return FileCopyUtils.copyToByteArray(getInputStream());
}
default String getContentAsString(Charset charset) throws IOException {
return FileCopyUtils.copyToString(new InputStreamReader(getInputStream(), charset));
}
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
@Nullable
String getFilename();
String getDescription();
}
主要提供如下几个方法:
Resource接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring将会提供不同的Resource实现类,不同的实现类负责不同的资源访问逻辑:
Interpreter,提供如何定义语言的文法,以及对语言句子的解释方法,包括表达式和评估器两部分。
SpEL,Spring Expression Language,可理解为一种解释器模式的体现。