SpringBoot 学习(三)---- SpringBoot 核心 之 Spring Boot自动配置原理

Spring Boot 在进行 SpringApplication 对象实例化时会加载 META-INF/spring.factories 文件,将该配置文件中的配置载入 Spring容器,进行自动配置。

一、源码分析

1、首先进入启动 Spring Boot 项目代码 SpringApplication.run(App.class,args)的源码。

                              程序清单:org/springframework/boot/SpringApplication.java

public static ConfigurableApplicationContext run (Object[ ] sources,String[ ] args ){
    return new SpringApplication( sources ).run( args ) ;
}

2、可以看到 run 方法实际上在创建 SpringApplication对象实例,下面来看创建 SpringApplication 对象实例的代码。

                  程序清单:org/springframework/boot/SpringApplication.java

public SpringApplication( Object... sources ){
    initialize( sources ) ;
}

3、接下来就是调用 initialize( sources ) 方法,该方法的源码如下:

                               程序清单:org/springframework/boot/SpringApplication.java

private void initialize(Object[] sources) {
     if (sources != null && sources.length > 0) {
         this.sources.addAll(Arrays.asList(sources));
     }
     this.webEnvironment = deduceWebEnvironment();
     setInitializers((Collection) getSpringFactoriesInstances(
     ApplicationContextInitializer.class));
     setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
     this.mainApplicationClass = deduceMainApplicationClass();
}

initialize( sources ) 方法解析:

  • 如果sources⻓度⼤于0的话,加⼊到SpringApplication的sources中,该sources是⼀个LinkedHashSet.

  • 调⽤deduceWebEnvironment⽅法判断是否是web环境

  • 设置initializers.

  • 设置Listeners.

  • 设置mainApplicationClass.

4、initialize 方法调用了 getSpringFactoriesInstances 方法,代码如下:

                               程序清单:org/springframework/boot/SpringApplication.java

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
     Class<?>[] parameterTypes, Object... args) {
     ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
     // Use names and ensure unique to protect against duplicates
     // 使⽤Set保存names来避免重复元素
     Set<String> names = new LinkedHashSet<String>(
     SpringFactoriesLoader.loadFactoryNames(type, classLoader));
     // 根据names来进⾏实例化
     List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
     classLoader, args, names);
     // 对实例进⾏排序
     AnnotationAwareOrderComparator.sort(instances);
     return instances;
}

getSpringFactoriesInstances 方法解析:

  • 先获得ClassLoader.
  • 调 SpringFactoriesLoader#loadFactoryNames进⾏加载,然后放⼊到LinkedHashSet进⾏去重.
  • 调 createSpringFactoriesInstances进⾏初始化
  • 排序

5、在 getSpringFactoriesInstances 中又调用了 loadFactoryNames 方法,继续进入该方法,查看源码如下:                         

                                 程序清单:org/springframework/boot/SpringApplication.java

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
 String factoryClassName = factoryClass.getName();
 try {
 Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURC
E_LOCATION) :
 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
 List<String> result = new ArrayList<String>();
 while (urls.hasMoreElements()) {
 URL url = urls.nextElement();
 Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
 String factoryClassNames = properties.getProperty(factoryClassName);
 result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassN
ames)));
 }
 return result;
 }
 catch (IOException ex) {
 throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
 "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
 }
}

loadFactoryNames 方法解析:

  • 获得factoryClassName,对于当前来说factoryClassName =org.springframework.context.ApplicationContextInitializer.
  • 通过传⼊的classLoader加载META-INF/spring.factories⽂件.
  • 通过调⽤PropertiesLoaderUtils#loadProperties将其转为Properties.
  • 获得factoryClassName对应的值进⾏返回.
    对于当前来说,由于我们只加⼊了spring-boot-starter-web的依赖,因此会加载如下的配置:
  • 在spring-boot/META-INF/spring.factories中.org.springframework.context.ApplicationContextInitializer值

6、从上述源码中可以看到架子啊了一个常量:FACTORIES_RESOURCE_LOCATION,该常量的源码如下:

/**
*The location to look for factories.
*<p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION="META-INF/spring.factories";

从该源码中可以看出,最终 Spring Boot 是通过加载 META-INF/spring.factories 文件进行自动配置的。其所在位置如下图:

 

二、spring.factories 分析

spring factories 文件非常重要,用来指导 Spring Boot 找到制定的自动配置文件。

spring factories 文件重点内容分析如下:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

org.springframework.boot.env.PropertySourceLoader 对应的值表示指定的 Spring Boot 配置文件支持的格式。Spring Boot 的配置文件内置支持 properties、xml、yml、和 yaml 几种格式。其中 properties 和 xml 对应的 Loader 类为 PropertiesPropertySourceLoader,yml 和 yaml 对应的 Loader 类为 YamlPropertySourceLoader。

#Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

org.springframework.boot.SpringApplicationRunListener 对应的值表示运行的监听器类。默认会加载 EventPublishingRunListener,这个 RunListener 是在SpringApplication 对象的 run 方法执行到不同阶段时,发布相应的 event 给 SpringApplication 对象的 Listeners 中记录的事件监听器。

#Applicaiton Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer

org.springframework.context.ApplicationContextInitializer 对应的值表示 Spring Boot 中的应用程序的初始化类。默认加载4个ApplicationContextInitializer类。

  • ConfigurationWarningsApplicationContextInitializer 的作用是报告常见的配置错误。
  • ContextIdApplicationContextIntializer 的作用是给ApplicationContext设置一个ID。
  • DelegatingApplicationContextIntializer 的作用是将初始化的工作委托给 context.initializer.classes 环境变量指定的初始化器。
  • ServerPortInfoApplicationContextInitializer 的作用是监听 EmveddedServletCibtauber-InitializedEvent 类型的事件,然后将内嵌的Web服务器使用的端口设置到 Application-Context 中。

 

 

三、Spring Boot Web 开发自动配置

在 spring.factories 中可以看出,Web 开发的自动配置类是 org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,这个类中自动实现了 Spring MVC 的配置。现在以 Spring MVC 的如下配置为例,了解 Spring Boot 是如何实现该自动配置的。

<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>

1、查询 WebMvcAutoConfiguration 的源码如下:

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
    WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
    ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration{

其中 @ConditionalOnClass 是一个条件注解,意思就是只有当前项目运行环境中有 Servlet 类,并且有 DispatcherServlet 类以及 WebMvcConfigurerAdapter 类(说明本项目是需要集成 Spring MVC 的),Spring Boot 才会初始化 WebMvcAutoConfiguration 进行自动配置。

2、自动配置视图解析器 ViewResolver:

在WebMvcAutoConfiguration 类下找到以下源码:

@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix(this.mvcProperties.getView().getPrefix());
    resolver.setSuffix(this.mvcProperties.getView().getSuffix());
    return resolver;
}

@Bean 在这里定义了一个 Bean 对象 InternalResourceViewResolver,以前是通过<bean>便签来定义的。

@ConditionalOnMissingBean 是一个条件注释,在当前环境下没有这个 Bean 的时候才会创建该 Bean。

方法在返回值即 InternalResourceViewResolver ,正是我们需要的对象,那么视图解析器中前缀和后缀 Spring Boot 是如何实现自动配置的呢?

resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());

该源码会找到一个 View对象:

public static class view{
    
    /**
    * Spring MVC 视图前缀
    */
    private String prefix;

    /**
    * Spring MVC 视图后缀
    */
    private String suffix;
    
    public String getPrefix(){
        return this.prefix;
    }
    public void setPrefix(String prefix){
        this.prefix = prefix;
    }
    public String getSuffix(){
        return this.suffix;
    }
    public void setSuffix(String suffix){
        this.suffix = suffix;
    }

}

View 对象则会通过获取prefix、suffix 加载视图解析器需要的前缀和后缀,该参数的值是可以通过全局配置文件来指定前缀和后缀的,配置如下:

spring.mvc.view.prefix = # Spring MVC view prefix
spring.mvc.view.suffix = # Spring MVC view suffix

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值