1. 工厂设计模式
Spring使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 bean 对象。
两者对比:
- BeanFactory :延迟注入(使用到某个 bean 的时候才会注入),占用更少的内存,程序启动速度更快。
- ApplicationContext:容器启动的时候,不管你用没用到,一次性创建所有bean 。 ApplicationContext 扩展了 BeanFactory , 除了有BeanFactory最基本的依赖注入支持,还有更多功能。
ApplicationContext的三个实现类:
- ClassPathXmlApplication:把上下文文件当成类路径资源。
- FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息。
- XmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息。
2. 单例设计模式
在系统中,有一些对象其实只需要一个:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造多个实例就可能导致一些问题:程序的行为异常、资源使用过量、或者不一致性的结果。
使用单例模式的好处:
- 对于频繁使用的对象,可省略创建对象所花的时间,这对于重量级对象,是非常可观的系统开销;
- new 操作的次数减少,对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。
Spring 中 bean 默认作用域 singleton(单例)。 除了 singleton 作用域,Spring 中 bean 作用域:
- 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 实现单例的方式:
- xml :
- 注解:@Scope(value = “singleton”)
Spring 通过 ConcurrentHashMap 实现单例注册表的特殊方式实现单例模式。
3. 代理设计模式
代理模式在 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 快很多。
4. 模板方法
模板方法模式是一种行为设计模式,它定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的实现方式。
- 应用:Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类。
- 一般情况下,我们使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。
5. 观察者模式
观察者模式是一种对象行为型模式。它表示一种对象与对象之间具有依赖关系,当一个对象发生改变的时候,这个对象所依赖的对象也会做出反应。
- 观察者模式经典应用:Spring 事件驱动模型。Spring 事件驱动模型在很多场景都可以解耦代码。比如每次添加商品时都需要重新更新商品索引,这时就可以利用观察者模式解决这个问题。
Spring 事件驱动模型中的三种角色
- 事件角色
ApplicationEvent (org.springframework.context包下)充当事件的角色,是一个抽象类,它继承java.util.EventObject并实现了 java.io.Serializable接口。
Spring 中默认存在以下事件,他们都是对 ApplicationContextEvent 的实现:
ContextStartedEvent:ApplicationContext 启动后触发的事件;
ContextStoppedEvent:ApplicationContext 停止后触发的事件;
ContextRefreshedEvent:ApplicationContext 初始化或刷新完成后触发的事件;
ContextClosedEvent:ApplicationContext 关闭后触发的事件。
- 事件监听者角色
ApplicationListener 充当了事件监听者角色,是一个接口,里面只定义了一个 onApplicationEvent()方法来处理ApplicationEvent。
ApplicationListener接口类源码如下,可以看出接口中的事件只要实现了 ApplicationEvent就可以了。
在 Spring中我们只要实现 ApplicationListener 接口的 onApplicationEvent() 方法即可完成监听事件。
package org.springframework.context;
import java.util.EventListener;
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);
}
- 事件发布者角色
ApplicationEventPublisher 充当了事件的发布者,它也是一个接口。
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
this.publishEvent((Object)event);
}
void publishEvent(Object var1);
}
Spring 的事件流程总结
定义一个事件:实现一个继承自 ApplicationEvent的类,并且写相应的构造函数;
定义一个事件监听者:实现 ApplicationListener 接口,重写 onApplicationEvent() 方法;
使用事件发布者发布消息:通过 ApplicationEventPublisher 的 publishEvent() 方法发布消息。
Example:
// 定义一个事件,继承自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));
}
}
当调用 DemoPublisher 的 publish() 方法的时候,比如 demoPublisher.publish(“你好”) ,控制台就会打印出:接收到的信息是:你好 。
6. 适配器模式
适配器模式(Adapter Pattern) 将一个接口转换成客户希望的另一个接口。
- 适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。
spring AOP中的适配器模式
我们知道 Spring AOP 的实现是基于代理模式,而 Spring AOP 的增强或通知(Advice)用到了适配器模式,与之相关的接口是AdvisorAdapter 。
- Advice 常用的类型有:BeforeAdvice、AfterAdvice、AfterReturningAdvice等。
- 每个类型Advice都有对应的拦截器:MethodBeforeAdviceInterceptor、AfterReturningAdviceAdapter、AfterReturningAdviceInterceptor。
- Spring预定义的通知 要通过对应的适配器,适配成 MethodInterceptor接口(方法拦截器)类型的对象 (如:MethodBeforeAdviceInterceptor 负责适配 MethodBeforeAdvice)。
spring MVC中的适配器模式
- Spring MVC中,DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。
- 解析到对应的 Handler后,开始由HandlerAdapter 适配器处理。HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类。
为什么要在 Spring MVC 中使用适配器模式?
- Spring MVC 中的 Controller 种类众多,不同类型的 Controller 通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet 直接获取对应类型的 Controller,那样每增加一个类型的Controller就需要使用增加一个if else判断instance of,这违反了设计模式的开闭原则 —— 对扩展开放,对修改关闭。
if(mappedHandler.getHandler() instanceof MultiActionController){
((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
...
}else if(...){
...
}
7. 装饰者模式
装饰者模式可以动态地给对象添加一些额外的属性或行为。
- 相比于使用继承,装饰者模式更灵活。简单点说就是当我们需要修改原有的功能,但我们又不愿直接去修改原有的代码时,设计一个Decorator套在原有代码外面。
其实在 JDK 中就有很多地方用到了装饰者模式,比如 InputStream家族:InputStream 类下有 FileInputStream (读取文件)、BufferedInputStream (增加缓存,使读取文件速度大大提升)等子类都在不修改InputStream 代码的情况下扩展了它的功能。
![image.png](https://cdn.nlark.com/yuque/0/2021/png/21447592/1633581324024-e9891249-6254-4ca8-93a4-9884f6b8743f.png#clientId=ud953f54c-6fe6-4&from=paste&height=277&id=uf6c731a7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=357&originWidth=861&originalType=binary&ratio=1&size=28811&status=done&style=none&taskId=ua33c4202-2f52-4388-b5c7-3c2bc036430&width=668.5)
Spring 中配置 DataSource 时,DataSource 可能是不同的数据库和数据源。我们能否根据客户的需求在少修改原有类的代码下动态切换不同的数据源?
- 这时就要用到装饰者模式(这一点我自己还没太理解具体原理)。Spring 中用到的包装器模式在类名上含有 Wrapper或者 Decorator。这些类基本上都是动态地给一个对象添加一些额外的职责。
总结
- 工厂设计模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
- 代理设计模式 : Spring AOP 功能的实现。
- 单例设计模式 : Spring 中的 Bean 默认都是单例的。
- 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
- 装饰者(包装器)设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
- 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
- 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。