第一章:Spring Framework總覽(Overview)
Spring Framework 版本 | Java 標准版 | Java 企業版 |
---|---|---|
1.x | 1.3+ | J2EE 1.3 + |
2.x | 1.4.2+ | J2EE 1.3 + |
3.x | 5+ | J2EE 1.4 和 Java EE 5 |
4.x | 6+ | Java EE 6 和 7 |
5.x | 8+ | Java EE 7 |
Spring 編程模型:
-
面向對象編程
-
契約接口:Aware、BeanPostProcessor ...
-
設計模式:觀察者模式、組合模式、模板模式 ...
-
對象繼承:Abstract* 類
-
-
面向切面編程
- 動態代理:JdkDynamicAopProxy
- 字節碼提升:ASM、CGLib、AspectJ...
-
面向元編程
- 注解:模式注解(@Component、@Service、@Respository ...)
- 配置:Environment 抽象、PropertySources、BeanDefinition ...
- 泛型:GenericTypeResolver、ResolvableType ...
-
函數驅動
- 函數接口:ApplicationEventPublisher
- Reactive:Spring WebFlux
-
模塊驅動
- Maven Artifacts
OSGI Bundles- Java 9 Automatic Modules
- Spring @Enable*
第二章:重新認識 IoC
IoC 容器的職責:
- 通用職責
- 依賴處理
- 依賴查找
- 依賴注入
- 生命周期管理
- 容器
- 托管的資源(Java Beans 或其他資源)
- 配置
- 容器
- 外部化配置
- 托管的資源(Java Beans 或其他資源)
- 依賴處理
Spring 作為 IoC 容器的優勢:
- 典型的 IoC 管理,依賴查找和依賴注入
- AOP 抽象
- 事務抽象
- 事件機制
- SPI 擴展
- 強大的第三方整合
- 易測試性
- 更好的面向對象
第三章:Spring IoC 容器概述
Spring IoC 依賴查找:
- 根據 Bean 名稱查找
- 實時查找
- 延遲查找
- 根據 Bean 類型查找
- 單個 Bean 對象
- 集合 Bean 對象
- 根據 Bean 名稱 + 類型查找
- 根據 Java 注解查找
- 單個 Bean 對象
- 集合 Bean 對象
Spring IoC 依賴注入:
- 根據 Bean 名稱注入
- 根據 Bean 類型注入
- 單個 Bean 對象
- 集合 Bean 對象
- 注入容器內建 Bean 對象
- 注入非 Bean 對象
- 注入類型
- 實時注入
- 延遲注入
延遲查找和延遲注入,使用到接口 org.springframework.beans.factory.ObjectProvider
和 org.springframework.beans.factory.ObjectFactory
,ObjectProvider
繼承 ObjectFactory
BeanFactory
和 ApplicationContext
誰才是 Spring IoC 容器?
BeanFactory
是 Spring 底層 IoC 容器ApplicationContext
是具備應用特性的BeanFactory
超集BeanFactory
是基本的 IoC 容器,ApplicationContext
實現BeanFactory
接口,並在內部使用ConfigurableListableBeanFactory
實現接口方法。
ApplicationContext 除了 IoC 容器角色,還有提供:
- 面向切面(AOP)
- 配置元信息(Configuration Metadata)
- 資源管理(Resources)
- 事件(Events)
- 國際化(i18n)
- 注解(Annotations)
- Environment 抽象(Environment Abstraction)
第四章:Spring Bean 基礎
什么是 BeanDefinition
?
org.springframework.beans.factory.config.BeanDefinition
BeanDefinition
是 Spring Framework 中定義 Bean 的配置元信息接口,包含:- Bean 的類名
- Bean 行為配置元素,如作用域、自動綁定的模式,生命周期回調等
- 其他 Bean 引用,又可稱作合作者(collaborators)或者依賴(dependencies)
- 配置設置,比如 Bean 屬性(Properties)
- BeanDefinition 構建
- 通過
BeanDefinitionBuilder
- 通過
AbstractBeanDefinition
以及派生類
- 通過
Bean 名稱生成器:org.springframework.beans.factory.support.BeanNameGenerator
注冊 Spring Bean:
- BeanDefinition 注冊
- XML 配置元信息
<bean name="..." ... />
- Java 注解配置元信息
@Bean
@Component
@Import
- Java API 配置元信息
- 命名方式:
BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
- 非命名方式:
BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,Be
anDefinitionRegistry)
- 配置類方式:
AnnotatedBeanDefinitionReader#register(Class...)
- 命名方式:
- XML 配置元信息
- 外部單例對象注冊
- Java API 配置元信息
SingletonBeanRegistry#registerSingleton
- Java API 配置元信息
Bean 實例化(Instantiation)
- 常規方式
- 通過構造器(配置元信息:XML、Java 注解和 Java API )
- 通過靜態工廠方法(配置元信息:XML 和 Java API )
- 通過 Bean 工廠方法(配置元信息:XML和 Java API )
- 通過
FactoryBean
(配置元信息:XML、Java 注解和 Java API )
- 特殊方式
- 通過
ServiceLoaderFactoryBean
(配置元信息:XML、Java 注解和 Java API ) - 通過
AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
- 通過
BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
- 通過
Bean 初始化(Initialization),同時存在時,順序從上到下:
@PostConstruct
標注方法- 實現
InitializingBean
接口的afterPropertiesSet()
方法 - 自定義初始化方法(
BeanDefinition
)- XML 配置:
<bean init-method=”init” ... />
- Java 注解:
@Bean(initMethod=”init”)
- Java API:
AbstractBeanDefinition#setInitMethodName(String)
- XML 配置:
Bean 延遲初始化(Lazy Initialization)
- XML 配置:
<bean lazy-init=”true” ... />
- Java 注解:
@Lazy(true)
Bean 銷毀(Destroy),同時存在時,順序從上到下:
@PreDestroy
標注方法- 實現
DisposableBean
接口的destroy()
方法 - 自定義銷毀方法
- XML 配置:
<bean destroy=”destroy” ... />
- Java 注解:
@Bean(destroy=”destroy”)
- Java API:
AbstractBeanDefinition#setDestroyMethodName(String)
- XML 配置:
第五章:Spring IoC 依賴查找
單一類型依賴查找接口 - BeanFactory
- 根據 Bean 名稱查找
getBean(String)
- Spring 2.5 覆蓋默認參數:
getBean(String,Object...)
- 根據 Bean 類型查找
- Bean 實時查找
- Spring 3.0 :
getBean(Class)
- Spring 4.1 覆蓋默認參數:
getBean(Class,Object...)
- Spring 3.0 :
- Spring 5.1 Bean 延遲查找
getBeanProvider(Class)
getBeanProvider(ResolvableType)
- Bean 實時查找
- 根據 Bean 名稱 + 類型查找:
getBean(String,Class)
集合類型依賴查找接口 - ListableBeanFactory
- 根據 Bean 類型查找
- 獲取同類型 Bean 名稱列表
getBeanNamesForType(Class)
- Spring 4.2
getBeanNamesForType(ResolvableType)
- 獲取同類型 Bean 實例列表
getBeansOfType(Class)
以及重載方法
- 獲取同類型 Bean 名稱列表
- 通過注解類型查找
- Spring 3.0 獲取標注類型 Bean 名稱列表
getBeanNamesForAnnotation(Class<? extends Annotation>)
- Spring 3.0 獲取標注類型 Bean 實例列表
getBeansWithAnnotation(Class<? extends Annotation>)
- Spring 3.0 獲取指定名稱 + 標注類型 Bean 實例
findAnnotationOnBean(String,Class<? extends Annotation>)
- Spring 3.0 獲取標注類型 Bean 名稱列表
層次性依賴查找接口 - HierarchicalBeanFactory
- 雙親 BeanFactory:
getParentBeanFactory()
- 層次性查找
- 根據 Bean 名稱查找
- 基於
containsLocalBean
方法實現
- 基於
- 根據 Bean 類型查找實例列表
- 單一類型:
BeanFactoryUtils#beanOfType
- 集合類型:
BeanFactoryUtils#beansOfTypeIncludingAncestors
- 單一類型:
- 根據 Java 注解查找名稱列表
BeanFactoryUtils#beanNamesForTypeIncludingAncestors
- 根據 Bean 名稱查找
Bean 延遲依賴查找接口
org.springframework.beans.factory.ObjectFactory
org.springframework.beans.factory.ObjectProvider
- Spring 5 對 Java 8 特性擴展
- 函數式接口
getIfAvailable(Supplier)
ifAvailable(Consumer)
- Stream 擴展 -
stream()
- 函數式接口
- Spring 5 對 Java 8 特性擴展
依賴查找安全性對比:
依賴查找類型 | 代表實現 | 是否安全 |
---|---|---|
單一類型查找 | BeanFactory#getBean |
否 |
ObjectFactory#getObject |
否 | |
ObjectProvider#getIfAvailable |
是 | |
集合類型查找 | ListableBeanFactory#getBeansOfType |
是 |
ObjectProvider#stream |
是 |
注意:層次性依賴查找的安全性取決於其擴展的單一或集合類型的 BeanFactory 接口
AbstractApplicationContext 內建可查找的依賴
Bean 名稱 | Bean 實例 | 使用場景 |
---|---|---|
environment | Environment 對象 | 外部化配置以及 Profiles |
systemProperties | java.util.Properties 對象 | Java 系統屬性 |
systemEnvironment | java.util.Map 對象 | 操作系統環境變量 |
messageSource | MessageSource 對象 | 國際化文案 |
lifecycleProcessor | LifecycleProcessor 對象 | Lifecycle Bean 處理器 |
applicationEventMulticaster | ApplicationEventMulticaster 對 象 | Spring 事件廣播器 |
注解驅動 Spring 應用上下文內建可查找的依賴
Bean 名稱 | Bean 實例 | 使用場景 |
---|---|---|
org.springframework.context.annotation.internalConfigurationAnnotationProcessor | ConfigurationClassPostProcesso | 處理 Spring 配置類 |
org.springframework.context.annotation.internalAutowiredAnnotationProcessor | AutowiredAnnotationBeanPostProcessor 對象 | 處理 @Autowired 以及 @Value 注解 |
org.springframework.context.annotation.internalCommonAnnotationProcessor | CommonAnnotationBeanPostProcessor 對象 | (條件激活)處理 JSR-250 注解,如 @PostConstruct 等 |
org.springframework.context.event.internalEventListenerProcessor | EventListenerMethodProcessor 對象 | 處理標注 @EventListener 的 Spring 事件監聽方法 |
org.springframework.context.event.internalEventListenerFactory | DefaultEventListenerFactory 對象 | @EventListener 事件監聽方法適配為 ApplicationListener |
org.springframework.context.annotation.internalPersistenceAnnotationProcessor | PersistenceAnnotationBeanPostProcessor 對象 | (條件激活)處理 JPA 注解場景 |
依賴查找中的經典異常,BeansException
子類型
異常類型 | 觸發條件(舉例) | 場景舉例 |
---|---|---|
NoSuchBeanDefinitionException | 當查找 Bean 不存在於 IoC 容器時 BeanFactory#getBean | ObjectFactory#getObject |
NoUniqueBeanDefinitionException | 類型依賴查找時,IoC 容器存在多個 Bean 實例 | BeanFactory#getBean(Class) |
BeanInstantiationException | 當 Bean 所對應的類型非具體類時 | BeanFactory#getBean |
BeanCreationException | 當 Bean 初始化過程中 | Bean 初始化方法執行異常時 |
BeanDefinitionStoreException | 當 BeanDefinition 配置元信息非法時 | XML 配置資源無法打開時 |
BeanFactory.getBean
方法的執行是線程安全的,超過過程中會增加互斥鎖
第六章:Spring IoC依賴注入(Dependency Injection)
依賴注入的模式和類型
- 手動模式 - 配置或者編程的方式,提前安排注入規則
- XML 資源配置元信息
- Java 注解配置元信息
- API 配置元信息
- 自動模式 - 實現方提供依賴自動關聯的方式,按照內建的注入規則
- Autowiring(自動綁定)
依賴注入類型
依賴注入類型 | 配置元數據舉例 |
---|---|
Setter 方法 | <proeprty name="user" ref="userBean"/> |
構造器 | <constructor-arg name="user" ref="userBean" /> |
字段 | @Autowired User user; |
方法 | @Autowired public void user(User user) { ... } |
接口回調 | class MyBean implements BeanFactoryAware { ... } |
自動綁定(Autowiring)模式,Autowiring modes
參考枚舉:org.springframework.beans.factory.annotation.Autowire
模式 | 說明 |
---|---|
no | 默認值,未激活 Autowiring,需要手動指定依賴注入對象。 |
byName | 根據被注入屬性的名稱作為 Bean 名稱進行依賴查找,並將對象設置到該屬性。 |
byType | 根據被注入屬性的類型作為依賴類型進行查找,並將對象設置到該屬性。 |
constructor | 特殊 byType 類型,用於構造器參數。 |
Java 注解配置元信息
-
@Autowired
-
@Resource
-
@Inject
可選,需要環境中存在 JSR-330 依賴
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
-
@Bean
Aware 系列接口回調
內建接口 | 說明 |
---|---|
BeanFactoryAware | 獲取 IoC 容器 - BeanFactory |
ApplicationContextAware | 獲取 Spring 應用上下文 - ApplicationContext 對象 |
EnvironmentAware | 獲取 Environment 對象 |
ResourceLoaderAware | 獲取資源加載器 對象 - ResourceLoader |
BeanClassLoaderAware | 獲取加載當前 Bean Class 的 ClassLoader |
BeanNameAware | 獲取當前 Bean 的名稱 |
MessageSourceAware | 獲取 MessageSource 對象,用於 Spring 國際化 |
ApplicationEventPublisherAware | 獲取 ApplicationEventPublishAware 對象,用於 Spring 事件 |
EmbeddedValueResolverAware | 獲取 StringValueResolver 對象,用於占位符處理 |
依賴注入類型選擇
- 低依賴:構造器注入
- 多依賴:Setter 方法注入
- 便利性:字段注入
- 聲明類:方法注入
各種類型注入:
-
基礎類型
- 原生類型(Primitive):boolean、byte、char、short、int、float、long、double
- 標量類型(Scalar):Number、Character、Boolean、Enum、Locale、Charset、Currency、Properties、UUID
- 常規類型(General):Object、String、TimeZone、Calendar、Optional 等
- Spring 類型:Resource、InputSource、Formatter 等
-
集合類型
- 數組類型(Array):原生類型、標量類型、常規類型、Spring 類型
- 集合類型(Collection)
- Collection:List、Set(SortedSet、NavigableSet、EnumSet)
- Map:Properties
-
限定注入
- 使用注解 @Qualifier 限定
- 通過 Bean 名稱限定
- 通過分組限定
- 基於注解 @Qualifier 擴展限定
- 自定義注解,如 Spring Cloud @LoadBalanced
- 使用注解 @Qualifier 限定
-
延遲依賴注入
-
使用 API ObjectFactory 延遲注入
- 單一類型
- 集合類型
-
使用 API ObjectProvider 延遲注入(推薦)
- 單一類型
- 集合類型
依賴處理過程
- 入口 -
DefaultListableBeanFactory#resolveDependency
- 依賴描述符 - DependencyDescriptor
- 自定綁定候選對象處理器 - AutowireCandidateResolver
@Autowired、@Inject 注入,參考 AutowiredAnnotationBeanPostProcessor
Java通用注解注入原理:
CommonAnnotationBeanPostProcessor
- 注入注解
- javax.xml.ws.WebServiceRef
- javax.ejb.EJB
- javax.annotation.Resource
- 生命周期注解
- javax.annotation.PostConstruct
- javax.annotation.PreDestroy
自定義依賴注入注解
-
基於
AutowiredAnnotationBeanPostProcessor
實現 -
自定義實現
- 生命周期處理
- InstantiationAwareBeanPostProcessor
- MergedBeanDefinitionPostProcessor
- 元數據
- InjectedElement
- InjectionMetadata
- 生命周期處理
-
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
@Autowired
@Value
@Inject
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
@PostConstruct
@PreDestroy
初始化 Bean 時,AutowiredAnnotationBeanPostProcessor
先解析 Bean 中的依賴(@Autowire
,@Value
),然后 CommonAnnotationBeanPostProcessor
調用初始化方法 @@PostConstruct
將 @Bean
方法設置為 static
,可以讓 Bean 提前初始化。
-
依賴查找:
ApplicationContext#getBean
-
依賴處理過程:
org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
第七章:Spring IoC依賴來源(Dependency Sources)
Spring IoC依賴來源
來源 | 配置元數據 | 注冊API | Spring Bean 對象 | 生命周期管理 | 配置元信息 | 使用場景 |
---|---|---|---|---|---|---|
Spring BeanDefinition | <bean id="user" class="org.geekbang...User"> @Bean public User user(){...} BeanDefinitionBuilder |
BeanDefinitionRegistry#registerBeanDefinition | 是 | 是 | 有 | 依賴查找、依賴注入 |
單例對象 | API 實現 | SingletonBeanRegistry#registerSingleton | 是 | 否 | 無 | 依賴查找、依賴注入 |
非 Spring 容器管理對象 | Resolvable Dependency | ConfigurableListableBeanFactory#registerResolvableDependency | 否 | 否 | 無 | 依賴注入 |
外部化配置 | @Value | Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); | 否 | 否 | 無 | 依賴注入 |
依賴注入比依賴查找多一個來源,Resolvable Dependency。也就是說,可以通過注入的方式獲取這類對象,但不能通過 BeanFactory#getBean
方法從容器中獲取。
Spring 內建 BeanDefintion
在使用 AnnotationConfigApplicationContext
或者在 XML 配置中配置了注解驅動 <context:annotation-config/>
,或組件掃描 <context:component-scan base-package="org.acme" />
,會觸發org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors
注入一些 Spring 內建的 Bean:
-
org.springframework.context.annotation.ConfigurationClassPostProcessor
-
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
-
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
-
org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor
需要環境中有 JPA 依賴
-
org.springframework.context.event.EventListenerMethodProcessor
-
org.springframework.context.event.DefaultEventListenerFactory
Spring 內建單例對象
Spring 啟動時,refresh()
方法會調用 org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
,會注入一些單例對象,名稱為:
environment
systemProperties
systemEnvironment
單例對象由 org.springframework.beans.factory.config.SingletonBeanRegistry
注冊,org.springframework.beans.factory.support.AbstractBeanFactory
實現了這個接口,從容器中獲取 Bean 的方法 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
中,會先從單例對象中查找,如果查找到,直接返回;查找不到,則從 Spring BeanDefinition 中獲取,並執行生命周期函數
Resolvable Dependency / 非 Spring 容器管理對象 / 可解析依賴
org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
后三個實際上都是同一個 ApplicationContext
order 值越大,優先級越小
第八章:Spring Bean 作用域
作用域 | 說明 |
---|---|
singleton | 默認 Spring Bean 作用域,一個 BeanFactory 有且僅有一個實例 |
prototype | 原型作用域,每次依賴查找和依賴注入生成新 Bean 對象 |
request | 將 Spring Bean 存儲在 ServletRequest 上下文中 |
session | 將 Spring Bean 存儲在 HttpSession 中 |
application | 將 Spring Bean 存儲在 ServletContext 中 |
注意事項:
- Spring 容器沒有辦法管理 prototype Bean 的完整生命周期,也沒有辦法記錄示例的存
在。銷毀回調方法將不會執行,可以利用 BeanPostProcessor 進行清掃工作。 - 無論是 Singleton 還是 Prototype Bean 均會執行初始化方法回調,不過僅 Singleton Bean 會執行銷毀方法回調
@Scope
注解定義原型 Bean :
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static User prototypeUser() {
return createUser();
}
"request" Bean 作用域
- XML -
<bean class= "..." scope = "request" />
- Java 注解 -
@RequestScope
或@Scope(WebApplicationContext.SCOPE_REQUEST)
每次使用的 CGLIB 代理對象是同一個,但是被代理的對象每次都會重新生成。
使用 IDEA 進行遠程調試:
- 在 Edit Configurations 中新增一個 Remote ,使用命令啟動 jar 時,在啟動命令中增加 Remote 里的內容,啟動 jar 以及 Remote,打斷點進行調試。
實現 API
@RequestScope
RequestScope
"session" Bean 作用域
配置
- XML -
<bean class= "..." scope = "session" />
- Java 注解 -
@RequestScope
或@Scope(WebApplicationContext.SCOPE_REQUEST)
實現 API
@SessionScope
SessionScope
"application" Bean 作用域
配置
- XML -
<bean class= "..." scope = "application" />
- Java 注解 -
@ApplicationScope
或@Scope(WebApplicationContext.SCOPE_APPLICATION)
實現 API
@ApplicationScope
ServletContextScope
實現方式與 request 和 session 不同,這里直接將 Bean 放入 ServletContext
中
自定義 Bean 作用域
實現 Scope
org.springframework.beans.factory.config.Scope
注冊 Scope
-
API
org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope
-
配置
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="..."> </entry> </map> </property> </bean>
第九章:Spring Bean生命周期(Bean Lifecycle)
Spring Bean 元信息配置階段
BeanDefinition 配置
- 面向資源
- XML 配置
- Properties 資源配置
- 面向注解
- 面向 API
Spring Bean 元信息解析階段
BeanDefinition 解析
- 面向資源 BeanDefinition 解析 -
BeanDefinitionReader
- XML 解析器 -
XmlBeanDefinitionReader
- Properties 解析器 -
PropertiesBeanDefinitionReader
- XML 解析器 -
- 面向注解 BeanDefinition 解析 -
AnnotatedBeanDefinitionReader
Spring Bean 注冊階段
BeanDefinition 注冊接口
BeanDefinitionRegistry
Spring BeanDefinition 合並階段
BeanDefinition 合並
父子 BeanDefinition 合並
- 當前 BeanFactory 查找
- 層次性 BeanFactory 查找
<bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
<property name="id" value="1"/>
...
</bean>
<bean id="superUser" class="org.geekbang.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user"
primary="true">
<property name="address" value="杭州"/>
</bean>
XmlBeanDefinitionReader#loadBeanDefinitions
加載 XML 文件時,賦值 DefaultListableBeanFactory#beanDefinitionMap
,這個 Map 中的 BeanDefinition
還沒有合並,也就是說 superUser 的屬性值還沒有從 user 中繼承過來。
AbstractBeanFactory#getBean
獲取 bean 時,執行 AbstractBeanFactory#getMergedBeanDefinition
,對 superUser 進行合並,放入 AbstractBeanFactory#mergedBeanDefinitions
中。
Spring Bean Class 加載階段
- ClassLoader 類加載
- Java Security 安全控制
- ConfigurableBeanFactory 臨時 ClassLoader
AbstractBeanDefinition#beanClass
被定義為 Object ,有兩種形式,一種是 全類名 的 String,另一種是 Class 對象
Java Security 安全控制 相關
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ->
doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
}
臨時 ClassLoader 與 load-time weaving 技術有關,用於進行類型檢查時(即尚未創建實際實例)
Spring Bean 實例化階段
-
傳統實例化方式
- 實例化策略 - InstantiationStrategy
-
構造器依賴注入
實例化階段,如果使用構造器注入,將解析構造器注入的依賴
AbstractAutowireCapableBeanFactory#createBeanInstance
Spring Bean 實例化前階段
- 非主流生命周期 - Bean 實例化前階段
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
返回非 null 時,阻止 bean 的默認實例化過程及以下生命周期
唯一可以進一步生命周期處理的是 BeanPostProcessor#postProcessAfterInitialization
Spring Bean 實例化后階段
- Bean 屬性賦值(Populate)判斷
InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
在給 bean 實例做屬性賦值的方法 AbstractAutowireCapableBeanFactory#populateBean
的最開始調用,如果返回 false ,阻止 bean 的屬性賦值及以下生命周期
Spring Bean 屬性賦值前階段
-
Bean 屬性值元信息
- PropertyValues
-
Bean 屬性賦值前回調
-
Spring 1.2 - 5.0:
InstantiationAwareBeanPostProcessor#postProcessPropertyValues
此方法已過期,使用
postProcessProperties
替代,為了兼容,只有在postProcessProperties
返回 null 時(默認實現),才會調用此方法 -
Spring 5.1:
InstantiationAwareBeanPostProcessor#postProcessProperties
-
在工廠將給定屬性值應用於給定 bean 之前,對它們進行處理。
在依賴注入( byName
或 byType
)之后,在將配置的屬性賦值給 bean 實例 AbstractAutowireCapableBeanFactory#applyPropertyValues
之前執行此階段方法
Spring Bean 初始化階段
AbstractAutowireCapableBeanFactory#initializeBean
Spring Bean Aware 接口回調階段
Spring Aware 接口,執行順序從上到下
BeanNameAware
BeanClassLoaderAware
BeanFactoryAware
依賴於 ApplicationContext
:
EnvironmentAware
EmbeddedValueResolverAware
ResourceLoaderAware
ApplicationEventPublisherAware
MessageSourceAware
ApplicationContextAware
在初始化 Bean 實例 AbstractAutowireCapableBeanFactory#initializeBean
的最開始執行此階段,前三個接口直接調用,而依賴於 ApplicationContext 的幾個 Aware 接口,在 ApplicationContext 的生命周期中,會在 beanFactory 中加入 ApplicationContextAwareProcessor
,在其 postProcessBeforeInitialization
方法中執行調用
ApplicationContextAwareProcessor
是包權限的
Spring Bean 初始化前階段
已完成
-
Bean 實例化
-
Bean 屬性賦值
-
Bean Aware 接口回調
方法回調
BeanPostProcessor#postProcessBeforeInitialization
Spring Bean 初始化階段
Bean 初始化(Initialization)
@PostConstruct
標注方法- 實現
InitializingBean
接口的afterPropertiesSet()
方法 - 自定義初始化方法
對 @PostConstruct
的處理需要依賴於注解驅動,CommonAnnotationBeanPostProcessor#postProcessBeforeInitialization
Spring Bean 初始化后階段
方法回調
BeanPostProcessor#postProcessAfterInitialization
Spring Bean 初始化完成階段
方法回調
- Spring 4.1 +:
SmartInitializingSingleton#afterSingletonsInstantiated
SmartInitializingSingleton
通常在 Spring ApplicationContext 場景使用
使用 BeanFactory
時,需要顯式的調用此方法;在 ApplicationContext
啟動時,調用了此方法 AbstractApplicationContext#finishBeanFactoryInitialization
,這個方法做了兩件事情:
- 將已注冊的
BeanDefinition
初始化成 Spring Bean - 調用所有
SmartInitializingSingleton#afterSingletonsInstantiated
Spring Bean 銷毀前階段
- 方法回調
DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
執行 ConfigurableBeanFactory#destroyBean
時,觸發 Bean 前銷毀階段
對 @PreDestroy
的處理需要依賴於注解驅動,CommonAnnotationBeanPostProcessor#postProcessBeforeDestruction
Spring Bean 銷毀階段
Bean 銷毀(Destroy)
@PreDestroy
標注方法- 實現
DisposableBean
接口的destroy()
方法 - 自定義銷毀方法
對 @PreDestroy
的處理需要依賴於注解驅動,CommonAnnotationBeanPostProcessor#postProcessBeforeDestruction
CommonAnnotationBeanPostProcessor
是 DestructionAwareBeanPostProcessor
的實現類之一
如果其他 DestructionAwareBeanPostProcessor
排序在 CommonAnnotationBeanPostProcessor
后,會先執行 @PreDestroy
標注方法,后執行其他 DestructionAwareBeanPostProcessor
銷毀前階段方法
Spring Bean 垃圾收集
Bean 垃圾回收(GC)
- 關閉 Spring 容器(應用上下文)
- 執行 GC
- Spring Bean 覆蓋的
finalize()
方法被回調
面試題
BeanPostProcessor 的使用場景有哪些?
答:BeanPostProcessor
提供 Spring Bean 初始化前和初始化后的生命周期回調,分別對應 postProcessBeforeInitialization
以及 postProcessAfterInitialization
方法,允許對關心的 Bean 進行擴展,甚至是替換。
加分項:其中,ApplicationContext
相關的 Aware
回調也是基於 BeanPostProcessor
實現,即 ApplicationContextAwareProcessor
。
BeanFactoryPostProcessor 與 BeanPostProcessor 的區別
答:BeanFactoryPostProcessor
是 Spring BeanFactory
(實際為 ConfigurableListableBeanFactory
) 的后置處理器,用於擴展 BeanFactory
,或通過 BeanFactory
進行依賴查找和依賴注入。
加分項:BeanFactoryPostProcessor
必須有 Spring ApplicationContext
執行,BeanFactory
無法與其直接交互。而 BeanPostProcessor
則直接與 BeanFactory
關聯,屬於 N 對 1 的關系。
BeanFactory 是怎樣處理 Bean 生命周期?
BeanFactory
的默認實現為 DefaultListableBeanFactory
,其中 Bean生命周期與方法映射如下:
- BeanDefinition 注冊階段 -
registerBeanDefinition
- BeanDefinition 合並階段 -
getMergedBeanDefinition
- Bean 實例化前階段 -
resolveBeforeInstantiation
- Bean 實例化階段 -
createBeanInstance
- Bean 實例化后階段 -
populateBean
- Bean 屬性賦值前階段 -
populateBean
- Bean 屬性賦值階段 -
populateBean
- Bean Aware 接口回調階段 -
initializeBean
- Bean 初始化前階段 -
initializeBean
- Bean 初始化階段 -
initializeBean
- Bean 初始化后階段 -
initializeBean
- Bean 初始化完成階段 -
preInstantiateSingletons
- Bean 銷毀前階段 -
destroyBean
- Bean 銷毀階段 -
destroyBean
第十章:Spring配置元信息(Configuration Metadata)
Spring 配置元信息
- Spring Bean 配置元信息 -
BeanDefinition
- Spring Bean 屬性元信息 -
PropertyValues
- Spring 容器配置元信息
- Spring 外部化配置元信息 -
PropertySource
- Spring Profile 元信息 -
@Profile
Spring Bean 配置元信息
Bean 配置元信息 - BeanDefinition
GenericBeanDefinition
:通用型BeanDefinition
RootBeanDefinition
:無 Parent 的BeanDefinition
或者合並后BeanDefinition
AnnotatedBeanDefinition
:注解標注的BeanDefinition
Spring Bean 屬性元信息
- Bean 屬性元信息 -
PropertyValues
- 可修改實現 -
MutablePropertyValues
- 元素成員 -
PropertyValue
- 可修改實現 -
- Bean 屬性上下文存儲 -
AttributeAccessor
- Bean 元信息元素 -
BeanMetadataElement
AttributeAccessorSupport#attributes
是附加屬性(不影響 Bean populate、initialize)
BeanMetadataAttributeAccessor#source
存儲當前 BeanDefinition 來自於何方(輔助作用)
Spring 容器配置元信息
Spring XML 配置元信息 - beans 元素相關
beans 元素屬性 | 默認值 | 使用場景 |
---|---|---|
profile | null(留空) | Spring Profiles 配置值 |
default-lazy-init | default | 當 outter beans “default-lazy-init” 屬性存在時,繼承該值,否則為“false” |
default-merge | default | 當 outter beans “default-merge” 屬性存在時,繼承該值,否則為“false” |
default-autowire | default | 當 outter beans “default-autowire” 屬性存在時,繼承該值,否則為“no” |
default-autowire-candidates | null(留空) | 默認 Spring Beans 名稱 pattern |
default-init-method | null(留空) | 默認 Spring Beans 自定義初始化方法 |
default-destroy-method | null(留空) | 默認 Spring Beans 自定義銷毀方法 |
Spring XML 配置元信息 - 應用上下文相關
XML 元素 | 使用場景 |
---|---|
<context:annotation-config /> | 激活 Spring 注解驅動 |
<context:component-scan /> | Spring @Component 以及自定義注解掃描 |
<context:load-time-weaver /> | 激活 Spring LoadTimeWeaver |
<context:mbean-export /> | 暴露 Spring Beans 作為 JMX Beans |
<context:mbean-server /> | 將當前平台作為 MBeanServer |
<context:property-placeholder /> | 加載外部化配置資源作為 Spring 屬性配置 |
<context:property-override /> | 利用外部化配置資源覆蓋 Spring 屬性值 |
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate
populateDefaults
基於 XML 資源裝載 Spring Bean 配置元信息
XML 元素 | 使用場景 |
---|---|
<beans:beans /> | 單 XML 資源下的多個 Spring Beans 配置 |
<beans:bean /> | 單個 Spring Bean 定義(BeanDefinition)配置 |
<beans:alias /> | 為 Spring Bean 定義(BeanDefinition)映射別名 |
<beans:import /> | 加載外部 Spring XML 配置資源 |
底層實現 - XmlBeanDefinitionReader
加載 BeanDefinition 入口方法 loadBeanDefinitions
使用 DOM 來解析 XML 文件,實現為 BeanDefinitionDocumentReader
解析方法:XmlBeanDefinitionReader#parseBeanDefinitions
- 判斷 beans 標簽的 profile 屬性,如果不在激活狀態,直接返回,不再向下解析
- 如果是 beans 標簽下的特殊元素,進行特殊處理,方法為
DefaultBeanDefinitionDocumentReader#parseDefaultElement
- beans
- bean
- alias
- import
- 否則,
BeanDefinitionParserDelegate#parseCustomElement
基於 Properties 資源裝載 Spring Bean 配置元信息
Properties 屬性名 | 使用場景 |
---|---|
(class) | Bean 類全稱限定名 |
(abstract) | 是否為抽象的 BeanDefinition |
(parent) | 指定 parent BeanDefinition 名稱 |
(lazy-init) | 是否為延遲初始化 |
(ref) | 引用其他 Bean 的名稱 |
(scope) | 設置 Bean 的 scope 屬性 |
${n} | n 表示第 n+1 個構造器參數 |
底層實現 - PropertiesBeanDefinitionReader
如果出現重復的 Bean 定義,后者不會被注冊進 BeanFactory
中
基於 Java 注解裝載 Spring Bean 配置元信息
Spring 模式注解
Spring 注解 | 場景說明 | 起始版本 |
---|---|---|
@Repository | 數據倉儲模式注解 | 2.0 |
@Component | 通用組件模式注解 | 2.5 |
@Service | 服務模式注解 | 2.5 |
@Controller | Web 控制器模式注解 | 2.5 |
@Configuration | 配置類模式注解 | 3.0 |
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters
將 @Component
及其派生注解加入篩選
Spring Bean 定義注解
Spring 注解 | 場景說明 | 起始版本 |
---|---|---|
@Bean | 替換 XML 元素 <bean> |
3.0 |
@DependsOn | 替代 XML 屬性 <bean depends-on="..."/> |
3.0 |
@Lazy | 替代 XML 屬性 <bean lazy-init="true|falses" /> |
3.0 |
@Primary | 替換 XML 元素 <bean primary="true|false" /> |
3.0 |
@Role | 替換 XML 元素 <bean role="..." /> |
3.1 |
@Lookup | 替代 XML 屬性 <bean lookup-method="..."> |
4.1 |
Spring Bean 依賴注入注解
Spring 注解 | 場景說明 | 起始版本 |
---|---|---|
@Autowired | Bean 依賴注入,支持多種依賴查找方式 | 2.5 |
@Qualifier | 細粒度的 @Autowired 依賴查找 | 2.5 |
AutowiredAnnotationBeanPostProcessor
與 @Autowired
相關
Java 注解 | 場景說明 | 起始版本 |
---|---|---|
@Resource | 類似於 @Autowired | 2.5 |
@Inject | 類似於 @Autowired | 2.5 |
CommonAnnotationBeanPostProcessor
與 @Resource
相關
AutowiredAnnotationBeanPostProcessor
與 @Inject
相關
Spring Bean 條件裝配注解
Spring 注解 | 場景說明 | 起始版本 |
---|---|---|
@Profile | 配置化條件裝配 | 3.1 |
@Conditional | 編程條件裝配 | 4.0 |
@Profile
基於 @Conditional
實現
@Conditional
相關 API,ConditionEvaluator
,用於判斷 Bean 是否滿足條件,滿足則注冊
Spring Bean 生命周期回調注解
Spring 注解 | 場景說明 | 起始版本 |
---|---|---|
@PostConstruct | 替換 XML 元素 <bean init-method="..." /> 或 InitializingBean |
2.5 |
@PreDestroy | 替換 XML 元素 <bean destroy-method="..." /> 或 DisposableBean |
2.5 |
CommonAnnotationBeanPostProcessor
Spring Bean 配置元信息底層實現
Spring BeanDefinition 解析與注冊
實現場景 | 實現類 | 起始版本 |
---|---|---|
XML 資源 | XmlBeanDefinitionReader | 1.0 |
Properties 資源 | PropertiesBeanDefinitionReader | 1.0 |
Java 注解 | AnnotatedBeanDefinitionReader | 3.0 |
-
XmlBeanDefinitionReader
和PropertiesBeanDefinitionReader
都繼承自AbstractBeanDefinitionReader
,實現了BeanDefinitionReader
接口,與資源(Resource)相關聯 -
AnnotatedBeanDefinitionReader
是獨立的類,與Resource
無關 -
Spring XML 資源 BeanDefinition 解析與注冊
- 核心 API -
XmlBeanDefinitionReader
- 資源 - Resource
- 底層 -
BeanDefinitionDocumentReader
- XML 解析 - Java DOM Level 3 API
- BeanDefinition 解析 -
BeanDefinitionParserDelegate
- BeanDefinition 注冊 -
BeanDefinitionRegistry
- 核心 API -
-
Spring Properties 資源 BeanDefinition 解析與注冊
- 核心 API -
PropertiesBeanDefinitionReader
- 資源
- 字節流 -
Resource
- 字符流 -
EncodedResouce
- 字節流 -
- 底層
- 存儲 -
java.util.Properties
- BeanDefinition 解析 - API 內部實現
- BeanDefinition 注冊 -
BeanDefinitionRegistry
- 存儲 -
- 資源
- 核心 API -
-
Spring Java 注冊 BeanDefinition 解析與注冊
- 核心 API -
AnnotatedBeanDefinitionReader
- 資源
- 類對象 -
java.lang.Class
- 類對象 -
- 底層
- 條件評估 -
ConditionEvaluator
- Bean 范圍解析 -
ScopeMetadataResolver
- BeanDefinition 解析 - 內部 API 實現
- BeanDefinition 處理 -
AnnotationConfigUtils.processCommonDefinitionAnnotations
- BeanDefinition 注冊 -
BeanDefinitionRegistry
- 條件評估 -
- 資源
- 核心 API -
Properties
資源加載默認編碼是 ISO-8859-1
AnnotatedBeanDefinitionReader
使用 ConditionEvaluator
判斷 Bean 的元信息,如果其中存在 @Conditional
條件,判斷此條件通過才會將 Bean 加入容器
基於 XML 資源裝載 Spring IoC 容器配置元信息
Spring IoC 容器相關 XML 配置
命名空間 | 所屬模塊 | Schema 資源 URL |
---|---|---|
beans | spring-beans | https://www.springframework.org/schema/beans/spring-beans.xsd |
context | spring-context | https://www.springframework.org/schema/context/spring-context.xsd |
aop | spring-aop | https://www.springframework.org/schema/aop/spring-aop.xsd |
tx | spring-tx | https://www.springframework.org/schema/tx/spring-tx.xsd |
util | spring-beans | https://www.springframework.org/schema/util/spring-util.xsd |
tool | spring-beans | https://www.springframework.org/schema/tool/spring-tool.xsd |
基於 Java 注解裝載 Spring IoC 容器配置元信息
Spring IoC 容器裝配注解
Spring 注解 | 場景說明 | 起始版本 |
---|---|---|
@ImportResource | 替換 XML 元素 <import> |
3.0 |
@Import | 導入 Configuration Class | 3.0 |
@ComponentScan | 掃描指定 package 下標注 Spring 模式注解的類 | 3.1 |
Spring IoC 配屬屬性注
Spring 注解 | 場景說明 | 起始版本 |
---|---|---|
@PropertySource | 配置屬性抽象 PropertySource 注解 | 3.1 |
@PropertySources | @PropertySource 集合注解 | 4.0 |
基於 Extensible XML authoring 擴展 Spring XML 元素
Spring XML 擴展
- 編寫 XML Schema 文件:定義 XML 結構
- users.xsd
- 自定義
NamespaceHandler
實現:命名空間綁定- spring.handlers
org.geekbang.thinking.in.spring.configuration.metadata.UsersNamespaceHandler
- 自定義
BeanDefinitionParser
實現:XML 元素與BeanDefinition
解析org.geekbang.thinking.in.spring.configuration.metadata.UserBeanDefinitionParser
- 注冊 XML 擴展:命名空間與 XML Schema 映射
- spring.schemas
- users-context.xml
觸發時機:BeanDefinitionParserDelegate#parseCustomElement
- 獲取 namespace
- 通過 namespace 解析
NamespaceHandler
- 構造
ParserContext
- 解析元素,獲取
BeanDefinintion
基於 Properties 資源裝載外部化配置
- 注解驅動
@org.springframework.context.annotation.PropertySource
@org.springframework.context.annotation.PropertySources
- API 編程
org.springframework.core.env.PropertySource
org.springframework.core.env.PropertySources
基於 YAML 資源裝載外部化配置
API 編程
org.springframework.beans.factory.config.YamlProcessor
org.springframework.beans.factory.config.YamlMapFactoryBean
org.springframework.beans.factory.config.YamlPropertiesFactoryBean
Requires SnakeYAML 1.18 or higher, as of Spring Framework 5.0.6
通過 PropertySourceFactory 接口,引入 PropertySource
org.springframework.core.io.support.PropertySourceFactory
@PropertySource(
name = "yamlPropertySource",
value = "classpath:/META-INF/user.yaml",
factory = YamlPropertySourceFactory.class)
面試題
Spring 內建 XML Schema 常見有哪些?
命名空間 | 所屬模塊 | Schema 資源 URL |
---|---|---|
beans | spring-beans | https://www.springframework.org/schema/beans/spring-beans.xsd |
context | spring-context | https://www.springframework.org/schema/context/spring-context.xsd |
aop | spring-aop | https://www.springframework.org/schema/aop/spring-aop.xsd |
tx | spring-tx | https://www.springframework.org/schema/tx/spring-tx.xsd |
util | spring-beans | https://www.springframework.org/schema/util/spring-util.xsd |
tool | spring-beans | https://www.springframework.org/schema/tool/spring-tool.xsd |
Spring配置元信息具體有哪些?
- Bean 配置元信息:通過媒介(如 XML、Proeprties 等),解析 BeanDefinition
- IoC 容器配置元信息:通過媒介(如 XML、Proeprties 等),控制 IoC 容器行為,比如注解驅動、AOP 等
- 外部化配置:通過資源抽象(如 Proeprties、YAML 等),控制 PropertySource
- Spring Profile:通過外部化配置,提供條件分支流程
Extensible XML authoring 的缺點?
- 高復雜度:開發人員需要熟悉 XML Schema,spring.handlers,spring.schemas 以及 Spring API 。
- 嵌套元素支持較弱:通常需要使用方法遞歸或者其嵌套解析的方式處理嵌套(子)元素。
- XML 處理性能較差:Spring XML 基於 DOM Level 3 API 實現,該 API 便於理解,然而性能較差。
- XML 框架移植性差:很難適配高性能和便利性的 XML 框架,如 JAXB。
第十一章:Spring 資源管理
引入動機
為什么 Spring 不使用 Java 標准資源管理,而選擇重新發明輪子?
- Java 標准資源管理強大,然而擴展復雜,資源存儲方式並不統一
- Spring 要自立門戶(重要的話,要講三遍)
- Spring “抄”、“超” 和 “潮”
Java 標准資源管理
Java 標准資源定位
職責 | 說明 |
---|---|
面向資源 | 文件系統、artifact(jar、war、ear 文件)以及遠程資源(HTTP、FTP 等) |
API 整合 | java.lang.ClassLoader#getResource、java.io.File 或 java.net.URL |
資源定位 | java.net.URL 或 java.net.URI |
面向流式存儲 | java.net.URLConnection |
協議擴展 | java.net.URLStreamHandler 或 java.net.URLStreamHandlerFactory |
Java URL 協議擴展
- 基於
java.net.URLStreamHandlerFactory
- 基於
java.net.URLStreamHandler
基於 java.net.URLStreamHandler 擴展協議
JDK 1.8 內建協議實現
協議 | 實現類 |
---|---|
file | sun.net.www.protocol.file.Handler |
ftp | sun.net.www.protocol.ftp.Handler |
http | sun.net.www.protocol.http.Handler |
https | sun.net.www.protocol.https.Handler |
jar | sun.net.www.protocol.jar.Handler |
mailto | sun.net.www.protocol.mailto.Handler |
netdoc | sun.net.www.protocol.netdoc.Handler |
實現類名必須為 Handler
實現類命名規則 | 說明 |
---|---|
默認 | sun.net.www.protocol.${protocol}.Handler |
自定義 | 通過 Java Properties java.protocol.handler.pkgs 指定實現類包名,實現類名必須為 Handler 。如果存在多包名指定,通過分隔符 | |
Spring 資源接口
類型 | 接口 |
---|---|
輸入流 | org.springframework.core.io.InputStreamSource |
只讀資源 | org.springframework.core.io.Resource |
可寫資源 | org.springframework.core.io.WritableResource |
編碼資源 | org.springframework.core.io.support.EncodedResource |
上下文資源 | org.springframework.core.io.ContextResource |
Spring 內建 Resource 實現
資源來源 | 資源協議 | 實現類 |
---|---|---|
Bean 定義 | 無 | org.springframework.beans.factory.support.BeanDefinitionResource |
數組 | 無 | org.springframework.core.io.ByteArrayResource |
類路徑 | classpath:/ |
org.springframework.core.io.ClassPathResource |
文件系統 | file:/ |
org.springframework.core.io.FileSystemResource |
URL | URL 支持的協議 | org.springframework.core.io.UrlResource |
ServletContext | 無 | org.springframework.web.context.support.ServletContextResource |
Spring Resource 接口擴展
- 可寫資源接口
org.springframework.core.io.WritableResource
org.springframework.core.io.FileSystemResource
org.springframework.core.io.FileUrlResource
(@since 5.0.2)org.springframework.core.io.PathResource
(@since 4.0 & @Deprecated)
- 編碼資源接口
org.springframework.core.io.support.EncodedResource
Spring 資源加載器
Resource 加載器
org.springframework.core.io.ResourceLoader
org.springframework.core.io.DefaultResourceLoader
org.springframework.core.io.FileSystemResourceLoader
org.springframework.core.io.ClassRelativeResourceLoader
org.springframework.context.support.AbstractApplicationContext
Spring 通配路徑資源加載器
- 通配路徑 ResourceLoader
org.springframework.core.io.support.ResourcePatternResolver
org.springframework.core.io.support.PathMatchingResourcePatternResolver
- 路徑匹配器
org.springframework.util.PathMatcher
- Ant 模式匹配實現 -
org.springframework.util.AntPathMatcher
- Ant 模式匹配實現 -
Spring 通配路徑資源擴展
- 實現
org.springframework.util.PathMatcher
- 重置 PathMatcher
PathMatchingResourcePatternResolver#setPathMatcher
依賴注入 Spring Resource
基於 @Value
實現
@Value("classpath:/...")
private Resource resource;
@Value("classpath*:/META-INF/*.properties")
private Resource[] propertiesResources;
依賴注入 ResourceLoader
- 方法一:實現
ResourceLoaderAware
回調 - 方法二:
@Autowired
注入ResourceLoader
- 方法三:注入
ApplicationContext
作為ResourceLoader
ApplicationContext
接口繼承 ResourcePatternResolver
繼承 ResourceLoader
ResourceLoaderAware
回調在 實例初始化(@PostConstruct
等)之前
面試題
Spring 配置資源中有哪些常見類型?
- XML 資源
- Properties 資源
- YAML 資源
請例舉不同類型 Spring 配置資源?
- XML 資源
- 普通 Bean Definition XML 配置資源 -
*.xml
- Spring Schema 資源 -
*.xsd
- 普通 Bean Definition XML 配置資源 -
- Properties 資源
- 普通 Properties 格式資源 -
*.properties
- Spring Handler 實現類映射文件 -
META-INF/spring.handlers
- Spring Schema 資源映射文件 -
META-INF/spring.schemas
- 普通 Properties 格式資源 -
- YAML 資源
- 普通 YAML 配置資源 -
*.yaml
或*.yml
- 普通 YAML 配置資源 -
Java 標准資源管理擴展的步驟?
- 簡易實現
- 實現
URLStreamHandler
並放置在sun.net.www.protocol.${protocol}.Handler
包下
- 實現
- 自定義實現
- 實現
URLStreamHandler
- 添加
-Djava.protocol.handler.pkgs
啟動參數,指向URLStreamHandler
實現類的包下
- 實現
- 高級實現
- 實現
URLStreamHandlerFactory
並傳遞到 URL 之中
- 實現
簡易實現實例
-
擴展 x 協議,新建類
sun.net.www.protocol.x.Handler
,類名格式必須符合規范public class Handler extends URLStreamHandler { @Override protected URLConnection openConnection(URL u) throws IOException { return new XURLConnection(u); } }
-
新建類
XURLConnection
,實現java.net.URLConnection
public class XURLConnection extends URLConnection { private final ClassPathResource resource; // URL = x:///META-INF/default.properties protected XURLConnection(URL url) { super(url); this.resource = new ClassPathResource(url.getPath()); } @Override public void connect() throws IOException { } public InputStream getInputStream() throws IOException { return resource.getInputStream(); } }
-
測試使用
public class HandlerTest { public static void main(String[] args) throws IOException { URL url = new URL("x:///META-INF/default.properties"); // 類似於 classpath:/META-INF/default.properties InputStream inputStream = url.openStream(); System.out.println(StreamUtils.copyToString(inputStream, Charset.forName("UTF-8"))); } }
自定義實現
-
新建類
Handler
,繼承sun.net.www.protocol.x.Handle
,類名必須為Handler
,包名無限制public class Handler extends sun.net.www.protocol.x.Handler { // -Djava.protocol.handler.pkgs=org.geekbang.thinking.in.spring.resource public static void main(String[] args) throws IOException { // springx 協議 URL url = new URL("springx:///META-INF/production.properties"); // 類似於 classpath:/META-INF/default.properties InputStream inputStream = url.openStream(); System.out.println(StreamUtils.copyToString(inputStream, Charset.forName("UTF-8"))); } }
-
運行時增加 VM 參數,
-Djava.protocol.handler.pkgs=org.geekbang.thinking.in.spring.resource
高級實現
public class MyURLStreamHandlerFactory implements URLStreamHandlerFactory {
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
return new Handler();
}
public static void main(String[] args) throws IOException {
// URL 設置 URLStreamHandlerFactory,必須在創建 URL 實例之前
URL.setURLStreamHandlerFactory(new MyURLStreamHandlerFactory());
// springx 協議
URL url = new URL("springx:///META-INF/production.properties"); // 類似於 classpath:/META-INF/default.properties
InputStream inputStream = url.openStream();
System.out.println(StreamUtils.copyToString(inputStream, Charset.forName("UTF-8")));
}
}