Spring 高階話題
1. Spring Aware
1.1 基礎知識
Spring 的依賴注入,即所有的 Bean 對 Spring 容器的存在是沒有意識的。就說明 Bean 之間的耦合度很低。
但在實際專案中,要想用到 Spring 容器中所提供的資源就需要使 Bean 意識到容器 Spring 的存在,才能呼叫 Spring 所提供的資源,這就是所謂的 Spring Aware。若使用了 Spring Aware ,你的 Bean 將會和 Spring 框架耦合。
Spring 提供的 Aware 介面如下表所示:
函式名 | 函式功能 |
---|---|
BeanNameAware | 獲得到容器中 Bean 的名稱 |
BeanFactoryAware | 獲得當前 bean factory,這樣可以呼叫容器的服務 |
ApplicationContextAware* | 當前application context,這樣可以呼叫容器的服務 |
MessageSourceAware | 獲得message source,這樣可以獲得文字資訊 |
ApplicationEventPublisherAware | 應用事件釋出器,可以釋出事件 |
ResourceLoaderAware | 獲得資源載入器,可以獲得外部資原始檔 |
Spring Aware 的目的是為了讓 Bean 獲得 Spring 容器的服務。因為 ApplicationContext 介面集成了 MessageSource 介面、ApplicationEventPublisher 介面和 ResourceLoader 介面,所以 Bean 繼承 ApplicationContextAware 可以獲得 Spring 容器的所有服務。
2. 多執行緒
2.1 基礎知識
Spring 通過任務執行器(TaskExecutor)來實現多執行緒和併發程式設計。使用 ThreadPoolTaskExecutor 可實現一個基於執行緒池的 TaskExecutor。而實際開發中任務一般是非阻礙的,即非同步的,所以我們要在配置類中通過 @EnbaleAsync 開啟對非同步任務的支援,並通過在實際執行的 Bean 的方法中使用 @Async 註解來宣告其是一個非同步任務。
3. 計劃任務
3.1 基礎知識
首先通過在配置類中註解 @EnableScheduling 來開啟對計劃任務的支援,然後在要執行計劃任務的方法上註解 @Scheduled ,宣告這是一個計劃任務。
4. 條件註解 @Conditional
4.1 基礎知識
@Conditional 根據滿足某一個特定條件建立一個特定的 Bean。比方說,當某一個 jar 包在一個類路徑下的時候,自動配置一個或多個 Bean;或者只有某個 Bean 被建立才會建立另外一個 Bean。總的來說,就是根據特定條件來控制 Bean 的建立行為,這樣我們可以利用這個特性來進行一些自動配置。
5. 組合註解與元註解
5.1 基礎知識
元註解 其實就是可以註解到別的註解上的註解,被註解的註解稱之為 組合註解,組合註解具備註解其上的元註解的功能。Spring 的很多註解都可以作為元註解,而且 Spring 本身已經有很多組合註解,如 @Configuration 就是一個組合 @Component 註解,表明這個類其實也是個 Bean。
6. @Enable* 註解的工作原理
6.1 基礎知識
@Enable* 的例子有:@EnableAspectJAutoProxy 開啟對 AspectJ 自動代理的支援;@EnableAsync 開啟非同步方法的支援;@EnableScheduling 開啟計劃任務的支援。
通過觀察這些 @Enable* 註解的原始碼,我們發向所有的註解都有一個 @Import 註解,@Import 是用來匯入配置類的,這也就意味著這些自動開啟的實現其實是匯入了一些自動配置的 Bean。這些匯入的配置方式主要分為以下三種類型。
(1)直接匯入配置類
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
直接匯入配置類 SchedulingConfiguraiton,這個類註解了 @Configuration,且註冊了一個 scheduledAnnotationProcessor 的 Bean,原始碼如下:
@Configuration
@Role(2)
public class SchedulingConfiguration {
public SchedulingConfiguration() {
}
@Bean(
name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
)
@Role(2)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
(2)依據條件選擇配置類
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})
public @interface EnableAsync {
Class<? extends Annotation> annotation() default Annotation.class;
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default 2147483647;
}
AsyncConfigurationSelector 通過條件來選擇需要匯入的配置類,AsyncConfigurationSelector 的根介面為 ImportSelector,這個介面需重寫 selectImports 方法,在此方法內進行事先條件判斷。此例中,若 adviceMode 為 PROXY,則返回 ProxyAsyncConfiguration 這個配置類;若 activeMode 為 ASPECTJ,則返回 AspectJAsyncConfiguration 配置類,原始碼如下:
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
public AsyncConfigurationSelector() {
}
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch(adviceMode) {
case PROXY:
return new String[]{ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[]{"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"};
default:
return null;
}
}
}
(3)動態註冊 Bean
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
AspectJAutoProxyRegistrar 實現了 ImportBeanDefinitionRegistrar 介面,它的作用是在執行時自動新增 Bean 到已有的配置類,通過重寫方法:
registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
其中,AnnotationMetadata 引數用來獲得當前配置類上的註解;BeanDefinitionRegistry 引數來註冊 Bean。原始碼如下:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
AspectJAutoProxyRegistrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}