Spring Boot原理探究

本次我们来探秘一下Springboot原理。如下是一个启动类。

@SpringBootApplication
public class DemoApplication {
   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }
}

Spring Boot启动原理

首先我们肯定要从@SpringBootApplication注解来开始看。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
点进@SpringBootApplication我们可以看到如下内容,重点关注下面三个注解:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

这里的@Configuration对我们来说不陌生,它就是JavaConfig形式的Spring Ioc容器的配置类使用的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。而基于JavaConfig的配置方式是这样:

@Configuration
public class MockConfiguration{
    //bean定义
    @Bean
    public 。。。
}

@ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

@EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器 

Spring Boot自动配置原理

自动配置的关键点在主要在于@EnableAutoConfiguration注解,我们通过查看其源码发现,其类上有个一@Import注解,AutoConfigurationImportSelector选择器为其导入了配置。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    // 排除自动配置
	Class<?>[] exclude() default {};
     // 排除自动配置
	String[] excludeName() default {};
}

通过查看AutoConfigurationImportSelector类,该类中有一个selectImports方法。该方法通过List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);通过这一行语句,获得了一个List<String> 这个list就是配置集合,最后将这些配置返回。

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);
         ……
         ……
        return configurations.toArray(new String[configurations.size()]);
	}


我们继续查看getCandidateConfigurations方法,追寻这些配置从何而来。 

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
		Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}


public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }



private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                        result.addAll((String)entry.getKey(), factoryClassNames);
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var9) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
            }
        }
    }

我们看到

Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
读取 META-INF/spring.factories配置信息获得一个URL迭代器。并将这个url集合存入Map中,随后其调用处以 factoryClassName(EnableAutoConfiguration)为Key获取对应的自动配置类集合并返回加入Spring容器
 
那么spring.factories是什么内容呢?
 
不同依赖下的spring.factories是不同的,总而言之,spring.factories中是各种Bean的全限定类名,例如映射器、解析器、配置类等,我们关注数量众多的 ***AutoConfiguration自动配置类。以其中一个ElasticsearchAutoConfiguration为例,我们看到,
@EnableConfigurationProperties(ElasticsearchProperties.class)
@Configuration
@ConditionalOnClass({ Client.class, TransportClientFactoryBean.class })
@ConditionalOnProperty(prefix = "spring.data.elasticsearch", name = "cluster-nodes", matchIfMissing = false)
@EnableConfigurationProperties(ElasticsearchProperties.class)
public class ElasticsearchAutoConfiguration {

打开ElasticsearchProperties,原来ElasticsearchProperties对象的值是从配置文件里面读取的,所以当我们要配置ES集群名称时,可指定一个名称替换默认的elasticsearch

@ConfigurationProperties(prefix = "spring.data.elasticsearch")
public class ElasticsearchProperties {

	/**
	 * Elasticsearch cluster name.
	 */
	private String clusterName = "elasticsearch";

	/**
	 * Comma-separated list of cluster node addresses.
	 */
	private String clusterNodes;

所以 ,前面的@EnableConfigurationProperties(ElasticsearchProperties.class)注解是将ElasticsearchProperties 与配置文件绑定起来。

所以Springboot能配置什么,就是获取*properties里面的属性值,我们也可以根据这些属性值来配置properteis配置文件。
 

用好SpringBoot只要把握这几点:

  1. SpringBoot启动会加载大量的自动配置类

  2. 所要做的就是我们需要的功能SpringBoot有没有帮我们写好的自动配置类:

  3. 如果有就再来看这个自动配置类中到底配置了哪些组件,Springboot自动配置类里边只要我们要用的组件有,我们就不需要再来配置了,但是如果说没有我们所需要的组件,那么我们就需要自己来写一个配置类来把我们相应的组件配置起来。

  4. 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,而这些属性我们就可以在配置文件指定这些属性的值

 

猜你喜欢

转载自blog.csdn.net/csdn9988680/article/details/81222163