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

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

引入

我們在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中也可以使用@Bean+@Autowired進行組件注入,將@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處理請求的過程的源碼解析。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章