Spring中用到的设计模式
控制反转(IoC)和依赖注入(DI)
IoC(Inversion of Control,控制翻转) 是 Spring 中一个非常重要的概念,它不是什么技术,而是一种解耦的设计思想。
它的主要目的是借助于“第三方”(Spring 中的 IOC 容器) 实现具有依赖关系的对象之间的解耦,从而降低代码之间的耦合度。
IOC 是一个原则,而不是一个模式。
Spring IOC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
IOC 容器负责创建对象,将对象连接在一起,配置这些对象,并从创建中处理这些对象的整个生命周期,直到它们被完全销毁。
在实际项目中一个 Service 类如果有几百甚至上千个类作为它的底层,我们需要实例化这个 Service,要搞清这个 Service 所有底层类的构造函数。
如果利用 IOC 的话,你只需要配置好,然后在需要的地方引用,这大大增加了项目的可维护性且降低了开发难度。
控制翻转的理解。举个例子:对象 a 依赖了对象b,当对象 a 需要使用 对象 b的时候必须自己去创建。
当系统引入了 IOC 容器后, 对象 a 和对象 b 之间失去了直接的联系。
这时候,当对象 a 需要使用对象 b 的时候,我们可以指定 IOC 容器去创建一个对象b注入到对象 a 中。
对象 a 获得依赖对象 b 的过程,由主动行为变为了被动行为,控制权反转。
DI(Dependecy Inject,依赖注入)是实现控制反转的一种设计模式,是指将实例变量传入到一个对象中去。
工厂模式
Spring 使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 bean 对象。
两者对比
BeanFactory:延迟注入(使用到某个 bean 的时候才会注入),相比于 ApplicationContext 占用更少的内存,程序启动速度更快。
ApplicationContext:容器启动的时候,一次性创建所有 bean。BeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory,所以一般开发人员使用ApplicationContext会更多。
ApplicationContext的三个实现类
ClassPathXmlApplication:上下文文件作为类路径资源。
FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息。
XmlWebApplicationContext:从 Web 系统中的 XML 文件载入上下文定义信息。
示例
1 |
|
单例模式
在系统中,有些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象等对象。
事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量。
使用单例模式的好处
对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销。
由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。
Spring 中 bean 的几种作用域
Spring 中 bean 的默认作用域就是 singleton(单例)的。
prototype: 每次请求都会创建一个新的 bean 实例。
request: 每一次 HTTP 请求都会产生一个新的 bean,该bean仅在当前 HTTP request 内有效。
session: 每一次 HTTP 请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
global-session: 全局 session 作用域,仅仅在基于 portlet 的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(如:HTML)的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。
Spring 实现单例的方式
1 |
|
1 |
|
案例
Spring 通过 ConcurrentHashMap 实现单例注册表的特殊方式,实现单例模式。
代理模式
代理模式在 AOP 中的应用
AOP(Aspect-Oriented Programming,面向切面编程)。
将与业务无关,却为业务模块所共同调用的逻辑(如:事务处理、日志管理、权限控制等)封装起来,减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP 是基于动态代理的。如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象;
而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理,这时候 Spring AOP 会使用Cglib,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。
使用 AOP 后可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。需要增加新功能时也方便,提高了系统扩展性。
日志功能、事务管理等等场景都用到了 AOP。
Spring AOP 和 AspectJ AOP 的区别
Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。
Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。
Spring AOP 已经集成了 AspectJ ,AspectJ 是 Java 生态系统中最完整的 AOP 框架。
AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单,如果切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ,它比Spring AOP 快很多。
模板方法模式
模板方法模式是一种行为设计模式,它定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
模板方法使得子类可以不改变一个算法的结构,即可重定义该算法的某些特定步骤的实现方式。
1 |
|
Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
一般情况下,都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。
参考资料
https://snailclimb.gitee.io/javaguide/#/docs/system-design/framework/spring/Spring-Design-Patterns