天天看點

第3章 全注解下的Spring IoC全注解下的Spring IoC

全注解下的Spring IoC

1.什麼是Bean?

在Spring中把每一個需要管理的對象稱為Spring Bean(簡稱Bean)

2.什麼是IoC容器?

Spring IoC容器是一個管理Bean的容器,它要求所有的IoC容器都需要實作接口BeanFactory。

下面是BeanFactory接口源碼:

package org.springframework.beans.factory;

public interface BeanFactory {

    /**
     * 用來引用一個執行個體,或把它和工廠産生的Bean區分開,就是說,如果一個FactoryBean的名字為a,那麼,&a會得到那個Factory
     */
    String FACTORY_BEAN_PREFIX = "&";

    /*
     * 四個不同形式的getBean方法,擷取執行個體
     */
    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

    boolean containsBean(String name); // 是否存在

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否為單執行個體

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否為原型(多執行個體)

    boolean isTypeMatch(String name, Class<?> targetType)
            throws NoSuchBeanDefinitionException;// 名稱、類型是否比對

    Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 擷取類型

    String[] getAliases(String name);// 根據執行個體的名字擷取執行個體的别名

}
           

預設情況下,Bean都是以單例形式存在,即使用getBean方法傳回的都是同一對象。

3.如何從IoC容器中取出Bean

通過上面的源碼可以看到,getBean方法有按類型(type)擷取Bean的,也有按名稱(name)擷取Bean的,這對于後面介紹的Spring依賴注入很重要。

4.ApplicationContext和BeanFactory的關系

由于BeanFactory的功能不夠強大,Spring在BeanFactory的基礎上,設計了一個ApplicationContext接口,是BeanFactory的子接口,他們的關系如下(圖檔來源于網絡):

第3章 全注解下的Spring IoC全注解下的Spring IoC

在Spring Boot中,主要通過注解方式裝配Bean到Spring IoC容器中(也支援XML).

5.裝配Bean注入IoC容器(通過兩個執行個體了解)

(1)@Bean

首先定義一個Java簡單對象,檔案User.java,代碼如下:

package com.springboot.chapter3.pojo;

import java.util.LongSummaryStatistics;

/**
 * Created by lizeyang on 2019/5/7.
 */
public class User {
    private Long id;
    private String userName;
    private String note;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}
           

然後再定義一個Java配置檔案AppConfig.java,代碼如下:

package com.springboot.chapter3.config;

import com.springboot.chapter3.pojo.User;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by lizeyang on 2019/5/7.
 */
@Configuration
public class AppConfig {
    @Bean(name="user")
    public User initUser(){
        User user = new User();
        user.setId(1L);
        user.setUserName("user_name_1");
        user.setNote("note_1");
        return user;
    }
}
           

其中@Configuration代表這是一個Java配置檔案,Spring容器會根據它來生成IoC容器裝配Bean。@Bean代表将initUser方法傳回的POJO傳回到IoC容器中,屬性name定義這個Bean的名稱,若不配置,方法名即為Bean名稱。

最後,使用AnnotationConfigApplicationContext建構IoC容器,代碼如下:

package com.springboot.chapter3.config;

import com.springboot.chapter3.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * Created by lizeyang on 2019/5/7.
 */
public class IoCTest {
    public static void main(String[] args){
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        User user = ctx.getBean(User.class);
        System.out.println(user.getId());
    }
}
           

運作IoCTest結果如下:

第3章 全注解下的Spring IoC全注解下的Spring IoC

顯然,配置在配置檔案中名稱為user的Bean已經裝配到IoC容器中,并且可以通過getBean方法擷取對應的Bean,并将Bean的屬性輸出出來。

(2)@ComponentScan

除了@Bean,還可以使用@Component和@ComponentScan,@Component是标明哪個類被掃描進入Spring IoC容器,@ComponentScan是标明采用何種政策掃描裝配Bean。

首先,在包com.springboot.chapter3.config内建立UserCpoy.java檔案,代碼如下:

package com.springboot.chapter3.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * Created by lizeyang on 2019/5/7.
 */
@Component("UserCopy")
public class UserCopy {
    @Value("1")
    private Long id;

    @Value("user_name_1")
    private String userName;

    @Value("note_1")
    private String note;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}
           

修改配置檔案為:AppConfigCopy,代碼為:

package com.springboot.chapter3.config;

import com.springboot.chapter3.pojo.User;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * Created by lizeyang on 2019/5/7.
 */
@Configuration
@ComponentScan
public class AppConfigCopy {
//    public User initUser(){
//        User user = new User();
//        user.setId(1L);
//        user.setUserName("user_name_1");
//        user.setNote("note_1");
//        return user;
//    }
}
           

運作IoCTest,結果與上面相同。

6.什麼是依賴注入?

依賴注入就是Bean之間的依賴,最常用的是@Autoeired注解,它會根據屬性的類型(type)找到對應的Bean進行注入(還記得BeanFactory裡面getBean方法的兩種方式嗎?)

另外。可以通過@Primary修改多個不同類型的Bean的優先權,或者将@Autowired和@Qualifier結合使用,通過類型和名稱一起找到Bean。

7.Spring IoC初始化和銷毀Bean過程——Bean生命周期

分為Bean定義、Bean初始化、Bean生存期和Bean的銷毀4個部分。

下圖為Spring Bean的初始化流程:

第3章 全注解下的Spring IoC全注解下的Spring IoC

下面是Spring Bean的生命周期:

第3章 全注解下的Spring IoC全注解下的Spring IoC

8.屬性檔案使用——application.properties

在Spring Boot中,我們一般先在Maven配置檔案中加載依賴,Spring Boot将建立讀取屬性檔案的上下文。如:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
           

有了依賴,就可以直接使用application.properties檔案,通過其機制讀取到上下文中,之後便可引用它。如:

spring.datasource.url=jdbc:mysql://localhost:3306/toutiao?useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=nowcoder
mybatis.config-location=classpath:mybatis-config.xml
#logging.level.root=DEBUG
           

引用有兩種方法,一種是Spring表達式,如通過@Value注解,使用${...}占位符讀取配置在屬性檔案中的内容,如:

@Component
public class DataBaseProperties{
    @Value("${database.drivename}")
    private String driveName = null;
}
           

另一種方法就是使用@ConfigurationProperties注解,如:

@Component
@ConfigurationProperties("database")

public class DataBaseProperties{
    ......
}
           

9.單例(isSingleton)和原型(isPrototype)的差別

我們通過一段代碼來進行比較兩者差別:

@Component
//@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ScopeBean{
    ...
}

//測試作用域
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
ScopeBean scopeBean1 = ctx.getBean(ScopeBean.class);
ScopeBean scopeBean2 = ctx.getBean(ScopeBean.class);
System.out.println(scopeBean1 == scopeBean2);
           

在第二行代碼注釋之後,測試結果為true,說明scopeBean1和scopeBean2變量都指向同一個執行個體,即為單例。

若将注釋消去,則設定Bean的作用域為prototype,讓IoC容器每次擷取Bean時,都建立一個Bean的執行個體傳回給調用者。

10.XML配置Bean

盡管Sping Boot建議使用注解和掃描配置Bean,但也不拒絕XML配置Bean.

通過一個例子來了解XML配置Bean:

先建立一個松鼠實作類,代碼如下:

package com.springboot.other.pojo;

import com.springboot.chapter3.pojo.definition.Animal;


public class Squirrel implements Animal {

	@Override
	public void use() {
		System.out.println("松鼠可以采集松果");
	}

}
           

這個檔案所在的包不在@ComponentScan定義的掃描包com.springboot.chapter3.*之内,也沒有标注@Component,是以不會被掃描機制所裝配,我們使用XML檔案來裝配,代碼如下:

<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">
       <bean id="squirrel" class="com.springboot.other.pojo.Squirrel"/>
</beans>
           

最後,裝配XML定義的Bean,代碼如下:

@Configuration
@ComponentScan(basePackages = "com.springboot.chapter3.*")
@ImportResource(value = {"classpath:spring-other.xml"})
public class AppConfig {
    ......
}
           

這樣就可以引入對應的XML,進而将XML定義的Bean裝配到IoC容器中。

11.使用Spring EL(了解即可)

Spring EL是一種表達式語言,通過Spring EL可以擁有更強大的運算規則來裝配Bean,最常用的是讀取屬性檔案的值,如:

@Value("#{3.14}")
private float pi;

@Value("#{1+2}")
private int run;
           

總結

Spring Boot關于IoC的部分基本就是這些,本節代碼已上傳github:

https://github.com/lizeyang18/SpringBoot-2.x/tree/master/Chapter3

學習永不止步,繼續加油~

繼續閱讀