Spring框架是每個java程序猿入門級的框架也是最重要的框架,而Spring中也采用了很多的設計模式,這些也會成為我們面試過程中經常會問到的問題,所以本文就整理出Spring中具體使用的哪些設計模式。
Java單例模式
Java原型模式(prototype)
Java模板模式(template)
Java觀察者模式(Observer)
Java工廠模式
Java適配器模式(adapter)
Java裝飾者模式(decorator)
Java代理模式
Java策略模式(Strategy)
@
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);
}
}