Spring Boot使用了多種方式來實現自動配置,其中DeferredImportSelector
介面是這些機制之一。
DeferredImportSelector
是ImportSelector
的一個擴充套件,它允許延遲匯入配置類直到所有@Configuration
類都被處理完畢。這對於某些自動配置類需要在應用程式上下文的建立過程中的後期階段才能做出決定的場景很有用。
為什麼這麼說呢?因為springboot匯入的自動裝配類大多都有@Conditional
註解來判斷這個裝配類所依賴的類在當前路徑下存不存在,@Conditional有很多種,有的只需要判斷當前專案路徑下有沒有該類(比如:@ConditionalOnClass
),有的需要判斷當前spring容器中有沒有該類的Bean比如:(@ConditionalOnBean
)。
所以,當情況為後者時,使用@Conditional
註解時必須確保我們匯入的Bean已經存在了。所以就會延遲匯入。那些加在啟動類上的@EnableXXX註解就是這樣的,在註解內部會用@Import匯入一個標記類的Bean,然後再在自動裝配類上@ConditionalOnBean註解判斷有沒有該Bean。這樣就可以用@EnableXXX註解來控制是否匯入自動裝配類
下面我們分別舉例:
舉例1、
我舉一個第一種情況的例子,假如我們專案中要用到Mybatis,那我們要匯入mybatis
的依賴和mybatis-spring-boot
的依賴。當然,這兩個依賴被springboot整合了
在mybatis-springboot的依賴中就會有一個自動裝配類:MybatisAutoConfiguration
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
的意思就是當類路徑下存在SqlSessionFactory
和SqlSessionFactoryBean
這兩個類時,配置才會被啟用,相應的Bean才會被建立。只要存在即可,不需要讓他成為容器的Bean。因為這兩個類都是mybatis包下的,我們總不能在這兩個類上加一個@Component註解吧!所以只需要判斷存在即可。所以只要我們匯入了mybatis的依賴,這個判斷就會生效。
舉例2
這個例子就是我們自定義starter
新增一個標記類ConfigMarker
public class ConfigMarker{
}
新增@EnableRegisterServer
註解,利用@Import註解匯入ConfigMarker到Bean容器
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({ConfigMarker.class}) //將ConfigMarker新增到容器中
public @interface EnableRegisterServer{
}
改造HttpAutoConfiguration
,新增條件註解@ConditionalOnBean(Marker.class)
@Configuration
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnBean(Marker.class) //只有當Marker類的Bean在容器中存在時,當前自動配置類才會生效
public class HttpAutoConfiguration {
@Resource
private HttpProperties properties; // 使用配置
// 在Spring上下文中建立一個物件
@Bean
public HttpClient init() {
HttpClient client = new HttpClient();
String url = properties.getUrl();
client.setUrl(url);
return client;
}
}
測試一下,在另外一個springboot專案中匯入當前工程starter,在主啟動類上新增註解@EnableRegisterServer
大功告成。
如果不在主啟動類上新增@EnableRegisterServer
註解,當前的標註類Marker
就不會被匯入。就不會滿足@ConditionalOnBean(Marker.class)
註解的條件,HttpAutoConfiguration
自動配置類也就不會被匯入了。
這時候再使用:
@Resource
private HttpClient httpClient;
程式就會報錯,因為spring容器中根本就不存在這個這個Bean。
所以,就透過@EnableRegisterServer
註解給自動裝配提供了一個開關。