Spring源碼解析-入門Spring的IOC思想

《Spring源碼解析-入門Spring的IOC思想》首發橙寂博客轉發請加此提示html

Spring源碼解析-入門Spring的IOC思想

1.引入

你們在面試的時候,應該都會碰到這麼一個問題。請淺談一下Spring IOC(控制反轉)思想?或者是解釋下什麼是DI(依賴注入)?。本篇會從Spring官方文檔的角度,結合本身的工做經驗 ,給你們講一下本身對Spring的一些理解。java

2.概述

Spring CoreSpring的核心。而Spring的核心講的就是一個IOC思想。SpringIOC容器管理着是一個或者多個Bean。在一個Spring應用啓動,Spring會根據咱們提供的元數據。 作一個自省的過程。當咱們真正用一些Bean時。這時咱們能夠經過註解(@Autowired)的方式或者是主動調用ApplicationContextgetBean()方法根據標識符(Id或者帶包名的類名)從容器中取得咱們想要的 Bean對象。這就是IOC也叫作DIweb

固然在容器中每一個Bean都是有個惟一的Id的,若是咱們不顯示的提供,Spring會根據Java的規範默認按照駝峯命名法命名(首字母小寫)。(例如:postService,postDao)面試

3.配置元數據

若是想Spring去管理Bean那麼你就得告訴Spring你須要管理的Bean。這個過程叫作配置元數據。配置元數據有兩中方式。 每一個BeanSpring中被定義成了BeanDefinition這個對象裏面保存咱們這個Bean的一些基本屬性:spring

  1. 帶包名的類名
  2. 其餘類的引用
  3. 管理bean的鏈接池的的大小,或者鏈接數。
  4. 其餘一些行爲。好比Bean的做用域。
3.1配置Bean的兩種方式
  • 註解

在咱們的平常工做中,咱們會使用websocket

@Commpont

@Service

@Configuration
//表明這是一個配置類
public class QuartzConfig {

    /** * 往容器中初始注入scheduler * @return * @throws SchedulerException */
    @Bean
    public Scheduler scheduler() throws SchedulerException{
        SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
        return schedulerFactoryBean.getScheduler();
    }
}

複製代碼

以上註解都是告訴Spring我須要將帶這些註解的Bean教給Spring容器管理。除了這個具體每一個註解帶的含義是不同的。有想要了解的本身去了解下。session

  • xml配置

dao.xmlapp

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>

複製代碼

以上是一個Dao的配置文件。接下來程序去加載這個文件就行了。socket

//這裏能夠加載多個文件,分隔。
ApplicationContext context = new ClassPathXmlApplicationContext("daos.xml");

// 經過Id得到容器中的對象
JpaAccountDao dao = context.getBean("accountDao");

// 使用對象中的方法
List<String> userList = dao.getUsernameList();

複製代碼

依賴注入(DI)

元數據配置好了,可是一般在咱們平常的使用中咱們的Bean確定是依賴了其餘Bean的。 好比一個Controller中須要注入Service等等。 因此在配置的時候咱們就須要把須要依賴的類(或屬性)注入進去。ide

  • xml配置

構造方式的形式注入Bean

package x.y;

//構造方式的形式注入
public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}

<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>

    <bean id="beanTwo" class="x.y.ThingTwo"/>

    <bean id="beanThree" class="x.y.ThingThree"/>
</beans>

複製代碼

構造方式的形式注入屬性

package examples;

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private int years;

    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

//第一種方式
<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

//第二種方式

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>


複製代碼

Setter方式注入

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setIntegerProperty(int i) {
        this.i = i;
    }
}

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>


複製代碼
  • 註解方式注入

這種方式應該是目前用的最多的一種形式。特別是在SpringBoot項目中,拋棄xml配置的方式,全局使用了註解的方式。 (題外話:SpringBoot中的自動裝配機制其實跟Spring的機制是相通的。Springboot內置了一些自動裝配的配置類。因此只須要改改配置文件。就能自動注入咱們須要類)

當你想要在你的Bean中使用@Autoried @Resource(name = "bean的名字")注入您須要的Bean。 你必定要肯定Bean存在於容器中。(也就是說必定要先提供的元數據)。

下面講幾個在平常開發中常常會用到的例子

@Controller
public class BaseController {
	//系統用戶
	@Autowired
	public SysUserService sysUserService;


  //這個name必定是你配置的名字,若是你沒配那麼默認是首字母小寫
  @Resource(name = "sysPermissionService")
  public SysPermissionService sysPermissionService;
  }

複製代碼

上面兩個例子是在開發常用的。

補充

  • 自動裝配機制

上面講了怎麼往Bean對象中注入屬性以及對象。而且演示了怎麼往Bean中注入另外一個Bean。那麼SpringIOC容器是怎麼作到兩個Bean之間互相協做的呢?這徹底取決於IOC的自動裝配機制。自動裝配能夠經過name,'type','構造函數'等模式來實現這個功能。 默認是經過Type來實現的。也是說好比你想要個String類型的對象它就會給你注入一個String類型的。

  • Bean的做用域

每一個Bean在容器中是有本身的做用域的。一共存在singleton(單例),prototype(原型),request,'session','application','websocket'。 這裏主要講一下'singleton'和'prototype'。我在面試中被問到過這二者的區別,我是這麼回答的:singleton是無狀態的,prototype是有狀態的。而後我舉了個例子,好比有個orderService購物車服務,一個客戶是否是有一個購物車,並且購物車是不能被共享的。也就是每一個人都要建立一個購物車。這就是prototype原型模式。每次獲取Bean都會有一個全新的Bean。因此說prototype有狀態. 而singleton容器中只有一個實例。使用singleton定義的實例在同一個線程中A對象跟B對象都依賴的是同一個實例。IOC中的對象默認是singleto做用域。 有想要了解其餘的參考spring官方文檔Bean的做用域

  • 容器的擴展

IOC內置了一些接口。方便咱們在SpringIOC外部操做Bean對容器進行擴展。好比ApplicationContextAware接口。BeanPostProcessor接口,BeanFactoryPostProcessor接口都內置了回調方法。 這裏給你們講一個實用的工具類就是,獲取ApplicationContext這個容器對象這樣咱們就能操做getBean方法。 下面咱們會經過繼承ApplicationContextAware來實現。

public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

    private static ApplicationContext applicationContext = null;


    /** * 取得存儲在靜態變量中的ApplicationContext. */
    public static ApplicationContext getApplicationContext() {
        assertContextInjected();
        return applicationContext;
    }

    /** * 從靜態變量applicationContext中取得Bean, 自動轉型爲所賦值對象的類型. */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        assertContextInjected();
        return (T) applicationContext.getBean(name);
    }

    /** * 從靜態變量applicationContext中取得Bean, 自動轉型爲所賦值對象的類型. */
    public static <T> T getBean(Class<T> requiredType) {
        assertContextInjected();
        return applicationContext.getBean(requiredType);
    }

    /** * 清除SpringContextHolder中的ApplicationContext爲Null. */
    public static void clearHolder() {
        applicationContext = null;
    }

    /** * 實現ApplicationContextAware接口, 注入Context到靜態變量中. */
    @Override
    public void setApplicationContext(ApplicationContext appContext) {
        applicationContext = appContext;
    }

    /** * 實現DisposableBean接口, 在Context關閉時清理靜態變量. */
    @Override
    public void destroy() throws Exception {
        SpringContextHolder.clearHolder();
    }

    /** * 檢查ApplicationContext不爲空. */
    private static void assertContextInjected() {
        Validate.validState(applicationContext != null, "applicaitonContext屬性未注入, 請在applicationContext.xml中定義SpringContextHolder.");
    }
}

複製代碼

想要了解其它兩個的使用參考spring官方文檔容器擴展

總結

SpringIOC是學習Spring的核心。下面筆者會帶着你們一塊兒去學習Spring Core的源碼。敬請期待。

相關文章
相關標籤/搜索