Spring 中使用的设计模式
文章目录
- Spring 中使用的设计模式
- 单例模式
- 工厂模式
- BeanFactory 的工厂模式定位
- BeanFactory 抽象工厂模式的实现
- FactoryBean 工厂方法模式实现
- 代理模式
- Spring AOP 代理的源码分析
- 适配器模式
- Spring AOP 中的`AdvisorAdapter`适配器适配Advice
- Spring MVC 中的`HandlerAdapter`适配器适配Controller
- 装饰器模式
- Spring JDBC 的 `DataSource` 装饰
- 模板模式
- Spring JDBC 的 `JdbcTemplate`
- 观察者模式
- Spring 事件机制(`ApplicationEvent`)
- 策略模式
- Spring 资源加载策略(`ResourceLoader`)
Spring 中使用的设计模式
单例模式
单例模式原理参看:设计模式——单例模式
在 Spring 中创建的 bean 默认是单例。Spring 创建 Bean 使用单例模式的实现方式并非传统设计模式中某种单一实现方式,而是结合自身特性和需求,基于三级缓存机制实现了单例 Bean 的创建和管理。
饿汉式
饿汉式单例模式是在类加载时就创建单例实例,线程安全但缺乏延迟加载特性。Spring 并不完全等同于饿汉式,虽然部分单例 Bean 在容器启动时就会被创建,但这并非强制要求。Spring 支持懒加载(lazy-init
),即可以配置某些 Bean 在第一次被请求时才创建,这与饿汉式的立即创建不同。例如:
<bean id="lazyBean" class="com.example.LazyBean" lazy-init="true"/>
懒汉式
懒汉式单例模式是在第一次使用时才创建实例,通常需要考虑线程安全问题。Spring 的单例 Bean 创建机制包含了懒加载的思想,当第一次请求某个单例 Bean 时,Spring 才会创建该 Bean 实例。但 Spring 通过三级缓存机制和同步控制来保证线程安全,而不是像传统懒汉式那样简单地使用同步方法。
双重检查锁(DCL)方式
双重检查锁方式通过减少同步范围来提高性能,同时保证线程安全。Spring 的三级缓存机制在处理单例 Bean 创建时,有类似的思想。例如,在获取单例 Bean 时,会先检查一级缓存 singletonObjects
中是否存在该 Bean 实例,如果不存在且该 Bean 正在创建中,会进行同步操作,再进一步检查二级缓存 earlySingletonObjects
和三级缓存 singletonFactories
,减少了不必要的同步开销。
Spring 中与单例 Bean 创建相关的核心源码片段
// DefaultSingletonBeanRegistry.java
// 获取 Bean
protected Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {// 先从一级缓存中获取 Bean 实例Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}// 标记该 Bean 正在创建中beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {// 通过工厂对象创建 Bean 实例singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}// 标记该 Bean 结束创建afterSingletonCreation(beanName);}if (newSingleton) {// 将创建好的 Bean 实例放入一级缓存addSingleton(beanName, singletonObject);}}return singletonObject;}
}// 三级缓存获取单例 Bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 先从一级缓存中获取 Bean 实例Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 如果一级缓存中没有,且该 Bean 正在创建中synchronized (this.singletonObjects) {// 从二级缓存中获取提前曝光的 Bean 实例singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {// 如果二级缓存中也没有,从三级缓存中获取创建 Bean 的工厂对象ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 通过工厂对象创建提前曝光的 Bean 实例singletonObject = singletonFactory.getObject();// 将提前曝光的 Bean 实例放入二级缓存this.earlySingletonObjects.put(beanName, singletonObject);// 从三级缓存中移除该工厂对象this.singletonFactories.remove(beanName);}}}}return singletonObject;
}protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {// 将 Bean 实例放入一级缓存this.singletonObjects.put(beanName, singletonObject);// 从三级缓存中移除该 Bean 的工厂对象this.singletonFactories.remove(beanName);// 从二级缓存中移除该 Bean 的提前曝光实例this.earlySingletonObjects.remove(beanName);// 标记该 Bean 不再是正在创建的状态this.registeredSingletons.add(beanName);}
}
工厂模式
Spring 框架中的 BeanFactory
主要运用了抽象工厂设计模式来实现创建 Bean 对象。
BeanFactory 的工厂模式定位
抽象工厂模式原理参考:设计模式——抽象工厂模式
1、不是简单工厂模式
原因:
- 简单工厂通常是一个具体类包含静态创建方法(如
Factory.createProduct(type)
) - BeanFactory 是一个复杂的接口体系,不是单一类;
- 创建逻辑分散在各种实现类中,不是集中在一个方法中;
2、包含工厂方法模式元素
体现:
- BeanFactory 定义了
getBean()
系列方法作为"工厂方法"; - 具体实现由子类决定(如
AnnotationConfigApplicationContext
、XmlBeanFactory
、DefaultListableBeanFactory
);
3. 更符合抽象工厂模式特征
主要原因:
- 产品家族:BeanFactory 每个具体实现代表一个产品族,每个产品族能够创建一组相关的产品 Bean(如:Controller、Service、Repository等);
- XML 配置产品族:
XmlBeanFactory
- 注解配置产品族:
AnnotationConfigApplicationContext
- Groovy 配置产品族:
GenericGroovyApplicationContext
- XML 配置产品族:
- 产品等级:每个具体的 Bean 类型代表一个产品等级,同一产品等级在不同产品族中都有对应的实现;
- DAO 层产品等级:
UserDao
、ProductDao
- Service 层产品等级:
UserService
、OrderService
- Controller 层产品等级:
UserController
、PaymentController
- DAO 层产品等级:
产品族与产品等级
-
产品族强调的是"如何创建"(配置方式/工厂类型)
比如:所有通过 XML 配置创建的 beans 属于同一产品族;
-
产品等级强调的是"创建什么"(bean 的类型)
比如:所有
UserService
实现,无论来自 XML 还是注解配置,属于同一产品等级;
对应抽象工厂模式角色
1、AbstractFactory(抽象工厂角色):BeanFactory
接口;
2、ConcreteFactory(具体工厂角色):AnnotationConfigApplicationContext
、XmlBeanFactory
等实现类;
- AbstractProduct(抽象产品角色):
Object
类,在 Java 中所有类都继承自 Object,getBean()
方法返回 Object 类型,这是所有 Bean 的抽象表示; - Product:每个具体的 Bean 类;
Spring 的 BeanFactory
之所以采用抽象工厂模式作为主要实现方式,是因为它需要:
- 更换产品族(如从 XML 配置切换到注解配置)时,不需要修改产品等级(业务类);
- 同一产品等级的产品可以在不同产品族中互换使用;
BeanFactory 抽象工厂模式的实现
抽象工厂模式为创建一系列相关或相互依赖的对象提供了一个接口,而无需指定它们具体的类。在 BeanFactory
里,BeanFactory
接口就相当于抽象工厂,它定义了获取 Bean 对象的抽象方法,不涉及具体的创建细节。不同的具体工厂类(如 XmlBeanFactory
、AnnotationConfigApplicationContext
等)去实现这个接口,负责创建具体的 Bean 对象。
- 客户端通过
BeanFactory
接口(抽象工厂)与系统交互; - 根据配置选择具体的
BeanFactory
实现(具体工厂); - 调用
getBean()
方法时,具体工厂根据配置和依赖关系创建具体产品(业务 bean); - 返回给客户端的是抽象产品类型(Object),但实际是具体的业务对象;
1、抽象工厂的核心 - BeanFactory 接口
public interface BeanFactory {// 核心工厂方法Object getBean(String name) throws BeansException;// 支持泛型的工厂方法<T> T getBean(String name, Class<T> requiredType) throws BeansException;// 支持构造函数参数的工厂方法Object getBean(String name, Object... args) throws BeansException;// 其他产品识别方法boolean containsBean(String name);Class<?> getType(String name) throws NoSuchBeanDefinitionException;// ...
}
2、具体工厂实现 - DefaultListableBeanFactory
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry {// 实现抽象工厂接口@Overridepublic <T> T getBean(Class<T> requiredType) throws BeansException {// 通过类型获取Bean的实现}@Overridepublic Object getBean(String name, Object... args) throws BeansException {// 通过名称和参数获取Bean的实现}// 管理产品(Bean)定义的注册表private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
}
3、获取不同产品等级的具体产品
// 或者使用BeanFactory
BeanFactory factory = new DefaultListableBeanFactory();
// 配置factory...
MyRepository repository = factory.getBean(MyRepository.class);
FactoryBean 工厂方法模式实现
工厂方法模式原理参考:设计模式——工厂方法模式
FactoryBean
是 Spring 对 工厂方法模式(Factory Method Pattern)的经典实现,通过将对象创建的细节封装在 getObject()
方法中,由子类决定具体实例化逻辑。
角色映射:
- 抽象工厂 →
FactoryBean
接口 - 具体工厂 → 用户实现的
FactoryBean
- 工厂方法 →
getObject()
- 具体产品 →
getObject()
返回的对象
抽象工厂 → FactoryBean
接口
Spring 的 FactoryBean
接口定义了工厂方法的契约:
public interface FactoryBean<T> {// 工厂方法:由子类实现具体对象的创建逻辑T getObject() throws Exception;// 返回产品类型Class<?> getObjectType();// 是否单例boolean isSingleton();
}
getObject()
是核心工厂方法,由子类实现。- 符合工厂方法模式中 “将对象创建延迟到子类” 的原则。
具体工厂 → FactoryBean
的实现类
用户自定义的 FactoryBean
实现类就是具体工厂,负责创建目标对象。
public class MyBeanFactoryBean implements FactoryBean<MyBean> {@Overridepublic MyBean getObject() throws Exception {// 工厂方法:创建 MyBean 对象return new MyBean();}@Overridepublic Class<?> getObjectType() {return MyBean.class;}@Overridepublic boolean isSingleton() {return true;}
}
MyBeanFactoryBean
实现了FactoryBean
接口,在getObject()
方法中创建并返回目标对象(MyBean
)。这里getObject()
方法就是工厂方法,它将MyBean
对象的创建逻辑封装在具体的实现类中。
抽象产品 → Object
(泛型 T)
由于 FactoryBean
是泛型接口(FactoryBean<T>
),其产品类型由 T
决定:
- 可以是任何类型(如
UserService
、DataSource
等)。 - 符合工厂方法模式中 “产品由工厂方法返回” 的设定。
具体产品 → getObject()
返回的实际对象
getObject()
返回的对象就是具体产品,例如上述自定义的 MyBean
目标对象。
代理模式
代理模式原理参考:设计模式——代理模式
Spring AOP 功能的实现是通过代理模式中的动态代理实现的。
Spring 支持两种代理机制:
- JDK 动态代理(基于接口)
- 通过
java.lang.reflect.Proxy
生成代理类。 - 要求目标类必须实现接口。
- 通过
- CGLIB 代理(基于子类)
- 通过字节码增强生成目标类的子类。
- 适用于无接口的类。
Spring 默认优先使用 JDK 动态代理,如果目标类没有接口,则退回到 CGLIB。
Spring AOP 代理的源码分析
代理创建入口:DefaultAopProxyFactory
Spring 通过 AopProxyFactory
决定使用 JDK 还是 CGLIB 代理:
public class DefaultAopProxyFactory implements AopProxyFactory {@Overridepublic AopProxy createAopProxy(AdvisedSupport config) {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {// 使用 CGLIB 代理return new ObjenesisCglibAopProxy(config);} else {// 使用 JDK 动态代理return new JdkDynamicAopProxy(config);}}
}
AdvisedSupport
存储了 AOP 的配置信息(如切面、拦截器等)。AopProxy
是代理创建的顶层接口,具体实现由JdkDynamicAopProxy
或CglibAopProxy
完成。
JDK 动态代理:JdkDynamicAopProxy
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler {private final AdvisedSupport advised; // AOP 配置@Overridepublic Object getProxy() {return Proxy.newProxyInstance(getClass().getClassLoader(),this.advised.getProxiedInterfaces(), // 目标接口this // InvocationHandler);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 1. 获取拦截器链(如事务、日志等)List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// 2. 执行拦截器链 + 目标方法return new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain).proceed();}
}
Proxy.newProxyInstance()
创建 JDK 动态代理对象。InvocationHandler.invoke()
拦截方法调用,执行增强逻辑(如事务、日志等)。
CGLIB 代理:CglibAopProxy
class CglibAopProxy implements AopProxy {@Overridepublic Object getProxy() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(config.getTargetClass()); // 目标类enhancer.setCallback(new DynamicAdvisedInterceptor(this.advised)); // 拦截器return enhancer.create(); // 生成代理对象}
}private static class DynamicAdvisedInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {// 1. 获取拦截器链List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// 2. 执行拦截器链 + 目标方法return new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}
}
Enhancer
创建 CGLIB 代理类(继承目标类)。MethodInterceptor.intercept()
拦截方法调用,执行增强逻辑。
适配器模式
适配器模式原理参考:设计模式——适配器模式
Spring AOP 的增强或通知使用到了适配器模式。SpringMVC 中用到了适配器模式适配 Controller。
Spring AOP 中的AdvisorAdapter
适配器适配Advice
Spring AOP 支持不同类型的 Advice(如 MethodBeforeAdvice
、AfterReturningAdvice
),但最终需要统一适配成 AOP 联盟标准的 Interceptor
。
目标接口:MethodInterceptor
(AOP 联盟标准)
MethodInterceptor
接口是 AOP 联盟标准中定义的用于拦截方法调用的接口。其 invoke
方法接收一个 MethodInvocation
对象,通过这个对象可以获取到目标方法的相关信息,并在方法调用前后执行自定义的增强逻辑。
public interface MethodInterceptor extends Interceptor {Object invoke(MethodInvocation invocation) throws Throwable;
}
被适配者(Adaptee)
MethodBeforeAdvice
(方法前置增强)AfterReturningAdvice
(方法后置增强)ThrowsAdvice
(异常增强)
适配器(Adapter)
MethodBeforeAdviceAdapter
(适配MethodBeforeAdvice
):将MethodBeforeAdvice
适配成MethodInterceptor
。它实现了MethodInterceptor
接口,并在invoke
方法中调用MethodBeforeAdvice
的before
方法,从而实现了在目标方法调用前执行MethodBeforeAdvice
的逻辑。AfterReturningAdviceAdapter
(适配AfterReturningAdvice
):将AfterReturningAdvice
适配成MethodInterceptor
。它在invoke
方法中调用AfterReturningAdvice
的afterReturning
方法,实现在目标方法正常返回后执行增强逻辑。ThrowsAdviceAdapter
(适配ThrowsAdvice
):将ThrowsAdvice
适配成MethodInterceptor
。它在invoke
方法中处理目标方法抛出的异常,并调用ThrowsAdvice
中相应的异常处理方法。
使用示例:
-
DefaultAdvisorChainFactory
获取所有 Advisor。 -
使用
AdvisorAdapterRegistry
将 Advice 适配成MethodInterceptor
。 -
在代理对象执行时调用
MethodInterceptor.invoke()
。// DefaultAdvisorChainFactory.getInterceptors() List<MethodInterceptor> interceptors = new ArrayList<>(); for (Advisor advisor : advisors) {if (advisor instanceof PointcutAdvisor) {MethodInterceptor[] interceptors = registry.getInterceptors(advisor);interceptors.addAll(Arrays.asList(interceptors));} }
Spring MVC 中的HandlerAdapter
适配器适配Controller
Spring MVC 支持多种 Controller 类型(如 @Controller
、HttpRequestHandler
、Servlet
),由于这些 Controller
的调用方式和接口各不相同,DispatcherServlet
没办法直接调用它们。这时就需要 HandlerAdapter
来适配不同的 Controller。
目标接口:HandlerAdapter
HandlerAdapter
接口为不同的 Controller
适配提供了统一的抽象,让 DispatcherServlet
能够以相同的方式调用不同类型的 Controller
。
public interface HandlerAdapter {boolean supports(Object handler); // 是否支持该 handlerModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
DispatcherServlet
通过HandlerAdapter
调用 Controller,而不关心具体实现。
被适配者(Adaptee)
@Controller
(注解方式)HttpRequestHandler
(函数式接口)Servlet
(传统方式)
适配器(Adapter)
RequestMappingHandlerAdapter
(处理@Controller
):专门处理使用@Controller
注解的控制器,它会解析控制器中的注解,将请求映射到对应的处理方法上。SimpleControllerHandlerAdapter
(处理Controller
接口):专门处理使用@Controller
注解的控制器,它会解析控制器中的注解,将请求映射到对应的处理方法上。HttpRequestHandlerAdapter
(处理HttpRequestHandler
):处理实现HttpRequestHandler
接口的控制器,直接调用其handleRequest
方法。
使用示例:
-
DispatcherServlet
借助HandlerMapping
找到对应的 Controller。 -
遍历
HandlerAdapter
列表,找到支持该 Controller 的适配器。 -
调用
handlerAdapter.handle()
执行 Controller 逻辑。// DispatcherServlet.doDispatch() HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
装饰器模式
装饰器模式原理参考:设计模式——装饰器模式
装饰器模式(Decorator Pattern)在 Spring 中主要用于动态增强对象功能,而无需修改原始类。
Spring 中配置 DataSource 的时候,DataSource 可能是不同的数据库和数据源,项目需要连接多个数据库,这种模式让我们可以根据客户需求切换不同的数据源。
在 Spring 对 DataSource
的装饰场景中,DataSource
接口是核心组件接口,具体的数据源实现类(如 BasicDataSource
、HikariDataSource
)是具体组件,而那些以 Wrapper
或 Decorator
结尾的类就是装饰器,它们可以动态地给具体组件添加额外功能。
Spring JDBC 的 DataSource
装饰
Spring 对 DataSource
进行装饰,提供延迟加载、连接池管理、事务控制等功能。
组件接口:DataSource
(JDBC 标准)
public interface DataSource {Connection getConnection() throws SQLException;
}
具体组件(ConcreteComponent)
BasicDataSource
(Apache DBCP 实现)HikariDataSource
(HikariCP 实现)
装饰器(Decorator)
LazyConnectionDataSourceProxy
(延迟获取连接):会延迟真实连接的创建。当应用调用getConnection()
方法时,它不会立即从底层数据源获取连接,而是在真正需要使用连接执行 SQL 操作时才去获取。这样可以减少不必要的连接创建,提高资源的使用效率。TransactionAwareDataSourceProxy
(支持 Spring 事务):确保从该数据源获取的连接能够参与 Spring 的事务管理。当在 Spring 事务环境中使用TransactionAwareDataSourceProxy
包装的数据源时,它会保证同一个事务中使用的是同一个连接,从而保证事务的一致性。DelegatingDataSource
(动态切换数据源):DelegatingDataSource
可以实现动态切换数据源的功能。在一些多数据源的应用场景中,根据不同的业务需求,可能需要在不同的数据源之间进行切换。
使用示例:
-
应用调用
decoratedDataSource.getConnection()
。 -
LazyConnectionDataSourceProxy
延迟创建真实连接。 -
TransactionAwareDataSourceProxy
确保连接参与 Spring 事务。// 原始 DataSource HikariDataSource rawDataSource = new HikariDataSource();// 装饰:延迟加载 + 事务支持 DataSource decoratedDataSource = new TransactionAwareDataSourceProxy(new LazyConnectionDataSourceProxy(rawDataSource) );
模板模式
模板方法模式原理参考:设计模式——模板方法模式
模板方法模式(Template Method Pattern)在 Spring 中广泛用于定义算法骨架,允许子类重写特定步骤。
Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,就是用到了模板模式。
Spring JDBC 的 JdbcTemplate
JdbcTemplate
使用模板方法模式封装 JDBC 操作流程(获取连接、执行 SQL、处理异常、释放资源),用户只需关注 SQL 执行逻辑。在 JdbcTemplate
里,JdbcTemplate
类定义了 JDBC 操作的整体流程,而具体的 SQL 执行逻辑则由实现 StatementCallback
接口的类来完成。
设计思路:
- 固定流程:
JdbcTemplate
所控制的获取连接、异常处理和资源释放这些固定步骤,是 JDBC 操作中通用且重复的部分。把这些步骤封装在JdbcTemplate
中,能避免代码的重复编写,同时也保证了资源管理的正确性和一致性。 - 扩展点:
StatementCallback
接口中的doInStatement
方法是一个扩展点,允许用户根据实际需求实现doInStatement()
方法来决定具体 SQL 逻辑。
抽象模板类:JdbcTemplate
public class JdbcTemplate {// 模板方法:定义 JDBC 操作流程public <T> T execute(StatementCallback<T> action) throws DataAccessException {// 1. 获取连接(固定步骤)Connection con = DataSourceUtils.getConnection(obtainDataSource());try {// 2. 创建 Statement(固定步骤)Statement stmt = con.createStatement();try {// 3. 调用子类实现的逻辑(可变步骤)return action.doInStatement(stmt);} finally {JdbcUtils.closeStatement(stmt);}} catch (SQLException ex) {// 4. 异常处理(固定步骤)throw translateException("StatementCallback", sql, ex);} finally {// 5. 释放连接(固定步骤)DataSourceUtils.releaseConnection(con, getDataSource());}}
}
子类实现:StatementCallback
(函数式接口)
@FunctionalInterface
public interface StatementCallback<T> {T doInStatement(Statement stmt) throws SQLException; // 由用户实现
}
调用示例:
jdbcTemplate.execute(stmt -> {stmt.execute("UPDATE users SET name = 'Jack' WHERE id = 1"); // 只需实现 SQL 逻辑return null;
});
观察者模式
Spring 事件驱动模型就是观察者模式很经典的一个应用。观察者模式(Observer Pattern)在 Spring 中用于实现 事件驱动架构,允许对象(观察者)监听特定事件(被观察者)并作出响应。Spring 通过 ApplicationEvent
和 ApplicationListener
机制实现了这一模式。
Spring 事件机制(ApplicationEvent
)
Spring 提供了内置事件(如 ContextRefreshedEvent
、ContextClosedEvent
)和自定义事件支持。
事件基类:ApplicationEvent
public abstract class ApplicationEvent extends EventObject {private final long timestamp; // 事件发生时间public ApplicationEvent(Object source) {super(source);}
}
被观察者:ApplicationEventPublisher
ApplicationEventPublisher
接口定义了发布事件的方法 publishEvent
。AbstractApplicationContext
作为其默认实现,它内部维护了一个观察者(ApplicationListener
)列表。当有事件发生时,AbstractApplicationContext
会根据事件类型找到对应的观察者,并通知它们进行处理。这种设计使得事件的发布和处理解耦,发布者只需要关心事件的发布,而不需要了解具体的处理逻辑。
public interface ApplicationEventPublisher {void publishEvent(ApplicationEvent event); // 发布事件
}
观察者:ApplicationListener
ApplicationListener
接口是一个函数式接口,它定义了处理事件的方法 onApplicationEvent
。实现该接口的类就是事件的观察者,当接收到匹配类型的事件时,会执行 onApplicationEvent
方法中的逻辑。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event); // 处理事件
}
事件传播流程:
-
发布事件:调用
applicationContext.publishEvent(new MyEvent())
。 -
通知观察者:
AbstractApplicationContext
遍历所有ApplicationListener
,匹配事件类型并调用invokeListener
方法来触发观察者的onApplicationEvent
方法,从而实现事件的处理。。// AbstractApplicationContext.publishEvent() protected void publishEvent(Object event) {// 1. 获取所有观察者Collection<ApplicationListener<?>> listeners = getApplicationListeners(event, type);// 2. 遍历通知for (ApplicationListener<?> listener : listeners) {invokeListener(listener, event);} }
策略模式
策略模式(Strategy Pattern)在 Spring 中用于动态选择算法或行为,通过接口抽象策略实现,使系统更灵活。
Spring 中资源访问接口 Resource 的设计是一种典型的策略模式。Resource 接口是所有资源访问类所实现的接口,Resource 接口就代表资源访问策略,但具体采用哪种策略实现,Resource 接口并不理会。客户端程序只和 Resource 接口耦合,并不知道底层采用何种资源访问策略,这样客户端程序可以在不同的资源访问策略之间自由切换。
Spring 资源加载策略(ResourceLoader
)
Spring 通过 ResourceLoader
动态加载类路径、文件系统、URL 等资源。
策略接口:ResourceLoader
ResourceLoader
接口定义了资源加载的策略,其中 getResource(String location)
方法是核心方法,它根据传入的资源位置 location
返回对应的 Resource
对象。这个接口为不同的资源加载策略提供了统一的抽象,使得客户端可以通过该接口来获取资源,而无需关心具体的加载实现。
public interface ResourceLoader {Resource getResource(String location); // 定义资源加载策略
}
具体策略:
ClassPathResourceLoader
(类路径资源):用于加载类路径下的资源。在实际的 Spring 实现中,当资源位置以"classpath:"
开头时,会使用ClassPathResource
来加载资源,它可以从类路径中找到并加载相应的文件。FileSystemResourceLoader
(文件系统资源):用于加载文件系统中的资源。当资源位置以"file:"
开头时,会使用FileSystemResource
来加载资源,它可以直接访问文件系统中的文件。UrlResourceLoader
(网络资源):用于加载网络资源。如果资源位置不以"classpath:"
或"file:"
开头,会默认使用UrlResource
来加载资源,它可以通过 URL 来访问网络上的资源。
上下文:DefaultResourceLoader
DefaultResourceLoader
是 ResourceLoader
接口的默认实现,它充当了策略模式中的上下文角色。在 getResource
方法中,根据资源位置的前缀来动态选择合适的资源加载策略。这种设计使得客户端可以通过统一的接口来获取不同类型的资源,而无需关心具体的加载方式。
public class DefaultResourceLoader implements ResourceLoader {public Resource getResource(String location) {if (location.startsWith("classpath:")) {return new ClassPathResource(location.substring("classpath:".length()));} else if (location.startsWith("file:")) {return new FileSystemResource(location.substring("file:".length()));} else {return new UrlResource(location); // 默认策略}}
}
调用流程:
客户端代码通过创建 DefaultResourceLoader
实例,并调用其 getResource
方法,传入资源位置,就可以动态地选择合适的资源加载策略。
ResourceLoader loader = new DefaultResourceLoader();
Resource resource = loader.getResource("classpath:application.properties"); // 动态选择策略