Spring中的设计模式

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 使用工厂模式可以通过 BeanFactoryApplicationContext 创建 bean 对象

两者的区别:

  • BeanFactory:延迟注入(使用到某个 bean 的时候才会注入),所以相比ApplicationContext占用内存更少,启动速度更快
  • ApplicationContext:容器启动的时候,不管你用没用到,一次性创建所有 bean

ApplicationContext 的三个实现类:

  1. ClassPathXmlApplication:把上下文文件当成类路径资源。它适用于当配置文件位于类路径中(通常是 resources 目录下)时的情况。
  2. FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息。它适用于当配置文件不在类路径中,而是位于文件系统的某个路径时的情况。
  3. 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 中 JdbcTemplateHibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。一般情况下,我们都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用 Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性

观察者模式

观察者模式是一种对象行为型模式。它表示的是一种对象与对象之间具有依赖关系,当一个对象发生改变的时候,依赖这个对象的所有对象也会做出反应。Spring 事件驱动模型就是观察者模式很经典的一个应用

Spring事件驱动模型中的三种角色

事件角色(ApplicationEvent)

Spring 中默认存在以下事件,他们都是对 ApplicationContextEvent 的实现(继承自ApplicationContextEvent):

  • ContextStartedEventApplicationContext 启动后触发的事件;
  • ContextStoppedEventApplicationContext 停止后触发的事件;
  • ContextRefreshedEventApplicationContext 初始化或刷新完成后触发的事件;
  • ContextClosedEventApplicationContext 关闭后触发的事件。

事件监听者(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事件流程总结

  1. 定义一个事件: 实现一个继承自 ApplicationEvent,并且写相应的构造函数;

  2. 定义一个事件监听者:实现 ApplicationListener 接口,重写 onApplicationEvent() 方法;

  3. 使用事件发布者发布消息: 可以通过 ApplicationEventPublisherpublishEvent() 方法发布消息。

    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

  1. advice的类型有:BeforeAdvice,AfterReturningAdvice,ThrowsAdvice等
  2. 每个类型的通知都有对应的拦截器
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 使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象。

代理设计模式 : Spring AOP 功能的实现。

单例设计模式 : Spring 中的 Bean 默认都是单例的。

模板方法模式 : Spring 中 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。

包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。

适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller

相关推荐
会编程的吕洞宾10 分钟前
Spring Boot多环境配置实战 配置文件加载顺序与切换不再翻车
java·后端
额鹅恶饿呃12 分钟前
随着CentOS官方停服的时间越来越久,大量仍在使用CentOS7的企业和运维从业者
java·python·算法·c#·ruby
秋名RG17 分钟前
Java 异常处理全攻略:从入门到实战(JDK 21 版)
java·开发语言
SQL-First布道者26 分钟前
我为什么把 MyBatis 从项目中删了?
java·spring·tomcat·mybatis·mybatis plus·spring jdbc
公爵爱学习30 分钟前
无人机多点导航笔记
java·前端·笔记
萧瑟余晖1 小时前
Java深入解析篇五十五之Unsafe 机制详解
java
kakawzw1 小时前
mybatis源码笔记1——JDBC和整体架构
java·mybatis
sunshine22 girl1 小时前
Java学习一 环境配置1 安装JDK,配置环境变量
java·开发语言·学习
秋饼1 小时前
Spring AI Session API 深度实战:从 ChatMemory 平滑迁移到事件溯源的企业级短期记忆
java·ai·技术分享·后端开发
QCodingDev1 小时前
Spring AI 2.0企业级RAG实战:引用校验、无依据拒答与知识治理怎么做?
java·人工智能·spring·ai