1. 程式人生 > 其它 >【筆記】Spring IoC

【筆記】Spring IoC

Spring的核心就是提供了一個IoC容器,它可以管理所有輕量級的JavaBean元件,提供的底層服務包括元件的生命週期管理、配置和組裝服務、AOP支援,以及建立在AOP基礎上的宣告式事務服務等。

核心問題是:

  1. 誰負責建立元件?
  2. 誰負責根據依賴關係組裝元件?
  3. 銷燬時,如何按依賴順序正確銷燬?

在IoC模式下,控制權發生了反轉,即從應用程式轉移到了IoC容器,所有元件不再由應用程式自己建立和配置,而是由IoC容器負責,這樣,應用程式只需要直接使用已經建立好並且配置好的元件

IoC又稱為依賴注入(DI:Dependency Injection),它解決了一個最主要的問題:將元件的建立+配置與元件的使用相分離,並且,由IoC容器負責管理元件的生命週期。因為IoC容器要負責例項化所有的元件,因此,有必要告訴容器如何建立元件,以及各元件的依賴關係。

注入方式

  • 屬性
  • 構造器

POM檔案

<?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>org.example</groupId>
    <artifactId>spring</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <java.version>11</java.version>

        <spring.version>5.2.3.RELEASE</spring.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>

</project>

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

    <bean id="userService" class="com.itranswarp.learnjava.service.UserService">
        <property name="mailService" ref="mailService" />
    </bean>

    <bean id="mailService" class="com.itranswarp.learnjava.service.MailService" />
</beans>
  • 每個<bean ...>都有一個id標識,相當於Bean的唯一ID;
  • userServiceBean中,通過<property name="..." ref="..." />注入了另一個Bean;
  • Bean的順序不重要,Spring根據依賴關係會自動正確初始化。

ApplicationContext

  • ClassPathXmlApplicationContext
  • AnnotationConfigApplicationContext

BeanFactory 是 ApplicationContext 的爹,青出於藍

開啟註解掃描

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="org.example.spring"
    />
</beans>

Annotation

  • @Component
  • @ComponentScan
  • @Configuration
  • @Autowried

Scope

Singleton : 在容器初始化時建立Bean ,容器關閉時銷燬Bean

Prototype:每次getBean時,容器都返回一個新的例項

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // @Scope("prototype")

注入List,List<T> T 為爹,可以批量注入兒子們

  • 設計時,存在多個實現類,單獨呼叫時需要指明bean_name

@Order,指定注入list時的訪問順序

非必要的注入,在@Autowired增加一個required = false

建立第三方Bean,單例

@Configuration
@ComponentScan
public class AppConfig {
    // 建立一個Bean:
    @Bean
    ZoneId createZoneId() {
        return ZoneId.of("Z");
    }
}

初始化和銷燬

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

在Bean的初始化和清理方法上標記@PostConstruct@PreDestroy

別名

出現重複的bean 命名,

  • 使用@Bean("name")指定別名
  • 也可以用@Bean+@Qualifier("name")指定別名。

注入時需要指定相對應的別名。注入標記有@Primary的Bean,通常定義為主資料來源,預設注入主資料來源

FactoryBean

當一個Bean實現了FactoryBean介面後,Spring會先例項化這個工廠,然後呼叫getObject()建立真正的Bean。getObjectType()可以指定建立的Bean的型別,因為指定型別不一定與實際型別一致,可以是介面或抽象類。

因此,如果定義了一個FactoryBean,要注意Spring建立的Bean實際上是這個FactoryBeangetObject()方法返回的Bean。為了和普通Bean區分,我們通常都以XxxFactoryBean命名。

Resource 類

Spring提供了一個org.springframework.core.io.Resource(注意不是javax.annotation.Resource),它可以像Stringint一樣使用@Value注入

注入配置

Spring容器提供 @PropertySource來自動讀取配置檔案

Spring容器看到@PropertySource("app.properties")註解後,自動讀取這個配置檔案,然後,我們使用@Value正常注入

@Configuration
@ComponentScan
@PropertySource("app.properties") // 表示讀取classpath的app.properties
public class AppConfig {
    @Value("${app.zone:Z}")
    String zoneId;

    @Bean
    ZoneId createZoneId() {
        return ZoneId.of(zoneId);
    }
}

注意注入的字串語法,它的格式如下:

  • "${app.zone}"表示讀取key為app.zone的value,如果key不存在,啟動將報錯;
  • "${app.zone:Z}"表示讀取key為app.zone的value,但如果key不存在,就使用預設值Z

JavaBean持有所有的配置

@Component
public class SmtpConfig {
    @Value("${smtp.host}")
    private String host;

    @Value("${smtp.port:25}")
    private int port;

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }
}

使用#{} 的語法讀取javabean 配置

@Componentpublic class MailService {    @Value("#{smtpConfig.host}")    private String smtpHost;    @Value("#{smtpConfig.port}")    private int smtpPort;}

條件裝配

根據不同的開發環境來決定所使用的配置

  • @Profile
  • @Conditional