Spring中使用的設計模式
Spring框架是每個java程式猿入門級的框架也是最重要的框架,而Spring中也採用了很多的設計模式,這些也會成為我們面試過程中經常會問到的問題,所以本文就整理出Spring中具體使用的哪些設計模式。
********
********
@
Spring使用的設計模式
1.單例模式
單例模式應該是大家印象最深的一種設計模式了。在Spring中最明顯的使用場景是在配置檔案中配置註冊bean物件的時候 設定scope的值為singleton 。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="com.dpb.pojo.User" id="user" scope="singleton"> <property name="name" value="波波烤鴨"></property> </bean> </beans>
原始碼實現:AbstractBeanFactory的getBean方法中

protected 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) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
雙重判斷加鎖的實現!!!
2.原型模式
原型模式也叫克隆模式,Spring中該模式使用的很明顯,和單例一樣在bean標籤中設定scope的屬性為prototype即表示該bean以克隆的方式生成
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="com.dpb.pojo.User" id="user" scope="prototype"> <property name="name" value="波波烤鴨"></property> </bean> </beans>
3.模板模式
模板模式的核心是父類定義好流程,然後將流程中需要子類實現的方法就抽象話留給子類實現,Spring中的JdbcTemplate就是這樣的實現。我們知道jdbc的步驟是固定的(
- 載入驅動,
- 獲取連線通道,
- 構建sql語句.
- 執行sql語句,
- 關閉資源),
在這些步驟中第3步和第四步是不確定的,所以就留給客戶實現,而我們實際使用JdbcTemplate的時候也確實是只需要構建SQL就可以了.這就是典型的模板模式。我們以query方法為例來看下JdbcTemplate中的程式碼
// 在execute方法中定義好了jdbc操作的流程 // action.doInStatement(stmtToUse);是回撥方法也就是鉤子 @Override public <T> T execute(StatementCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(getDataSource()); Statement stmt = null; try { Connection conToUse = con; if (this.nativeJdbcExtractor != null && this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) { conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } stmt = conToUse.createStatement(); applyStatementSettings(stmt); Statement stmtToUse = stmt; if (this.nativeJdbcExtractor != null) { stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt); } T result = action.doInStatement(stmtToUse); handleWarnings(stmt); return result; } catch (SQLException ex) { // Release Connection early, to avoid potential connection pool deadlock // in the case when the exception translator hasn't been initialized yet. JdbcUtils.closeStatement(stmt); stmt = null; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex); } finally { JdbcUtils.closeStatement(stmt); DataSourceUtils.releaseConnection(con, getDataSource()); } }
query方法
@Override public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException { Assert.notNull(sql, "SQL must not be null"); Assert.notNull(rse, "ResultSetExtractor must not be null"); if (logger.isDebugEnabled()) { logger.debug("Executing SQL query [" + sql + "]"); } // 實現模板中預留的功能 class QueryStatementCallback implements StatementCallback<T>, SqlProvider { @Override public T doInStatement(Statement stmt) throws SQLException { ResultSet rs = null; try { // 此處具體執行查詢操作 rs = stmt.executeQuery(sql); ResultSet rsToUse = rs; if (nativeJdbcExtractor != null) { rsToUse = nativeJdbcExtractor.getNativeResultSet(rs); } // 處理資料封裝操作 return rse.extractData(rsToUse); } finally { JdbcUtils.closeResultSet(rs); } } @Override public String getSql() { return sql; } } return execute(new QueryStatementCallback()); }
4.觀察者模式
觀察者模式定義的是物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新。使用比較場景是在監聽器中而spring中Observer模式常用的地方也是listener的實現。如ApplicationListener。 Spring中的事件監聽請參考我的另一篇文章
Spring之事件監聽(觀察者模型)5.工廠模式
簡單工廠模式
簡單工廠模式就是通過工廠根據傳遞進來的引數決定產生哪個物件。Spring中我們通過getBean方法獲取物件的時候根據id或者name獲取就是簡單工廠模式了。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:annotation-config/> <bean class="com.dpb.pojo.User" id="user"> <property name="name" value="波波烤鴨"></property> </bean> </beans>
工廠方法模式
在Spring中我們一般是將Bean的例項化直接交給容器去管理的,實現了使用和建立的分離,這時容器直接管理物件,還有種情況是,bean的建立過程我們交給一個工廠去實現,而Spring容器管理這個工廠。這個就是我們講的工廠模式,在Spring中有兩種實現一種是靜態工廠方法模式,一種是動態工廠方法模式。以靜態工廠來演示
/** * User 工廠類 * @author dpb[波波烤鴨] * */ public class UserFactory { /** * 必須是static方法 * @return */ public static UserBean getInstance(){ return new UserBean(); } }
application.xml檔案中註冊
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 靜態工廠方式配置 配置靜態工廠及方法 --> <bean class="com.dpb.factory.UserFactory" factory-method="getInstance" id="user2"/> </beans>
6.介面卡模式
將一個類的介面轉換成客戶希望的另外一個介面。使得原本由於介面不相容而不能一起工作的那些類可以在一起工作。這就是介面卡模式。在Spring中在AOP實現中的Advice和interceptor之間的轉換就是通過介面卡模式實現的。
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); } }
詳細介紹可以參考此文 Spring之AOP介面卡模式
7.裝飾者模式
裝飾者模式又稱為包裝模式(Wrapper),作用是用來動態的為一個物件增加新的功能。裝飾模式是一種用於代替繼承的技術,無須通過繼承增加子類就能擴充套件物件的新功能。使用物件的關聯關係代替繼承關係,更加靈活,同時避免型別體系的快速膨脹。
spring中用到的包裝器模式在類名上有兩種表現:一種是類名中含有Wrapper,另一種是類名中含有Decorator。基本上都是動態地給一個物件新增一些額外的職責。
具體的使用在Spring session框架中的SessionRepositoryRequestWrapper使用包裝模式對原生的request的功能進行增強,可以將session中的資料和分散式資料庫進行同步,這樣即使當前tomcat崩潰,session中的資料也不會丟失。
檢視需要的maven依賴
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session</artifactId> <version>1.3.1.RELEASE</version> </dependency>
8.代理模式
代理模式應該是大家非常熟悉的設計模式了,在Spring中AOP的實現中代理模式使用的很徹底,如果不瞭解代理模式歡迎檢視我之前的文章,連結在頂部。
9.策略模式
策略模式對應於解決某一個問題的一個演算法族,允許使用者從該演算法族中任選一個演算法解決某一問題,同時可以方便的更換演算法或者增加新的演算法。並且由客戶端決定呼叫哪個演算法,spring中在例項化物件的時候用到Strategy模式。
@Override public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) { // Don't override the class with CGLIB if no overrides. if (bd.getMethodOverrides().isEmpty()) { Constructor<?> constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class<?> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() { @Override public Constructor<?> run() throws Exception { return clazz.getDeclaredConstructor((Class[]) null); } }); } else { constructorToUse =clazz.getDeclaredConstructor((Class[]) null); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } return BeanUtils.instantiateClass(constructorToUse); } else { // Must generate CGLIB subclass. return instantiateWithMethodInjection(bd, beanName, owner); } }