Spring中的设计模式
控制反转(IoC)和依赖注入(DI)
IoC 是一个原则,而不是一个模式,以下模式(但不限于)实现了 IoC 原则。
**Spring IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。**IoC 容器负责创建对象,将对象连接在一起,配置这些对象,并从创建中处理这些对象的整个生命周期,直到它们被完全销毁。
控制反转怎么理解呢? 举个例子:"对象 a 依赖了对象 b,当对象 a 需要使用 对象 b 的时候必须自己去创建。但是当系统引入了 IOC 容器后, 对象 a 和对象 b 之间就失去了直接的联系。这个时候,当对象 a 需要使用 对象 b 的时候, 我们可以指定 IOC 容器去创建一个对象 b 注入到对象 a 中"。 对象 a 获得依赖对象 b 的过程,由主动行为变为了被动行为,控制权反转,这就是控制反转名字的由来。
DI(Dependency Inject,依赖注入)是实现控制反转的一种设计模式,依赖注入就是将实例变量传入到一个对象中去
关于 Spring IOC 的理解,推荐看这一下知乎的一个回答:https://www.zhihu.com/question/23277575/answer/169698662 ,非常不错。
工厂设计模式
Spring 使用工厂模式可以通过 BeanFactory
或 ApplicationContext
创建 bean 对象
两者的区别:
BeanFactory
:延迟注入(使用到某个 bean 的时候才会注入),所以相比ApplicationContext
占用内存更少,启动速度更快ApplicationContext
:容器启动的时候,不管你用没用到,一次性创建所有 bean
ApplicationContext
的三个实现类:
ClassPathXmlApplication
:把上下文文件当成类路径资源。它适用于当配置文件位于类路径中(通常是resources
目录下)时的情况。FileSystemXmlApplication
:从文件系统中的 XML 文件载入上下文定义信息。它适用于当配置文件不在类路径中,而是位于文件系统的某个路径时的情况。XmlWebApplicationContext
:从 Web 系统中的 XML 文件载入上下文定义信息。从 Web 应用的web.xml
文件或ServletContext
中加载 Spring 配置文件,配置文件放在WEB-INF
目录下
单例设计模式
在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生
使用单例模式的好处:
- 对于经常使用的对象,省下了创建对象的时间,节省了系统开销
- new的对象操作次数减少,系统的内存使用频率也会降低,减少GC的压力
Spring 中 bean 的默认作用域就是 singleton(单例)的。
Spring 通过 ConcurrentHashMap
实现单例注册表的特殊方式实现单例模式
大概思路是:判断缓存是否存在实例,实例对象不存在就注册到单例注册表中
代理设计模式
代理模式主要是在AOP中的应用
Spring AOP 就是基于动态代理的 ,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy 去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理
模版方法
模板方法模式是一种行为设计模式,它定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的实现方式。
public abstract class Template {
//这是我们的模板方法
public final void TemplateMethod(){
PrimitiveOperation1();
PrimitiveOperation2();
PrimitiveOperation3();
}
protected void PrimitiveOperation1(){
//当前类实现
}
//被子类实现的方法
protected abstract void PrimitiveOperation2();
protected abstract void PrimitiveOperation3();
}
public class TemplateImpl extends Template {
@Override
public void PrimitiveOperation2() {
//当前类实现
}
@Override
public void PrimitiveOperation3() {
//当前类实现
}
}
Spring 中 JdbcTemplate
、HibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。一般情况下,我们都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用 Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性
观察者模式
观察者模式是一种对象行为型模式。它表示的是一种对象与对象之间具有依赖关系,当一个对象发生改变的时候,依赖这个对象的所有对象也会做出反应。Spring 事件驱动模型就是观察者模式很经典的一个应用
Spring事件驱动模型中的三种角色
事件角色(ApplicationEvent)
Spring 中默认存在以下事件,他们都是对 ApplicationContextEvent
的实现(继承自ApplicationContextEvent
):
ContextStartedEvent
:ApplicationContext
启动后触发的事件;ContextStoppedEvent
:ApplicationContext
停止后触发的事件;ContextRefreshedEvent
:ApplicationContext
初始化或刷新完成后触发的事件;ContextClosedEvent
:ApplicationContext
关闭后触发的事件。
事件监听者(ApplicationListener)
ApplicationListener充当了事件监听者角色,它是一个接口,里面只定义了一个
onApplicationEvent()方法来处理
ApplicationEvent
ApplicationListener
接口类源码如下,可以看出接口定义看出接口中的事件只要实现了 ApplicationEvent
就可以了。所以,在 Spring 中我们只要实现 ApplicationListener
接口的 onApplicationEvent()
方法即可完成监听事件
时间发布者(ApplicationEventPublisher)
ApplicationEventPublisher
充当了事件的发布者,它也是一个接口。
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
this.publishEvent((Object)event);
}
void publishEvent(Object var1);
}
ApplicationEventPublisher
接口的publishEvent()
这个方法在AbstractApplicationContext
类中被实现,阅读这个方法的实现,你会发现实际上事件真正是通过ApplicationEventMulticaster
来广播出去的
Spring事件流程总结
-
定义一个事件: 实现一个继承自
ApplicationEvent
,并且写相应的构造函数; -
定义一个事件监听者:实现
ApplicationListener
接口,重写onApplicationEvent()
方法; -
使用事件发布者发布消息: 可以通过
ApplicationEventPublisher
的publishEvent()
方法发布消息。Example:
java// 定义一个事件,继承自ApplicationEvent并且写相应的构造函数 public class DemoEvent extends ApplicationEvent{ private static final long serialVersionUID = 1L; private String message; public DemoEvent(Object source,String message){ super(source); this.message = message; } public String getMessage() { return message; } // 定义一个事件监听者,实现ApplicationListener接口,重写 onApplicationEvent() 方法; @Component public class DemoListener implements ApplicationListener<DemoEvent>{ //使用onApplicationEvent接收消息 @Override public void onApplicationEvent(DemoEvent event) { String msg = event.getMessage(); System.out.println("接收到的信息是:"+msg); } } // 发布事件,可以通过ApplicationEventPublisher 的 publishEvent() 方法发布消息。 @Component public class DemoPublisher { @Autowired ApplicationContext applicationContext; public void publish(String message){ //发布事件 applicationContext.publishEvent(new DemoEvent(this, message)); } }
适配器模式
适配器模式(Adapter Pattern) 将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作
SpringAop的适配器
主要是通过MethodBeforeAdviceAdapter` 通过调用 `getInterceptor` 方法,将 `MethodBeforeAdvice` 适配成 `MethodBeforeAdviceInterceptor
- advice的类型有:BeforeAdvice,AfterReturningAdvice,ThrowsAdvice等
- 每个类型的通知都有对应的拦截器
advice | 拦截器 |
---|---|
BeforeAdvice | MethodBeforeAdviceInterceptor |
AfterAdvice | AfterReturningAdviceInterceptor |
AfterAdvice | ThrowsAdviceInterceptor |
3.Spring容器需要将每个具体的advice封装成对应的拦截器,返回给容器,这里对advice转换就需要用到适配器模式。
Example:
实现封装成对应的拦截器(以前置通知为例)
用于实现方法执行前的通知(Before Advice)
java
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args, Object target) throws Throwable;
}
转换对应拦截器的逻辑
java
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
// 通知类型匹配对应的拦截器
return new MethodBeforeAdviceInterceptor(advice);
}
}
MethodBeforeAdviceAdapter实现了AdvisorAdapter
java
public interface AdvisorAdapter {
// 判断通知类型是否匹配
boolean supportsAdvice(Advice advice);
// 获取对应的拦截器
MethodInterceptor getInterceptor(Advisor advisor);
}
SpringMVC的适配器
在 Spring MVC 中,DispatcherServlet
根据请求信息调用 HandlerMapping
,解析请求对应的 Handler
。解析到对应的 Handler
(也就是我们平常说的 Controller
控制器)后,开始由HandlerAdapter
适配器处理。HandlerAdapter
作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller
作为需要适配的类。
装饰者模式
装饰者模式可以动态地给对象添加一些额外的属性或行为。相比于使用继承,装饰者模式更加灵活。简单点儿说就是当我们需要修改原有的功能,但我们又不愿直接去修改原有的代码时,设计一个 Decorator 套在原有代码外面。其实在 JDK 中就有很多地方用到了装饰者模式,比如 InputStream
家族,InputStream
类下有 FileInputStream
(读取文件)、BufferedInputStream
(增加缓存,使读取文件速度大大提升)等子类都在不修改InputStream
代码的情况下扩展了它的功能
Spring 中用到的包装器模式在类名上含有 Wrapper
或者 Decorator
总结
工厂设计模式 : Spring 使用工厂模式通过 BeanFactory
、ApplicationContext
创建 bean 对象。
代理设计模式 : Spring AOP 功能的实现。
单例设计模式 : Spring 中的 Bean 默认都是单例的。
模板方法模式 : Spring 中 jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller
。