Spring系列2:Spring容器基本概念和使用

本文內容

  1. 簡單回顧IoC和DI概念
  2. Spring容器的概念
  3. 的xml配置和初始化
  4. 容器的基本使用
  5. bean的定義和初始化配置

簡單理解IoC和DI概念

什麼是IoC控制反轉?

通俗地但不嚴謹地講,以前傳統方式都是應用程式需要一個對象,直接通過new的方式來生成,該對象的管理也是由當前程式自己控制。現在有一個容器,負責將應用程式需要的所有對象都new好了,對象都統一由這個容器管理,應用程式需要對象的時候直接找容器要,應用程式說我不關係對象是怎麼來的反正你給我就行。這樣和以前的方式不一樣了,以前是應用程式自己創建和管理,現在交給容器統一創建管理了,控制權發生反轉了,這簡單理解為IoC。

什麼是DI依賴注入?

通俗地但不嚴謹地講,應用程式需要的對象A依賴於B,由容器直接自動將B依賴給到對象A,可以理解為自動將依賴B注入到A 中了。

Spring官方的對於這個2個概念的說明,比較繞,這裡也附上。

IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.

IoC也稱為依賴項注入(DI)。在這個過程中,對象只能通過構造函數參數、工廠方法的參數或在對象實例被構造或從工廠方法返回後在對象實例上設置的屬性來定義它們的依賴關係(即它們所處理的其他對象)。容器然後在創建bean時注入這些依賴項。這個過程本質上是bean本身的逆過程(因此得名「控制反轉」),它通過直接構造類來控制依賴項的實例化或位置

Spring容器的概念和使用

Spring IoC容器負責實例化、配置和組裝bean。容器通過讀取配置元數據來獲取關於實例化、配置和組裝哪些對象的指令。配置元數據以XML、Java注釋或Java程式碼表示。

Talk is cheap. Show me the code

屁話少說,放碼過來

下面將通過一個案例來快速演示Spring容器的簡單使用。

環境準備

IDE: IDEA

Maven: 3.5.6

JDK: java8

創建Maven工程並Spring引入依賴

pom文件如下

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

    <groupId>org.example</groupId>
    <artifactId>spring-ioc-quickstart</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring.verison>5.2.19.RELEASE</spring.verison>
    </properties>

    <dependencies>
        <!--Spring上下文的依賴-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.verison}</version>
        </dependency>
    </dependencies>

    <build>
        <!--配置文件相關-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>

創建2個簡單類A、B

A依賴B如下

public class A {
    private B b;

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}

B如下

public class B {
}

創建Spring的XML配置文件

resources目錄創建spring.xml配置文件,並配置bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd">

    <!--A的定義資訊-->
    <bean id="a" class="com.crab.spring.A">
        <!--注入B對象依賴-->
        <property name="b" ref="b"/>
    </bean>

    <!--B的定義資訊-->
    <bean id="b" class="com.crab.spring.B"></bean>
</beans>

創建一個測試類

public class MainTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        A a = context.getBean("a", A.class);
        B b = context.getBean("b", B.class);
        System.out.println("A對象:" + a);
        System.out.println("A對象中的B依賴: " + a.getB() );
        System.out.println("B對象:" + b);
    }
}

最終文件目錄結構

image-20220112120030437

運行結果

對象AB都統一由ClassPathXmlApplicationContext容器管理,並在A需要B依賴的時候容器自動注入,應用程式需要的時候直接從容器拿介面如context.getBean(),。結合著裡面下IoC和DI的概念。以上就是Spring容器的簡單使用。

A對象:com.crab.spring.A@2d928643
A對象中的B依賴: com.crab.spring.B@5025a98f
B對象:com.crab.spring.B@5025a98f

Spring 容器對象

BeanFactory 介面

BeanFactory 介面提供了一種高級配置機制,能夠管理任何類型的對象,是Spring IoC容器根介面。主要的定義方法如下。

package org.springframework.beans.factory;

public interface BeanFactory {
    // 指定名稱獲取bean
    Object getBean(String name) throws BeansException;
    // 指定名稱和類型獲取bean
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	// 指定類型獲取bean
	<T> T getBean(Class<T> requiredType) throws BeansException;
	// 容器中是否存在bean
	boolean containsBean(String name);
    // 是否是單例
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    //
}

非常建議閱讀BeanFactory 的源碼上的注釋說明,非常的詳盡,常見的面試:請描述下Spring的生命周期?注釋上就有非常官方的完整說明

ApplicationContext介面

ApplicationContext介面是BeanFactory的子介面,在其基礎上提供更多企業級的功能。負責實例化、配置和組裝bean,支援xml配置文件、java註解、Java-based等方式進行bean的配置。常使用的實現類有: ClassPathXmlApplicationContextFileSystemXmlApplicationContextAnnotationConfigApplicationContext等,後面詳細講解和使用,請保持閱讀的熱情。

bean定義對象

在 Spring 中,構成應用程式並由 Spring IoC 容器管理的對象稱為 bean。這些 bean 是使用提供給容器的配置元數據創建的,如以 XML 定義的形式。

<!--A的定義資訊-->
<bean id="a" class="com.crab.spring.A">
    <!--注入B對象依賴-->
    <property name="b" ref="b"/>
</bean>

xml中<bean />配置格式和支援元素如下:

<bean id="myChild" class="com.crab.spring.demo02.ChildBean"
      init-method="init"
      destroy-method="destroy"
      name="childBean,aliasName"
      parent="myParent"
      scope="singleton"
      primary="true"
      depends-on="myParent"
      autowire="byType"
      autowire-candidate="true"
      factory-bean="myFactory"
      factory-method="getObj"
      abstract="false"
      ></bean>
元素 說明
id 唯一ID
class 對應的類,全路徑
name 支援名稱,可以逗號/分號/空格來分隔多個,多個名稱可用作別名
init-method 自定義的初始化方法
destroy-method 自定義的bean被銷毀的方法
parent 指定父類引用
scope 指定作用域,如單例和原型,後面詳細講
primary 是否是主要的,用於依賴注入時候容器內有多個同類型的bean時做選擇
depends-on 依賴於容器中的另外一個bean的引用
autowire 自動注入依賴,指定通過名稱或是類型
autowire-candidate 標記自動依賴注入時候選
factory-bean 指定創建該bean的工廠
factory-method 指定創建的工廠方法
abstract 是否是抽象
lazy-init 延遲載入

通常情況下,如果不顯式指定name ,Spring默認生成名稱的規則是:將類名稱轉成小駝峰式來命名bean。如AccountManager 生成accountManager

在Spring容器內,bean的定義資訊最終是通過介面BeanDefinition及其實現類體現。BeanDefinition介面和抽象類AbstractBeanDefinition`,主要定義如下

package org.springframework.beans.factory.config;

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	void setParentName(@Nullable String parentName);
	void setBeanClassName(@Nullable String beanClassName);
	void setScope(@Nullable String scope);
	void setLazyInit(boolean lazyInit);
	void setDependsOn(@Nullable String... dependsOn);
	void setAutowireCandidate(boolean autowireCandidate);
	void setPrimary(boolean primary);
	void setFactoryBeanName(@Nullable String factoryBeanName);
	void setFactoryMethodName(@Nullable String factoryMethodName);
	void setInitMethodName(@Nullable String initMethodName);
	void setDestroyMethodName(@Nullable String destroyMethodName);
    // 省略
}

package org.springframework.beans.factory.support;

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Cloneable {
	private volatile Object beanClass;
	private String scope = SCOPE_DEFAULT;
	private boolean abstractFlag = false;
	private Boolean lazyInit;
	private int autowireMode = AUTOWIRE_NO;
	private String[] dependsOn;
	private boolean autowireCandidate = true;
	private boolean primary = false;
	private String factoryBeanName;
	private String factoryMethodName;
	private String initMethodName;
	private String destroyMethodName;
}

總結

本文主要講解了Ioc的概念,演示Spring Ioc容器的快速使用,並詳細說明bean定義元素。下一篇將講bean的依賴注入。

本文對應的源碼:

//github.com/kongxubihai/pdf-spring-series/tree/main/spring-ioc-quickstart

知識分享,轉載請註明出處。學無先後,達者為先!

Tags: