【筆記】Spring IoC
Spring的核心就是提供了一個IoC容器,它可以管理所有輕量級的JavaBean元件,提供的底層服務包括元件的生命週期管理、配置和組裝服務、AOP支援,以及建立在AOP基礎上的宣告式事務服務等。
核心問題是:
- 誰負責建立元件?
- 誰負責根據依賴關係組裝元件?
- 銷燬時,如何按依賴順序正確銷燬?
在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; - 在
userService
Bean中,通過<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實際上是這個FactoryBean
的getObject()
方法返回的Bean。為了和普通Bean區分,我們通常都以XxxFactoryBean
命名。
Resource 類
Spring提供了一個org.springframework.core.io.Resource
(注意不是javax.annotation.Resource
),它可以像String
、int
一樣使用@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