共计 8781 个字符,预计需要花费 22 分钟才能阅读完成。
1. 简略工厂 (非 23 种设计模式中的一种)
实现形式:
BeanFactory。Spring 中的 BeanFactory
就是简略工厂模式的体现,依据传入一个惟一的标识来取得 Bean 对象,但是否是在传入参数后创立还是传入参数前创立这个要依据具体情况来定。
本质:
由一个工厂类依据传入的参数,动静决定应该创立哪一个产品类。
实现原理:
bean 容器的启动阶段:
- 读取 bean 的 xml 配置文件, 将 bean 元素别离转换成一个
BeanDefinition
对象。 - 而后通过
BeanDefinitionRegistry
将这些 bean 注册到 beanFactory 中,保留在它的一个ConcurrentHashMap
中。 - 将
BeanDefinition
注册到了beanFactory
之后,在这里 Spring 为咱们提供了一个扩大的切口,容许咱们通过实现接口BeanFactoryPostProcessor
在此处来插入咱们定义的代码。
典型的例子就是:PropertyPlaceholderConfigurer
,咱们个别在配置数据库的dataSource
时应用到的占位符的值,就是它注入进去的。
容器中 bean 的实例化阶段:
实例化阶段次要是通过反射或者 CGLIB 对 bean 进行实例化,在这个阶段 Spring 又给咱们裸露了很多的扩大点:
- 各种的 Aware 接口 ,比方
BeanFactoryAware
,对于实现了这些 Aware 接口的 bean,在实例化 bean 时 Spring 会帮咱们注入对应的 BeanFactory 的实例。 - BeanPostProcessor 接口 ,实现了
BeanPostProcessor
接口的 bean,在实例化 bean 时 Spring 会帮咱们调用接口中的办法。 - InitializingBean 接口 ,实现了
InitializingBean
接口的 bean,在实例化 bean 时 Spring 会帮咱们调用接口中的办法。 - DisposableBean 接口 ,实现了
BeanPostProcessor
接口的 bean,在该 bean 死亡时 Spring 会帮咱们调用接口中的办法。
设计意义:
松耦合。能够将原来硬编码的依赖,通过 Spring 这个 beanFactory
这个工厂来注入依赖,也就是说原来只有依赖方和被依赖方,当初咱们引入了第三方——spring 这个 beanFactory
,由它来解决 bean 之间的依赖问题,达到了松耦合的成果.
bean 的额定解决。通过 Spring 接口的裸露,在实例化 bean 的阶段咱们能够进行一些额定的解决,这些额定的解决只须要让 bean 实现对应的接口即可,那么 spring 就会在 bean 的生命周期调用咱们实现的接口来解决该 bean。 [十分重要]
2. 工厂办法
实现形式:
FactoryBean 接口。
实现原理:
实现了 FactoryBean
接口的 bean 是一类叫做 factory 的 bean。其特点是,spring 会在应用 getBean()
调用取得该 bean 时,会主动调用该 bean 的 getObject()
办法,所以返回的不是 factory 这个 bean,而是这个 bean.getOjbect()
办法的返回值。
例子:
典型的例子有 spring 与 mybatis 的联合。
代码示例:
图片
阐明:
咱们看下面该 bean,因为实现了 FactoryBean
接口,所以返回的不是 SqlSessionFactoryBean
的实例,而是它的 SqlSessionFactoryBean.getObject()
的返回值。
扩大: 设计模式是什么鬼(工厂办法)
3. 单例模式
Spring 依赖注入 Bean 实例默认是单例的。
Spring 的依赖注入(包含 lazy-init
形式)都是产生在 AbstractBeanFactory
的 getBean 里。getBean 的 doGetBean
办法调用 getSingleton
进行 bean 的创立。
剖析 getSingleton()
办法
public Object getSingleton(String beanName){
// 参数 true 设置标识容许晚期依赖
return getSingleton(beanName,true);
}
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) {
// 当某些办法须要提前初始化的时候则会调用 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 != NULL_OBJECT ? singletonObject : null);
}
getSingleton() 过程图
ps:spring 依赖注入时,应用了 双重判断加锁 的单例模式
图片
总结
单例模式定义:保障一个类仅有一个实例,并提供一个拜访它的全局拜访点。
spring 对单例的实现:spring 中的单例模式实现了后半句话,即提供了全局的拜访点 BeanFactory
。但没有从结构器级别去管制单例,这是因为 spring 治理的是任意的 java 对象。
扩大: 设计模式是什么鬼(单例)
4. 适配器模式
实现形式:
SpringMVC 中的适配器 HandlerAdatper
。
实现原理:
HandlerAdatper
依据 Handler 规定执行不同的 Handler。
实现过程:
DispatcherServlet
依据 HandlerMapping
返回的 handler,向 HandlerAdatper
发动申请,解决 Handler。
HandlerAdapter
依据规定找到对应的 Handler 并让其执行,执行结束后 Handler 会向 HandlerAdapter
返回一个 ModelAndView
,最初由 HandlerAdapter
向 DispatchServelet
返回一个 ModelAndView
。
实现意义:
HandlerAdatper
使得 Handler 的扩大变得容易,只须要减少一个新的 Handler 和一个对应的 HandlerAdapter
即可。
因而 Spring 定义了一个适配接口,使得每一种 Controller
有一种对应的适配器实现类,让适配器代替 controller
执行相应的办法。这样在扩大 Controller
时,只须要减少一个适配器类就实现了 SpringMVC 的扩大了。
扩大: 设计模式是什么鬼(适配器)
5. 装璜器模式
实现形式:
Spring 中用到的包装器模式在类名上有两种体现:一种是类名中含有 Wrapper,另一种是类名中含有 Decorator。
本质:
动静地给一个对象增加一些额定的职责。
就减少性能来说, Decorator
模式相比生成子类更为灵便。
扩大: 设计模式是什么鬼(装璜)
6. 代理模式
实现形式:
AOP 底层,就是动静代理模式的实现。
动静代理:
在内存中构建的,不须要手动编写代理类
动态代理:
须要手工编写代理类,代理类援用被代理对象。
实现原理:
切面在利用运行的时刻被织入。个别状况下,在织入切面时,AOP 容器会为指标对象创立动静的创立一个代理对象。SpringAOP 就是以这种形式织入切面的。
织入:把切面利用到指标对象并创立新的代理对象的过程。
扩大: 设计模式是什么鬼(代理)
7. 观察者模式
实现形式:
spring 的事件驱动模型应用的是 观察者模式,Spring 中 Observer
模式罕用的中央是 listener
的实现。
具体实现:
事件机制的实现须要三个局部:事件源、事件、事件监听器
ApplicationEvent
抽象类 [事件]
继承自 jdk 的 EventObject
, 所有的事件都须要继承 ApplicationEvent
, 并且通过结构器参数 source 失去事件源.
该类的实现类 ApplicationContextEvent
示意 ApplicaitonContext
的容器事件.
代码:
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp;
public ApplicationEvent(Object source){super(source);
this.timestamp = System.currentTimeMillis();}
public final long getTimestamp(){return this.timestamp;}
}
ApplicationListener
接口 [事件监听器]
继承自 jdk 的 EventListener
, 所有的监听器都要实现这个接口。
这个接口只有一个 onApplicationEvent()
办法, 该办法承受一个 ApplicationEvent
或其子类对象作为参数, 在办法体中, 能够通过不同对 Event 类的判断来进行相应的解决。
当事件触发时所有的监听器都会收到音讯。
代码:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);
}
ApplicationContext
接口 [事件源]
ApplicationContext
是 spring 中的全局容器,翻译过去是”利用上下文”。
实现了 ApplicationEventPublisher
接口。
职责:
负责读取 bean 的配置文档, 治理 bean 的加载, 保护 bean 之间的依赖关系, 能够说是负责 bean 的整个生命周期, 再艰深一点就是咱们平时所说的 IOC 容器。
代码:
public interface ApplicationEventPublisher {void publishEvent(ApplicationEvent event);
}
public void publishEvent(ApplicationEvent event){Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {logger.trace("Publishing event in" + getDisplayName() + ":" + event);
}
getApplicationEventMulticaster().multicastEvent(event);
if (this.parent != null) {this.parent.publishEvent(event);
}
}
ApplicationEventMulticaster
抽象类 [事件源中 publishEvent 办法须要调用其办法 getApplicationEventMulticaster
]
属于事件播送器, 它的作用是把 Applicationcontext
公布的 Event 播送给所有的监听器.
代码:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
private ApplicationEventMulticaster applicationEventMulticaster;
protected void registerListeners(){
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String lisName : listenerBeanNames) {getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
}
扩大: 设计模式是什么鬼(观察者)
8. 策略模式
实现形式:
Spring 框架的资源拜访 Resource 接口。该接口提供了更强的资源拜访能力,Spring 框架自身大量应用了 Resource
接口来拜访底层资源。
Resource 接口介绍
source
接口是具体资源拜访策略的形象,也是所有资源拜访类所实现的接口。
Resource
接口次要提供了如下几个办法:
- getInputStream(): 定位并关上资源,返回资源对应的输出流。每次调用都返回新的输出流。调用者必须负责敞开输出流。
- exists(): 返回 Resource 所指向的资源是否存在。
- isOpen(): 返回资源文件是否关上,如果资源文件不能屡次读取,每次读取完结应该显式敞开,以避免资源透露。
- getDescription(): 返回资源的形容信息,通常用于资源解决出错时输入该信息,通常是全限定文件名或理论 URL。
- getFile: 返回资源对应的 File 对象。
- getURL: 返回资源对应的 URL 对象。
最初两个办法通常毋庸应用,仅在通过简略形式拜访无奈实现时,Resource 提供传统的资源拜访的性能。
Resource
接口自身没有提供拜访任何底层资源的实现逻辑,针对不同的底层资源,Spring 将会提供不同的 Resource 实现类,不同的实现类负责不同的资源拜访逻辑。
Spring 为 Resource
接口提供了如下实现类:
- UrlResource: 拜访网络资源的实现类。
- ClassPathResource: 拜访类加载门路里资源的实现类。
- FileSystemResource: 拜访文件系统里资源的实现类。
- ServletContextResource: 拜访绝对于 ServletContext 门路里的资源的实现类.
- InputStreamResource: 拜访输出流资源的实现类。
- ByteArrayResource: 拜访字节数组资源的实现类。
这些 Resource
实现类,针对不同的的底层资源,提供了相应的资源拜访逻辑,并提供便捷的包装,以利于客户端程序的资源拜访。
扩大: 设计模式是什么鬼(策略)
9. 模版办法模式
经典模板办法定义:
父类定义了骨架(调用哪些办法及程序),某些特定办法由子类实现。
最大的益处:代码复用,缩小反复代码。除了子类要实现的特定办法,其余办法及办法调用程序都在父类中事后写好了。
所以父类模板办法中有两类办法:
独特的办法:所有子类都会用到的代码
不同的办法:子类要笼罩的办法,分为两种:
- 形象办法:父类中的是形象办法,子类必须笼罩
- 钩子办法:父类中是一个空办法,子类继承了默认也是空的
注:为什么叫钩子,子类能够通过这个钩子(办法),管制父类,因为这个钩子理论是父类的办法(空办法)!
Spring 模板办法模式本质:
是模板办法模式和回调模式的联合,是 Template Method
不须要继承的另一种实现形式。Spring 简直所有的外接扩大都采纳这种模式。
举荐: 设计模式是什么鬼(模板办法)
具体实现:
JDBC 的形象和对 Hibernate 的集成,都采纳了一种理念或者解决形式,那就是模板办法模式与相应的 Callback 接口相结合。
采纳模板办法模式是为了以一种对立而集中的形式来解决资源的获取和开释,以 JdbcTempalte 为例:
public abstract class JdbcTemplate {
public final Object execute(String sql){
Connection cnotallow=null;
Statement stmt=null;
try{
cnotallow=getConnection();
stmt=con.createStatement();
Object retValue=executeWithStatement(stmt,sql);
return retValue;
}catch(SQLException e){...}finally{
closeStatement(stmt);
releaseConnection(con);
}
}
protected abstract Object executeWithStatement(Statement stmt, String sql);
}
引入回调起因:
JdbcTemplate
是抽象类,不可能独立应用,咱们每次进行数据拜访的时候都要给出一个相应的子类实现, 这样必定不不便,所以就引入了回调。
回调代码
public interface StatementCallback{Object doWithStatement(Statement stmt);}
利用回调办法重写 JdbcTemplate
办法
public class JdbcTemplate {
public final Object execute(StatementCallback callback){
Connection cnotallow=null;
Statement stmt=null;
try{
cnotallow=getConnection();
stmt=con.createStatement();
Object retValue=callback.doWithStatement(stmt);
return retValue;
}catch(SQLException e){...}finally{
closeStatement(stmt);
releaseConnection(con);
}
}
...// 其它办法定义
}
Jdbc 应用办法如下:
JdbcTemplate jdbcTemplate=...;
final String sql=...;
StatementCallback callback=new StatementCallback(){public Object=doWithStatement(Statement stmt){return ...;}
}
jdbcTemplate.execute(callback);
为什么 JdbcTemplate 没有应用继承?
因为这个类的办法太多,然而咱们还是想用到 JdbcTemplate
已有的稳固的、专用的数据库连贯,那么咱们怎么办呢?
咱们能够把变动的货色抽出来作为一个参数传入 JdbcTemplate
的办法中。然而变动的货色是一段代码,而且这段代码会用到 JdbcTemplate
中的变量。怎么办?
那咱们就用回调对象吧。在这个回调对象中定义一个操纵 JdbcTemplate
中变量的办法,咱们去实现这个办法,就把变动的货色集中到这里了。而后咱们再传入这个回调对象到 JdbcTemplate
,从而实现了调用。