Spring用到了哪些設(shè)計(jì)模式?你知道嗎?
松哥的 Spring 源碼分析課程結(jié)束好久了,今天和大伙總結(jié)下 Spring 中都用到了哪些設(shè)計(jì)模式。
Spring 作為企業(yè)級(jí)應(yīng)用開(kāi)發(fā)中最受歡迎的框架之一,其內(nèi)部廣泛采用了多種設(shè)計(jì)模式,使得框架不僅功能強(qiáng)大,而且具有很高的可擴(kuò)展性和靈活性。是我們學(xué)習(xí)設(shè)計(jì)模式不可多得的優(yōu)質(zhì)材料。
一 單例模式 (Singleton Pattern)
在 Spring 框架中,單例模式被廣泛應(yīng)用于各種組件和工具類,以確保在整個(gè)應(yīng)用程序生命周期中,這些對(duì)象只有一個(gè)實(shí)例,從而節(jié)省內(nèi)存和提高性能。
松哥這里給大家舉幾個(gè)常見(jiàn)的 Spring 中單例的應(yīng)用。
BeanFactory
BeanFactory 是 Spring 框架中的另一個(gè)核心接口,它負(fù)責(zé)創(chuàng)建和管理 bean。BeanFactory 的實(shí)現(xiàn)類(如 DefaultListableBeanFactory)也通常以單例模式存在。
源碼示例:
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
@Override
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
@Override
public Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
singletonObject = getEarlyBeanReference(beanName, mbd, bean);
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
DefaultListableBeanFactory 本身的初始化邏輯如下:
圖片
可以看到,如果存在 BeanFactory,則先銷毀,再創(chuàng)建新的 BeanFactory。
二 工廠模式 (Factory Pattern)
工廠模式提供了一種創(chuàng)建對(duì)象的接口,但讓子類決定實(shí)例化哪一個(gè)類。Spring 中的 BeanFactory 接口及其實(shí)現(xiàn)類(如 DefaultListableBeanFactory)就是工廠模式的應(yīng)用。通過(guò)這些工廠,我們可以方便地管理和創(chuàng)建bean實(shí)例。
Spring 源碼案例
public interface BeanFactory {
Object getBean(String name) throws BeansException;
}
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
}
DefaultListableBeanFactory 是 BeanFactory 的一個(gè)實(shí)現(xiàn),負(fù)責(zé)創(chuàng)建和管理 bean 的實(shí)例。
三 原型模式 (Prototype Pattern)
原型模式通過(guò)復(fù)制現(xiàn)有對(duì)象來(lái)創(chuàng)建新對(duì)象,而無(wú)需知道任何創(chuàng)建細(xì)節(jié)。在 Spring 中,我們可以通過(guò)設(shè)置 bean 的 scope 屬性為 prototype 來(lái)實(shí)現(xiàn)每次請(qǐng)求時(shí)都創(chuàng)建一個(gè)新的 bean 實(shí)例。
Spring 源碼案例
<bean id="exampleBean" class="com.example.ExampleBean" scope="prototype"/>
這個(gè)配置表示每次請(qǐng)求 exampleBean 時(shí),都會(huì)創(chuàng)建一個(gè)新的實(shí)例。
四 模板方法模式 (Template Method Pattern)
在 Spring 框架中,模板方法模式被廣泛應(yīng)用于多個(gè)模塊,以提供靈活且可擴(kuò)展的解決方案。模板方法模式的核心思想是定義一個(gè)操作中的算法骨架,而將一些步驟延遲到子類中實(shí)現(xiàn)。這樣,子類可以不改變算法結(jié)構(gòu)的情況下重新定義算法的某些特定步驟。
這里松哥和大家分享兩個(gè)經(jīng)典的模版方法模式:JdbcTemplate 和 PlatformTransactionManager。
JdbcTemplate
JdbcTemplate 是 Spring JDBC 模塊中的一個(gè)核心類,它使用模板方法模式來(lái)簡(jiǎn)化數(shù)據(jù)庫(kù)操作。
模板方法:
- execute:執(zhí)行 SQL 語(yǔ)句的基本方法。
- query:查詢數(shù)據(jù)庫(kù)的基本方法。
- update:執(zhí)行更新操作的基本方法。
具體實(shí)現(xiàn):
- queryForObject:查詢單個(gè)對(duì)象。
- queryForList:查詢列表。
- batchUpdate:批量更新。
源碼示例:
public abstract class JdbcOperations {
public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException {
return queryForObject(sql, args, getJdbcOperations().new SingleColumnRowMapper(rowMapper));
}
public int update(String sql, PreparedStatementSetter pss) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL update [" + sql + "]");
}
Connection con = DataSourceUtils.getConnection(getDataSource());
PreparedStatement ps = null;
try {
ps = con.prepareStatement(sql);
pss.setValues(ps);
int rows = ps.executeUpdate();
if (logger.isDebugEnabled()) {
logger.debug(rows + " rows affected");
}
return rows;
} catch (Throwable ex) {
// Handle exception
throw translateException("PreparedStatement", sql, ex);
} finally {
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
}
PlatformTransactionManager
PlatformTransactionManager 接口定義了事務(wù)管理的基本方法,具體的事務(wù)管理實(shí)現(xiàn)類(如 DataSourceTransactionManager)則提供了具體的實(shí)現(xiàn)。
模板方法:
- getTransaction:獲取事務(wù)。
- commit:提交事務(wù)。
- rollback:回滾事務(wù)。
具體實(shí)現(xiàn):
- DataSourceTransactionManager:基于數(shù)據(jù)源的事務(wù)管理。
- JtaTransactionManager:基于JTA的事務(wù)管理。
源碼示例:
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager {
@Override
protected TransactionStatus doBegin(Object transaction, TransactionDefinition definition) {
// 獲取數(shù)據(jù)庫(kù)連接
ConnectionHolder conHolder = (ConnectionHolder) transaction;
Connection con = conHolder.getConnection();
// 設(shè)置事務(wù)隔離級(jí)別
Integer previousIsolationLevel = DataSourceUtils.storeIsolationLevelIfNotSet(con, definition.getIsolationLevel());
// 開(kāi)啟事務(wù)
boolean newTransaction = false;
if (!con.getAutoCommit()) {
logger.debug("Not switching JDBC Connection [" + con + "] to manual commit because already manually committed");
} else {
newTransaction = true;
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
// 返回事務(wù)狀態(tài)
return new DataSourceTransactionObject(conHolder, previousIsolationLevel, newTransaction);
}
}
五 適配器模式 (Adapter Pattern)
適配器模式將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另一個(gè)接口。SpringMVC 中的 HandlerAdapter 接口及其多個(gè)實(shí)現(xiàn)類(如 RequestMappingHandlerAdapter)就是適配器模式的應(yīng)用,它們負(fù)責(zé)處理不同類型的控制器方法。
Spring 源碼案例
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
public class RequestMappingHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof HandlerMethod;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((HandlerMethod) handler).invokeAndHandle(request, response);
}
}
RequestMappingHandlerAdapter 適配了 HandlerMethod 類型的控制器方法,使其能夠處理HTTP請(qǐng)求。
六 裝飾者模式 (Decorator Pattern)
裝飾者模式允許動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)。Spring AOP 中的切面實(shí)現(xiàn)可以看作是對(duì)原有對(duì)象的一種裝飾。通過(guò) @Around 注解定義的環(huán)繞通知可以在不改變?cè)袠I(yè)務(wù)邏輯的情況下增加額外的功能。
Spring 源碼案例
public class TransactionInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 開(kāi)始事務(wù)
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
Object result = invocation.proceed();
transactionManager.commit(status);
return result;
} catch (RuntimeException ex) {
transactionManager.rollback(status);
throw ex;
}
}
}
TransactionInterceptor 是一個(gè)典型的裝飾者模式應(yīng)用,它在方法調(diào)用前后添加了事務(wù)管理的邏輯。
七 觀察者模式 (Observer Pattern)
觀察者模式定義了對(duì)象之間的一對(duì)多依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生變化時(shí),所有依賴于它的對(duì)象都會(huì)得到通知并自動(dòng)更新。Spring 中的 ApplicationEvent 和 ApplicationListener 接口共同實(shí)現(xiàn)了觀察者模式。
Spring 源碼案例
public interface ApplicationListener<E extends ApplicationEvent> {
void onApplicationEvent(E event);
}
public class ContextRefreshedEvent extends ApplicationEvent {
public ContextRefreshedEvent(Object source) {
super(source);
}
}
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("Context refreshed!");
}
}
MyApplicationListener 監(jiān)聽(tīng)了 ContextRefreshedEvent 事件,當(dāng)上下文刷新時(shí),會(huì)輸出一條消息。
八 代理模式 (Proxy Pattern)
代理模式為其他對(duì)象提供一個(gè)代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。Spring AOP 使用動(dòng)態(tài)代理技術(shù)(JDK 動(dòng)態(tài)代理或 CGLIB)來(lái)實(shí)現(xiàn)代理模式。例如,當(dāng)你在方法上添加事務(wù)管理注解 @Transactional 時(shí),Spring 會(huì)自動(dòng)創(chuàng)建一個(gè)代理對(duì)象來(lái)管理事務(wù)的開(kāi)始和結(jié)束。
Spring 源碼案例
public class DefaultAopProxyFactory implements AopProxyFactory {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
return new ObjenesisCglibAopProxy(config);
} else {
return new JdkDynamicAopProxy(config);
}
}
}
DefaultAopProxyFactory 根據(jù)配置選擇使用 CGLIB 或 JDK 動(dòng)態(tài)代理來(lái)創(chuàng)建代理對(duì)象。
九 組合模式 (Composite Pattern)
組合模式允許將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。在 Spring 配置中,可以將多個(gè) bean 組合在一起形成一個(gè)復(fù)雜的結(jié)構(gòu)。
Spring 源碼案例
<beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testdb"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
這個(gè)配置文件中,jdbcTemplate 依賴于 dataSource,形成了一個(gè)簡(jiǎn)單的組合結(jié)構(gòu)。
十 策略模式 (Strategy Pattern)
策略模式定義了一系列算法,并將每一個(gè)算法封裝起來(lái),使它們可以互換。Spring 中的 Resource 接口及其多個(gè)實(shí)現(xiàn)類(如 ClassPathResource, FileSystemResource)就是策略模式的應(yīng)用,可以根據(jù)需要選擇不同的資源訪問(wèn)方式。
Spring 源碼案例
public interface ResourceLoader {
Resource getResource(String location);
}
public class DefaultResourceLoader implements ResourceLoader {
@Override
public Resource getResource(String location) {
if (location.startsWith("classpath:")) {
return new ClassPathResource(location.substring("classpath:".length()));
} else {
return new FileSystemResource(location);
}
}
}
DefaultResourceLoader 根據(jù)資源路徑的前綴選擇合適的 Resource 實(shí)現(xiàn)類。
十一 小結(jié)
通過(guò)上述案例,我們可以看到 Spring 框架巧妙地運(yùn)用了多種設(shè)計(jì)模式,不僅提高了代碼的復(fù)用性和可維護(hù)性,還增強(qiáng)了框架的靈活性和擴(kuò)展性。