Spring框架系列(3) - 深入淺出Spring核心之控制反轉(IOC)

語言: CN / TW / HK

Spring基礎 - Spring簡單例子引入Spring的核心 中向你展示了IoC的基礎含義,同時以此發散了一些IoC相關知識點; 本節將在此基礎上進一步解讀IOC的含義以及IOC的使用方式。@pdai

  • Spring框架系列(3) - Spring核心之控制反轉(IOC)

引入

我們在 Spring基礎 - Spring簡單例子引入Spring的核心 中向你展示了IoC的基礎含義,同時以此發散了一些IoC相關知識點。

  1. Spring框架管理這些Bean的建立工作,即由使用者管理Bean轉變為框架管理Bean,這個就叫 控制反轉 - Inversion of Control (IoC)
  2. Spring 框架託管建立的Bean放在哪裡呢? 這便是 IoC Container ;
  3. Spring 框架為了更好讓使用者配置Bean,必然會引入 不同方式來配置Bean? 這便是xml配置,Java配置,註解配置 等支援
  4. Spring 框架既然接管了Bean的生成,必然需要 管理整個Bean的生命週期 等;
  5. 應用程式程式碼從Ioc Container中獲取依賴的Bean,注入到應用程式中,這個過程叫 依賴注入(Dependency Injection,DI) ; 所以說控制反轉是通過依賴注入實現的,其實它們是同一個概念的不同角度描述。通俗來說就是 IoC是設計思想,DI是實現方式
  6. 在依賴注入時,有哪些方式呢?這就是構造器方式,@Autowired, @Resource, @Qualifier... 同時Bean之間存在依賴(可能存在先後順序問題,以及 迴圈依賴問題 等)

本節將在此基礎上進一步解讀IOC的含義以及IOC的使用方式;後續的文章還將深入IOC的實現原理:

如何理解IoC

如果你有精力看英文,首推 Martin Fowler大師的 Inversion of Control Containers and the Dependency Injection pattern ;其次IoC作為一種設計思想,不要過度解讀,而是應該簡化理解,所以我這裡也整合了 張開濤早前的部落格 IoC基礎 並加入了自己的理解。

Spring Bean是什麼

IoC Container管理的是Spring Bean, 那麼Spring Bean是什麼呢?

Spring裡面的bean就類似是定義的一個元件,而這個元件的作用就是實現某個功能的,這裡所定義的bean就相當於給了你一個更為簡便的方法來呼叫這個元件去實現你要完成的功能。

IoC是什麼

Ioc—Inversion of Control,即“控制反轉”, 不是什麼技術,而是一種設計思想 。在Java開發中,Ioc意味著將你設計好的物件交給容器控制,而不是傳統的在你的物件內部直接控制。

我們來深入分析一下:

  • 誰控制誰,控制什麼

傳統Java SE程式設計,我們直接在物件內部通過new進行建立物件,是程式主動去建立依賴物件;而IoC是有專門一個容器來建立這些物件,即由Ioc容器來控制對 象的建立;誰控制誰?當然是IoC 容器控制了物件;控制什麼?那就是主要控制了外部資源獲取(不只是物件包括比如檔案等)。

  • 為何是反轉,哪些方面反轉了 ?

有反轉就有正轉,傳統應用程式是由我們自己在物件中主動控制去直接獲取依賴物件,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴物件;為何是反轉?因為由容器幫我們查詢及注入依賴物件,物件只是被動的接受依賴物件,所以是反轉;哪些方面反轉了?依賴物件的獲取被反轉了。

  • 用圖例說明一下 ?

傳統程式設計下,都是主動去建立相關物件然後再組合起來:

當有了IoC/DI的容器後,在客戶端類中不再主動去建立這些物件了,如圖

IoC能做什麼

IoC 不是一種技術,只是一種思想 ,一個重要的面向物件程式設計的法則,它能指導我們如何設計出松耦合、更優良的程式。

傳統應用程式都是由我們在類內部主動建立依賴物件,從而導致類與類之間高耦合,難於測試;有了IoC容器後, 把建立和查詢依賴物件的控制權交給了容器,由容器進行注入組合物件,所以物件與物件之間是 鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程式的整個體系結構變得非常靈活

其實IoC對程式設計帶來的最大改變不是從程式碼上,而是從思想上,發生了“主從換位”的變化。應用程式原本是老大,要獲取什麼資源都是主動出擊,但是在IoC/DI思想中,應用程式就變成被動的了,被動的等待IoC容器來建立並注入它所需要的資源了。

IoC很好的體現了面向物件設計法則之一—— 好萊塢法則:“別找我們,我們找你” ;即由IoC容器幫物件找相應的依賴物件並注入,而不是由物件主動去找。

IoC和DI是什麼關係

控制反轉是通過依賴注入實現的,其實它們是同一個概念的不同角度描述。通俗來說就是 IoC是設計思想,DI是實現方式

DI—Dependency Injection,即依賴注入:元件之間依賴關係由容器在執行期決定,形象的說,即由容器動態的將某個依賴關係注入到元件之中。依賴注入的目的並非為軟體系統帶來更多功能,而是為了提升元件重用的頻率,併為系統搭建一個靈活、可擴充套件的平臺。通過依賴注入機制,我們只需要通過簡單的配置,而無需任何程式碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。

我們來深入分析一下:

  • 誰依賴於誰

當然是應用程式依賴於IoC容器;

  • 為什麼需要依賴

應用程式需要IoC容器來提供物件需要的外部資源;

  • 誰注入誰

很明顯是IoC容器注入應用程式某個物件,應用程式依賴的物件;

  • 注入了什麼

就是注入某個物件所需要的外部資源(包括物件、資源、常量資料)。

  • IoC和DI由什麼關係呢

其實它們是同一個概念的不同角度描述,由於控制反轉概念比較含糊(可能只是理解為容器控制物件這一個層面,很難讓人想到誰來維護物件關係),所以2004年大師級人物Martin Fowler又給出了一個新的名字:“依賴注入”,相對IoC 而言,“依賴注入”明確描述了“被注入物件依賴IoC容器配置依賴物件”。通俗來說就是 IoC是設計思想,DI是實現方式

Ioc 配置的三種方式

Spring基礎 - Spring簡單例子引入Spring的核心 已經給出了三種配置方式,這裡再總結下;總體上目前的主流方式是 註解 + Java 配置

xml 配置

顧名思義,就是將bean的資訊配置.xml檔案裡,通過Spring載入檔案為我們建立bean。這種方式出現很多早前的SSM專案中,將第三方類庫或者一些配置工具類都以這種方式進行配置,主要原因是由於第三方類不支援Spring註解。

  • 優點: 可以使用於任何場景,結構清晰,通俗易懂

  • 缺點: 配置繁瑣,不易維護,枯燥無味,擴充套件性差

舉例:

  1. 配置xx.xml檔案
  2. 宣告名稱空間和配置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">
    <!-- services -->
    <bean id="userService" class="tech.pdai.springframework.service.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions for services go here -->
</beans>

Java 配置

將類的建立交給我們配置的JavcConfig類來完成,Spring只負責維護和管理,採用純Java建立方式。其本質上就是把在XML上的配置宣告轉移到Java配置類中

  • 優點:適用於任何場景,配置方便,因為是純Java程式碼,擴充套件性高,十分靈活

  • 缺點:由於是採用Java類的方式,宣告不明顯,如果大量配置,可讀性比較差

舉例:

  1. 建立一個配置類, 新增@Configuration註解宣告為配置類
  2. 建立方法,方法上加上@bean,該方法用於建立例項並返回,該例項建立後會交給spring管理,方法名建議與例項名相同(首字母小寫)。注:例項類不需要加任何註解
/**
 * @author pdai
 */
@Configuration
public class BeansConfig {

    /**
     * @return user dao
     */
    @Bean("userDao")
    public UserDaoImpl userDao() {
        return new UserDaoImpl();
    }

    /**
     * @return user service
     */
    @Bean("userService")
    public UserServiceImpl userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao());
        return userService;
    }
}

註解配置

通過在類上加註解的方式,來宣告一個類交給Spring管理,Spring會自動掃描帶有@Component,@Controller,@Service,@Repository這四個註解的類,然後幫我們建立並管理,前提是需要先配置Spring的註解掃描器。

  • 優點:開發便捷,通俗易懂,方便維護。

  • 缺點:具有侷限性,對於一些第三方資源,無法添加註解。只能採用XML或JavaConfig的方式配置

舉例:

  1. 對類新增@Component相關的註解,比如@Controller,@Service,@Repository
  2. 設定ComponentScan的basePackage, 比如 <context:component-scan base-package='tech.pdai.springframework'> , 或者 @ComponentScan("tech.pdai.springframework") 註解,或者 new AnnotationConfigApplicationContext("tech.pdai.springframework") 指定掃描的basePackage.
/**
 * @author pdai
 */
@Service
public class UserServiceImpl {

    /**
     * user dao impl.
     */
    @Autowired
    private UserDaoImpl userDao;

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return userDao.findUserList();
    }

}

依賴注入的三種方式

常用的注入方式主要有三種:構造方法注入(Construct注入),setter注入,基於註解的注入(介面注入)

setter方式

  • 在XML配置方式中 ,property都是setter方式注入,比如下面的xml:
<?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">
    <!-- services -->
    <bean id="userService" class="tech.pdai.springframework.service.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions for services go here -->
</beans>

本質上包含兩步:

  1. 第一步,需要new UserServiceImpl()建立物件, 所以需要預設建構函式
  2. 第二步,呼叫setUserDao()函式注入userDao的值, 所以需要setUserDao()函式

所以對應的service類是這樣的:

/**
 * @author pdai
 */
public class UserServiceImpl {

    /**
     * user dao impl.
     */
    private UserDaoImpl userDao;

    /**
     * init.
     */
    public UserServiceImpl() {
    }

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return this.userDao.findUserList();
    }

    /**
     * set dao.
     *
     * @param userDao user dao
     */
    public void setUserDao(UserDaoImpl userDao) {
        this.userDao = userDao;
    }
}
  • 在註解和Java配置方式下
/**
 * @author pdai
 */
public class UserServiceImpl {

    /**
     * user dao impl.
     */
    private UserDaoImpl userDao;

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return this.userDao.findUserList();
    }

    /**
     * set dao.
     *
     * @param userDao user dao
     */
    @Autowired
    public void setUserDao(UserDaoImpl userDao) {
        this.userDao = userDao;
    }
}

在Spring3.x剛推出的時候,推薦使用注入的就是這種, 但是這種方式比較麻煩,所以在Spring4.x版本中推薦建構函式注入。

建構函式

  • 在XML配置方式中<constructor-arg> 是通過建構函式引數注入,比如下面的xml:
<?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">
    <!-- services -->
    <bean id="userService" class="tech.pdai.springframework.service.UserServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions for services go here -->
</beans>

本質上是new UserServiceImpl(userDao)建立物件, 所以對應的service類是這樣的:

/**
 * @author pdai
 */
public class UserServiceImpl {

    /**
     * user dao impl.
     */
    private final UserDaoImpl userDao;

    /**
     * init.
     * @param userDaoImpl user dao impl
     */
    public UserServiceImpl(UserDaoImpl userDaoImpl) {
        this.userDao = userDaoImpl;
    }

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return this.userDao.findUserList();
    }

}
  • 在註解和Java配置方式下
/**
 * @author pdai
 */
 @Service
public class UserServiceImpl {

    /**
     * user dao impl.
     */
    private final UserDaoImpl userDao;

    /**
     * init.
     * @param userDaoImpl user dao impl
     */
    @Autowired // 這裡@Autowired也可以省略
    public UserServiceImpl(final UserDaoImpl userDaoImpl) {
        this.userDao = userDaoImpl;
    }

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return this.userDao.findUserList();
    }

}

在Spring4.x版本中推薦的注入方式就是這種, 具體原因看後續章節。

註解注入

以@Autowired(自動注入)註解注入為例,修飾符有三個屬性:Constructor,byType,byName。預設按照byType注入。

  • constructor :通過構造方法進行自動注入,spring會匹配與構造方法引數型別一致的bean進行注入,如果有一個多引數的構造方法,一個只有一個引數的構造方法,在容器中查詢到多個匹配多引數構造方法的bean,那麼spring會優先將bean注入到多引數的構造方法中。
  • byName :被注入bean的id名必須與set方法後半截匹配,並且id名稱的第一個單詞首字母必須小寫,這一點與手動set注入有點不同。
  • byType :查詢所有的set方法,將符合符合引數型別的bean注入。

比如:

/**
 * @author pdai
 */
@Service
public class UserServiceImpl {

    /**
     * user dao impl.
     */
    @Autowired
    private UserDaoImpl userDao;

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return userDao.findUserList();
    }

}

IoC和DI使用問題小結

這裡總結下實際開發中會遇到的一些問題:

為什麼推薦構造器注入方式?

先來看看Spring在文件裡怎麼說:

The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.

簡單的翻譯一下:這個構造器注入的方式 能夠保證注入的元件不可變,並且確保需要的依賴不為空 。此外,構造器注入的依賴總是能夠在返回客戶端(元件)程式碼的時候保證完全初始化的狀態。

下面來簡單的解釋一下:

  • 依賴不可變 :其實說的就是final關鍵字。
  • 依賴不為空 (省去了我們對其檢查):當要例項化UserServiceImpl的時候,由於自己實現了有引數的建構函式,所以不會呼叫預設建構函式,那麼就需要Spring容器傳入所需要的引數,所以就兩種情況:1、有該型別的引數->傳入,OK 。2:無該型別的引數->報錯。
  • 完全初始化的狀態 :這個可以跟上面的依賴不為空結合起來,向構造器傳參之前,要確保注入的內容不為空,那麼肯定要呼叫依賴元件的構造方法完成例項化。而在Java類載入例項化的過程中,構造方法是最後一步(之前如果有父類先初始化父類,然後自己的成員變數,最後才是構造方法),所以返回來的都是初始化之後的狀態。

所以通常是這樣的

/**
 * @author pdai
 */
 @Service
public class UserServiceImpl {

    /**
     * user dao impl.
     */
    private final UserDaoImpl userDao;

    /**
     * init.
     * @param userDaoImpl user dao impl
     */
    public UserServiceImpl(final UserDaoImpl userDaoImpl) {
        this.userDao = userDaoImpl;
    }

}

如果使用setter注入,缺點顯而易見,對於IOC容器以外的環境,除了使用反射來提供它需要的依賴之外, 無法複用該實現類 。而且將一直是個潛在的隱患,因為你不呼叫將一直無法發現NPE的存在。

// 這裡只是模擬一下,正常來說我們只會暴露介面給客戶端,不會暴露實現。
UserServiceImpl userService = new UserServiceImpl();
userService.findUserList(); // -> NullPointerException, 潛在的隱患

迴圈依賴的問題:使用field注入可能會導致迴圈依賴,即A裡面注入B,B裡面又注入A:

public class A {
    @Autowired
    private B b;
}

public class B {
    @Autowired
    private A a;
}

如果使用構造器注入,在spring專案啟動的時候,就會丟擲:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?從而提醒你避免迴圈依賴,如果是field注入的話,啟動的時候不會報錯,在使用那個bean的時候才會報錯。

我在使用構造器注入方式時注入了太多的類導致Bad Smell怎麼辦?

比如當你一個Controller中注入了太多的Service類,Sonar會給你提示相關告警

對於這個問題,說明你的類當中有太多的責任,那麼你要好好想一想是不是自己違反了類的 單一性職責原則 ,從而導致有這麼多的依賴要注入。

(pdai: 想起來一句話: 所有困難問題的解決方式,都在另外一個層次

@Autowired和@Resource以及@Inject等註解注入有何區別?

@Autowired和@Resource以及@Inject等註解注入有何區別? 這時平時在開發中,或者常見的面試題。

@Autowired

  • Autowired註解原始碼

在Spring 2.5 引入了 @Autowired 註解

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
  boolean required() default true;
}

從Autowired註解原始碼上看,可以使用在下面這些地方:

@Target(ElementType.CONSTRUCTOR) #建構函式
@Target(ElementType.METHOD) #方法
@Target(ElementType.PARAMETER) #方法引數
@Target(ElementType.FIELD) #欄位、列舉的常量
@Target(ElementType.ANNOTATION_TYPE) #註解

還有一個value屬性,預設是true。

  • 簡單總結

1、@Autowired是Spring自帶的註解,通過AutowiredAnnotationBeanPostProcessor 類實現的依賴注入

2、@Autowired可以作用在CONSTRUCTOR、METHOD、PARAMETER、FIELD、ANNOTATION_TYPE

3、@Autowired預設是根據型別(byType )進行自動裝配的

4、如果有多個型別一樣的Bean候選者,需要指定按照名稱(byName )進行裝配,則需要配合@Qualifier。

指定名稱後,如果Spring IOC容器中沒有對應的元件bean丟擲NoSuchBeanDefinitionException。也可以將@Autowired中required配置為false,如果配置為false之後,當沒有找到相應bean的時候,系統不會拋異常

  • 簡單使用程式碼

在欄位屬性上。

@Autowired
private HelloDao helloDao;

或者

private HelloDao helloDao;
public HelloDao getHelloDao() {
 return helloDao;
}
@Autowired
public void setHelloDao(HelloDao helloDao) {
 this.helloDao = helloDao;
}

或者

private HelloDao helloDao;
//@Autowired
public HelloServiceImpl(@Autowired HelloDao helloDao) {
 this.helloDao = helloDao;
}
// 構造器注入也可不寫@Autowired,也可以注入成功。

將@Autowired寫在被注入的成員變數上,setter或者構造器上,就不用再xml檔案中配置了。

如果有多個型別一樣的Bean候選者,則預設根據設定的屬性名稱進行獲取。如 HelloDao 在Spring中有 helloWorldDao 和 helloDao 兩個Bean候選者。

@Autowired
private HelloDao helloDao;

首先根據型別獲取,發現多個HelloDao,然後根據helloDao進行獲取,如果要獲取限定的其中一個候選者,結合@Qualifier進行注入。

@Autowired
@Qualifier("helloWorldDao")
private HelloDao helloDao;

注入名稱為helloWorldDao 的Bean元件。@Qualifier("XXX") 中的 XX是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。

多個型別一樣的Bean候選者,也可以@Primary進行使用,設定首選的元件,也就是預設優先使用哪一個。

注意:使用@Qualifier 時候,如何設定的指定名稱的Bean不存在,則會丟擲異常,如果防止丟擲異常,可以使用:

@Qualifier("xxxxyyyy")
@Autowired(required = false)
private HelloDao helloDao;

在SpringBoot中也可以使用@[email protected]進行元件注入,將@Autowired加到引數上,其實也可以省略。

@Bean
public Person getPerson(@Autowired Car car){
 return new Person();
}
// @Autowired 其實也可以省略

@Resource

  • Resource註解原始碼
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
    String name() default "";
    // 其他省略
}

從Resource註解原始碼上看,可以使用在下面這些地方:

@Target(ElementType.TYPE) #介面、類、列舉、註解
@Target(ElementType.FIELD) #欄位、列舉的常量
@Target(ElementType.METHOD) #方法

name 指定注入指定名稱的元件。

  • 簡單總結

1、@Resource是JSR250規範的實現,在javax.annotation包下

2、@Resource可以作用TYPE、FIELD、METHOD上

3、@Resource是預設根據屬性名稱進行自動裝配的,如果有多個型別一樣的Bean候選者,則可以通過name進行指定進行注入

  • 簡單使用程式碼
@Component
public class SuperMan {
    @Resource
    private Car car;
}

按照屬性名稱 car 注入容器中的元件。如果容器中BMW還有BYD兩種型別元件。指定加入BMW。如下程式碼:

@Component
public class SuperMan {
    @Resource(name = "BMW")
    private Car car;
}

name 的作用類似 @Qualifier

@Inject

  • Inject註解原始碼
@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}

從Inject註解原始碼上看,可以使用在下面這些地方:

@Target(ElementType.CONSTRUCTOR) #建構函式
@Target(ElementType.METHOD) #方法
@Target(ElementType.FIELD) #欄位、列舉的常量
  • 簡單總結

1、@Inject是JSR330 (Dependency Injection for Java)中的規範,需要匯入javax.inject.Inject jar包 ,才能實現注入

2、@Inject可以作用CONSTRUCTOR、METHOD、FIELD上

3、@Inject是根據型別進行自動裝配的,如果需要按名稱進行裝配,則需要配合@Named;

  • 簡單使用程式碼
@Inject
private Car car;

指定加入BMW元件。

@Inject
@Named("BMW")
private Car car;

@Named 的作用類似 @Qualifier!

總結

1、@Autowired是Spring自帶的,@Resource是JSR250規範實現的,@Inject是JSR330規範實現的

2、@Autowired、@Inject用法基本一樣,不同的是@Inject沒有required屬性

3、@Autowired、@Inject是預設按照型別匹配的,@Resource是按照名稱匹配的

4、@Autowired如果需要按照名稱匹配需要和@Qualifier一起使用,@Inject和@Named一起使用,@Resource則通過name進行指定

如果你還期望原始碼層理解,我給你找了一篇文章 Spring原始碼分析@Autowired、@Resource註解的區別

參考文章

Inversion of Control Containers and the Dependency Injection pattern

https://www.iteye.com/blog/jinnianshilongnian-1413846

https://blog.csdn.net/qq_35634181/article/details/104276056

https://www.cnblogs.com/diandianquanquan/p/11518365.html

更多文章

首先, 從Spring框架的整體架構和組成對整體框架有個認知。

  • Spring基礎 - Spring和Spring框架組成
    • Spring是什麼?它是怎麼誕生的?有哪些主要的元件和核心功能呢? 本文通過這幾個問題幫助你構築Spring和Spring Framework的整體認知。

其次,通過案例引出Spring的核心(IoC和AOP),同時對IoC和AOP進行案例使用分析。

基於Spring框架和IOC,AOP的基礎,為構建上層web應用,需要進一步學習SpringMVC。

  • Spring基礎 - SpringMVC請求流程和案例
    • 前文我們介紹了Spring框架和Spring框架中最為重要的兩個技術點(IOC和AOP),那我們如何更好的構建上層的應用呢(比如web 應用),這便是SpringMVC;Spring MVC是Spring在Spring Container Core和AOP等技術基礎上,遵循上述Web MVC的規範推出的web開發框架,目的是為了簡化Java棧的web開發。 本文主要介紹SpringMVC的請求流程和基礎案例的編寫和執行。

Spring進階 - IoC,AOP以及SpringMVC的原始碼分析

  • Spring進階 - Spring IOC實現原理詳解之IOC體系結構設計
    • 在對IoC有了初步的認知後,我們開始對IOC的實現原理進行深入理解。本文將幫助你站在設計者的角度去看IOC最頂層的結構設計
  • Spring進階 - Spring IOC實現原理詳解之IOC初始化流程
    • 上文,我們看了IOC設計要點和設計結構;緊接著這篇,我們可以看下原始碼的實現了:Spring如何實現將資源配置(以xml配置為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的
  • Spring進階 - Spring IOC實現原理詳解之Bean例項化(生命週期,迴圈依賴等)
    • 上文,我們看了IOC設計要點和設計結構;以及Spring如何實現將資源配置(以xml配置為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的;容器中存放的是Bean的定義即BeanDefinition放到beanDefinitionMap中,本質上是一個 ConcurrentHashMap<String, Object> ;並且BeanDefinition介面中包含了這個類的Class資訊以及是否是單例等。那麼如何從BeanDefinition中例項化Bean物件呢,這是本文主要研究的內容?
  • Spring進階 - Spring AOP實現原理詳解之切面實現
    • 前文,我們分析了Spring IOC的初始化過程和Bean的生命週期等,而Spring AOP也是基於IOC的Bean載入來實現的。本文主要介紹Spring AOP原理解析的切面實現過程(將切面類的所有切面方法根據使用的註解生成對應Advice,並將Advice連同切入點匹配器和切面類等資訊一併封裝到Advisor,為後續交給代理增強實現做準備的過程)。
  • Spring進階 - Spring AOP實現原理詳解之AOP代理
    • 上文我們介紹了Spring AOP原理解析的切面實現過程(將切面類的所有切面方法根據使用的註解生成對應Advice,並將Advice連同切入點匹配器和切面類等資訊一併封裝到Advisor)。本文在此基礎上繼續介紹,代理(cglib代理和JDK代理)的實現過程。
  • Spring進階 - Spring AOP實現原理詳解之Cglib代理實現
    • 我們在前文中已經介紹了SpringAOP的切面實現和建立動態代理的過程,那麼動態代理是如何工作的呢?本文主要介紹Cglib動態代理的案例和SpringAOP實現的原理。
  • Spring進階 - Spring AOP實現原理詳解之JDK代理實現
    • 上文我們學習了SpringAOP Cglib動態代理的實現,本文主要是SpringAOP JDK動態代理的案例和實現部分。
  • Spring進階 - SpringMVC實現原理之DispatcherServlet初始化的過程
    • 前文我們有了IOC的原始碼基礎以及SpringMVC的基礎,我們便可以進一步深入理解SpringMVC主要實現原理,包含DispatcherServlet的初始化過程和DispatcherServlet處理請求的過程的原始碼解析。本文是第一篇:DispatcherServlet的初始化過程的原始碼解析。
  • Spring進階 - SpringMVC實現原理之DispatcherServlet處理請求的過程
    • 前文我們有了IOC的原始碼基礎以及SpringMVC的基礎,我們便可以進一步深入理解SpringMVC主要實現原理,包含DispatcherServlet的初始化過程和DispatcherServlet處理請求的過程的原始碼解析。本文是第二篇:DispatcherServlet處理請求的過程的原始碼解析。