最近我看了看Springboot的源碼,藉着週末分享@Import這一小知識點。java
再看源碼以前先寫一個小的demo,試着看看@Import是怎麼用的。(爲避免文章過長,下面代碼均有必定程度的代碼刪減,能夠自行補全或查看源碼哦!)web
1.建立一個maven項目,pom.xml文件配置以下:spring
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.建立一個實體類app
package com.entity;
public class User implements Serializable{
private String name;
private String age;
public User() {
this.name = "xiaochen";
this.age = "6";
}
}
3.建立ImportUser類maven
package com.demo;
public class ImportUser implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {"com.entity.User"};
}
}
這裏看到ImportSelector接口,是否是好像在哪裏見過呢?ide
4.建立配置類spring-boot
package com.demo;
@Import(ImportUser.class)
@Configuration
public class ImportConfiguration {}
5.建立啓動類post
package com.demo;
public class ImportDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//這裏使用register()和scan()方法均可,用scan()方法就不用在配置類中使用@Configuration註解了。
// applicationContext.register(ImportConfiguration.class);
applicationContext.scan("com.demo");
applicationContext.refresh();
User user = applicationContext.getBean(User.class);
System.out.println(user);
}
}
6,運行結果學習
上邊的代碼很簡單,就不過多解釋,下面直接看Springboot中是怎麼使用的。ui
咱們知道@SpringbootApplication註解主要是由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan組成。而@EnableAutoConfiguration中就用到了@Import註解,用於引入
EnableAutoConfigurationImportSelector類,代碼以下:
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
這裏咱們先建立一個Springboot啓動類
@SpringBootApplication
public class ApplicationStart {
public static void main(String[] args) {
SpringApplication.run(ApplicationStart.class, args);
}
}
點擊run()方法進入SpringApplication類中,依次查看調用方法;
public ConfigurableApplicationContext run(String... args) {
refreshContext(context);
}
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
}
protected void refresh(ApplicationContext applicationContext) {
((AbstractApplicationContext) applicationContext).refresh();
}
@Override
public void refresh() throws BeansException, IllegalStateException {
invokeBeanFactoryPostProcessors(beanFactory);
}
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}
到此再點擊進入org.springframework.context.support.PostProcessorRegistrationDelegate類中;
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);
}
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
再點擊postProcessBeanDefinitionRegistry()方法進入org.springframework.context.annotation.ConfigurationClassPostProcessor類中;
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
parser.parse(candidates);
}
這裏再點擊parse()方法進入org.springframework.context.annotation.ConfigurationClassParser類當中;
public void parse(Set<BeanDefinitionHolder> configCandidates) {
processDeferredImportSelectors();
}
private void processDeferredImportSelectors() {
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
}
這裏看到selectImports()方法就在上面demo中出現過,再查看一下以前使用@Import註解導入的EnableAutoConfigurationImportSelector類,其就間接實現了ImportSelector接口;這和demo中是否是很類似。
這裏的 selectImports()方法再往裏就是讀取各個jar包中有含 META-INF/spring.factories.文件的信息了,這裏就很少作分析了。
但這裏會不會有個疑問,@Import是怎麼起做用的,這裏就要看看deferredImport是怎麼實例化的!!!,也是在上面的方法中有這樣一段代碼;
private void processDeferredImportSelectors() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
}
}
代碼中能夠看出是deferredImportSelectors對象間接賦值 , 那就找它實例化的位置;
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates,
boolean checkForCircularImports) throws IOException {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
//這裏用於實例化ImportSelector的實現類
ImportSelector selector = BeanUtils.instantiateClass(candidateClass,ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(selector, this.environment,this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
}
}
}
上面也就是判斷並實例化ImportSelector接口的實現類,而怎麼識別的呢? importCandidates又是如何獲得的?接着找。。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
}
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports,Set<SourceClass> visited) throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
看到這裏的判斷也大致明白了吧。
這裏就不作總結了,加個最近以爲頗有道理的一句話,學習實際上是個低成本,高回報的方式。