Spring 源碼分析(一)之 Spring IOC 容器基礎(chǔ)介紹

spring.png

Spring 源碼分析(一)之 Spring IOC 容器基礎(chǔ)介紹

Spring IOC 容器在項(xiàng)目中的作用

  • 將對(duì)象的構(gòu)建統(tǒng)一解決
  • 并自動(dòng)維護(hù)對(duì)象的依賴關(guān)系,從而降低實(shí)現(xiàn)成本
  • ...
spring-1.jpg

IOC(Inversion of Control) 控制反轉(zhuǎn)

是面向?qū)ο缶幊讨械囊环N設(shè)計(jì)原則,可以用來減低計(jì)算機(jī)代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡(jiǎn)稱DI),還有一種方式叫“依賴查找”(Dependency Lookup)。

系統(tǒng)中通過引入實(shí)現(xiàn)了IoC模式的IoC容器,即可由IoC容器來管理對(duì)象的生命周期、依賴關(guān)系等,從而使得應(yīng)用程序的配置和依賴性規(guī)范與實(shí)際的應(yīng)用程序代碼分離。其中一個(gè)特點(diǎn)就是通過文本的配置文件進(jìn)行應(yīng)用程序組件間相互關(guān)系的配置,而不用重新修改并編譯具體的代碼。


在我們開發(fā)中,經(jīng)常會(huì)A類引用B類、C類、D類...,如果全部交給我們自己來維護(hù)這些類的依賴關(guān)系,工作量比較大而且很容易出錯(cuò),而IOC容器的出現(xiàn),正式為解決這一問題,其可以將對(duì)象的構(gòu)建方法進(jìn)行統(tǒng)一,并自動(dòng)維護(hù)對(duì)象的依賴關(guān)系,從而降低系統(tǒng)的實(shí)現(xiàn)成本。實(shí)現(xiàn)方式:提前對(duì)目標(biāo)對(duì)象基于XML進(jìn)行聲明,或使用注解進(jìn)行聲明。

具體demo如下。

pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.demo.spring</groupId>
    <artifactId>spring-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.version>4.3.8.RELEASE</spring.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
    </dependencies>
</project>

實(shí)現(xiàn)實(shí)體Bean的構(gòu)建

  • 基于ClassName的構(gòu)建
  • 基于構(gòu)造方法的構(gòu)建
  • 靜態(tài)工廠的方法創(chuàng)建
  • FactoryBean創(chuàng)建

spring.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">
        <!-- 默認(rèn)構(gòu)造函數(shù)構(gòu)建 基于反射構(gòu)建-->
        <bean class="com.demo.spring.HelloSpring"></bean>
    
        <!-- 指定構(gòu)造函數(shù)構(gòu)建 基于反射構(gòu)建-->
        <bean class="com.demo.spring.HelloSpring">
            <constructor-arg index="0" value="zyy"/>
            <constructor-arg name="age" value="18"/>
        </bean>
    
        <!-- 靜態(tài)工廠方法構(gòu)建-->
        <bean id="helloSpring" class="com.demo.spring.HelloSpring" factory-method="buid">
            <constructor-arg name="type" value="A"></constructor-arg>
        </bean>
    
        <!-- FactoryBean創(chuàng)建 自定義創(chuàng)建bean-->
        <bean id="driver" class="com.demo.spring.DriverFactoryBean" >
           <property name="jdbcUrl" value="jdbc:mysql://192.168.5.104:3306"></property>
        </bean>
    </beans>

HelloSpring,java

    package com.demo.spring;

    /**
     * com.demo.spring
     *
     * @author Zyy
     * @date 2019/2/12 15:50
     */
    public class HelloSpring {
        private String name;
        private int age;
    
        public HelloSpring() {
        }
    
        public HelloSpring(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        //靜態(tài)工廠以及AB測(cè)試
        public static HelloSpring buid(String type) {
            if ("A".equals(type)) {
                return new HelloSpring("zyy",18);
            } else if ("B".equals(type)) {
                return new HelloSpring("xxx",20);
            } else {
               throw new IllegalArgumentException("argument must A or B");
            }
    
        }
    }

DriverFactoryBean.java

package com.demo.spring;

import org.springframework.beans.factory.FactoryBean;

import java.sql.Driver;
import java.sql.DriverManager;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/12 16:13
 */
public class DriverFactoryBean implements FactoryBean {
    private String jdbcUrl;

    public Object getObject() throws Exception {
        return DriverManager.getDriver(jdbcUrl);
    }

    public Class<?> getObjectType() {
        return Driver.class;
    }

    public boolean isSingleton() {
        return true;
    }

    public String getJdbcUrl() {
        return jdbcUrl;
    }

    public void setJdbcUrl(String jdbcUrl) {
        this.jdbcUrl = jdbcUrl;
    }
}
bean的基本特性
  • 作用范圍
  • 生命周期
  • 裝載機(jī)制
作用范圍

bean很多是無狀態(tài)的,對(duì)于無狀態(tài)的對(duì)象可以采取單例,而有狀態(tài)的對(duì)象則必須用是多例模式進(jìn)行創(chuàng)建,通過scope可以進(jìn)行設(shè)置

scope="prototype"
scope="singleton"

例如

<bean id="di" class="com.demo.spring.DI" scope="singleton"/>

如果一個(gè)bean設(shè)置成prototype 則可以通過可以BeanFactoryAware 獲取BeanFactory對(duì)象,即可每次獲取都是新對(duì)象。

生命周期

Bean對(duì)象的創(chuàng)建、初始化、銷毀即是Bean的生命周期。通過init-method、destroy-method屬性可以分別指定構(gòu)建方法和初始方法。

<bean id="di" class="com.demo.spring.DI" scope="singleton" init-method="init" destroy-method="destroy"/>

或者可以讓Bean去實(shí)現(xiàn)initializingBean.afterPropertiesSet()、DisposableBean.destroy()方法。分別對(duì)應(yīng)初始方法和銷毀方法。

加載機(jī)制

指定Bean在何時(shí)進(jìn)行加載。設(shè)置lazy-init。
當(dāng)為true:懶加載,用到的時(shí)候才創(chuàng)建對(duì)象
當(dāng)為false:容器啟動(dòng)時(shí),就創(chuàng)建對(duì)象

<bean id="di" lazy-init="true" class="com.demo.spring.DI" scope="singleton" init-method="init" destroy-method="destroy"/>

什么時(shí)候使用懶加載?

懶加載會(huì)使容器啟動(dòng)更快,而非懶加載可以讓容器啟動(dòng)時(shí)更快的發(fā)現(xiàn)程序中的錯(cuò)誤,一般我們選擇非懶加載。

spring依賴注入

一個(gè)bean依賴其他的bean由springIOC容器統(tǒng)一的管理進(jìn)行注入,無需自己外部傳入,內(nèi)部創(chuàng)建進(jìn)行處理。

依賴注入的幾種方式

  • set方法注入
  • 構(gòu)造方法注入
  • 自動(dòng)注入(byName,byType)
  • 依賴方法注入(lookup-method)

自動(dòng)注入(byName,byType)

DI.java

 package com.demo.spring;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/12 16:35
 */
public class DI {
}

HelloSpring.java

package com.demo.spring;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/12 15:50
 */
public class HelloSpring {
    private String name;
    private int age;
    private DI di;

    public HelloSpring() {
    }

    public HelloSpring(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public DI getDi() {
        return di;
    }

    public void setDi(DI di) {
        this.di = di;
    }

    //靜態(tài)工廠以及AB測(cè)試
    public static HelloSpring buid(String type) {
        if ("A".equals(type)) {
            return new HelloSpring("zyy",18);
        } else if ("B".equals(type)) {
            return new HelloSpring("xxx",20);
        } else {
           throw new IllegalArgumentException("argument must A or B");
        }

    }
}

spring.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">
    <!-- 默認(rèn)構(gòu)造函數(shù)構(gòu)建 基于反射構(gòu)建-->
    <bean class="com.demo.spring.HelloSpring"></bean>

    <!-- 指定構(gòu)造函數(shù)構(gòu)建 基于反射構(gòu)建-->
    <bean id="helloByName" class="com.demo.spring.HelloSpring" autowire="byName">
        <constructor-arg index="0" value="zyy"/>
        <constructor-arg name="age" value="18"/>
    </bean>
    <!-- byName-->
    <bean id="di" class="com.demo.spring.DI"/>

    <!-- byType時(shí),如果bean相同 可以指定primary為true 來選擇要注入哪一個(gè)-->
    <bean class="com.demo.spring.DI" primary="true"/>
    <bean class="com.demo.spring.DI"/>

    <!-- 靜態(tài)工廠方法構(gòu)建-->
    <bean id="helloSpring" class="com.demo.spring.HelloSpring" factory-method="buid">
        <constructor-arg name="type" value="A"></constructor-arg>
    </bean>

    <!-- FactoryBean創(chuàng)建 自定義創(chuàng)建bean-->
    <bean id="driver" class="com.demo.spring.DriverFactoryBean" >
       <property name="jdbcUrl" value="jdbc:mysql://192.168.5.104:3306"></property>
    </bean>
</beans>
依賴方法注入(lookup-method)

一個(gè)單例的bean依賴了一個(gè)多例的bean,為了每次獲取多例的bean是不同的

DI.java

package com.demo.spring;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/12 16:35
 */
public class DI {

    public void inject() {
        System.out.println(" inject ");
    }

    public void init() {

    }

    public void destroy () {

    }
}

LookUpTest.java

package com.demo.spring;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/12 18:20
 */
public abstract class LookUpTest {

    public void create() {
        getDi().inject();
    }

    //這個(gè)抽象方法由spring采用cglib(動(dòng)態(tài)字節(jié)碼)進(jìn)行實(shí)現(xiàn)
    public abstract DI getDi();
}

spring.xml

<bean class="com.demo.spring.LookUpTest" >
    <lookup-method name="getDi"/>
</bean>

該操作的原理是基于動(dòng)態(tài)代理技術(shù),重新生成一個(gè)類繼承目標(biāo)類(cglib),然后重寫抽象方法達(dá)到注入目的。

對(duì)于單例bean依賴多例bean這種情也可以通過實(shí)現(xiàn)ApplicationContextAware、BeanFactoryAware接口獲取BeanFactory實(shí)例,從而調(diào)用geBean()方法獲取新的實(shí)例,推薦使用這種方法,比lookup-method邏輯清晰。

DI.java

package com.demo.spring;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/12 16:35
 */
public class DI {

    public void inject() {
        System.out.println(" inject ");
    }

    public void init() {

    }

    public void destroy () {

    }
}

BeanFactoryAwareTest.java

package com.demo.spring;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/12 22:24
 */
public class BeanFactoryAwareTest implements BeanFactoryAware {

    private BeanFactory beanFactory;

    public void create() {
        beanFactory.getBean(DI.class).inject();
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}

測(cè)試

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * PACKAGE_NAME
 *
 * @author Zyy
 * @date 2019/2/12 16:02
 */
public class SpringIOCTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        //context.getBean("helloSpring");
        //context.getBean("driver");
        context.getBean("helloByName");
    }
}

如有問題,歡迎留言:)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容