SpringBoot(核心自动配置原理)

一、 解析主类 — 注解

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = "com.wangxing.springboot")
public class Springbootdemo1Application {
    public static void main(String[] args) {
  	SpringApplication.run(Springbootdemo1Application.class, args);
    }
}

1.1 @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 {
........
}

  @SpringBootApplication实际上它是一个复合注解。

  虽然它的定义使用了多个注解,但实际上对于 SpringBoot 应用来说,重要的只有三个注解,如下:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

  所以,如果我们使用上面三个主解作用在主类上整个 SpringBoot 应用依然可以与之前的启动类功能对等。

package com.wangxing.springboot.springbootdemo2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public class Springbootdemo2Application {
    public static void main(String[] args) {
        SpringApplication.run(Springbootdemo2Application.class, args);
    }
}

  每次都写三个注解显然过于繁琐,所以写一个 @SpringBootApplication 这样的一站式复合注解显然更方便。

1.2 @SpringBootConfiguration注解 [创建配置类从而代替配置文件]

  @SpringBootConfiguration注解中包含了@Configuration。
  @Configuration — 是 Spring JavaConfig 形式的 Spring IoC 容器的配置类使用的注解。
  所以 SpringBoot 应用本质就是 Spring 应用,那自然也需要加载某个 IoC 容器的配置,而 SpringBoot 社区推荐使用基于 JavaConfig 的配置形式,所以,这里的启动类标注了 @Configuration 之后,表示其本身也是一个 IoC 容器的配置类。

  Spring JavaConfig 形式的 Spring IoC 容器的配置类
  配置方式

  (1)基于 XML 的配置方式。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean定义 -->

</beans>

  (2)基于 JavaConfig 的配置方式

@Configuration
public class MyConfiguration{
// bean定义
} 

  任何一个标注了 @Configuration 的 Java 类定义都是一个 JavaConfig 配置类。

  注册 bean 定义

  1.基于 XML 的注册 bean 定义方式。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean定义 -->
<bean id=””  class=””></bean>
</beans>

  2.基于JavaConfig 的注册 bean 定义方式。

@Configuration
public class MyConfiguration{
// bean定义
 	@Bean
    public StudentService  studentService() {
        return new StudentServiceImpl();
    }
} 

  任何一个标注了 @Bean 的方法,其返回值将作为一个 bean 定义注册到 Spring 的 IoC 容器,方法名将默认成为该 bean 定义的 id。

  依赖注入的配置方式

  1. 基于 XML 的依赖注入方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean定义 -->
<bean id=””  class=””>
<property name="studentDao" ref="studentDao" />
</bean>
</beans>

  2.基于JavaConfig 的依赖注入方式。

@Configuration
public class MyConfiguration{
// bean定义
 	@Bean
    public StudentService  studentService() {
        return new StudentServiceImpl(studentDao() );
}

	@Bean
    public  StudentDao  studentDao() {
        return new StudentDaoImpl();
}
} 

如果一个 bean 的定义依赖其他 bean,则直接调用对应 JavaConfig 类中依赖 bean 的创建方法就可以了。

@SpringBootApplication中包含了@SpringBootConfiguration注解,@SpringBootConfiguration注解中包含了@Configuration注解,@Configuration注解标注所在java类上,表示这个java类就是一个 JavaConfig 配置类,这个JavaConfig 配置类可以代替掉Spring配置文件【applicationContext.xml】,当我们在主类上标注@SpringBootApplication时,意味着主类是一个JavaConfig 配置类,因此我们在创建SptingBoot项目的时候才不用去编写Spring配置文件【applicationContext.xml】。

1.3 @EnableAutoConfiguration [“智能”的完成自动配置]

  @EnableAutoConfiguration 中包含了@Import注解
  @Import注解将bean对象注册到javaConfig配置类中。

  @EnableAutoConfiguration注解利用@Import注解收集和注册特定模块相关的 bean 定义

  源码:

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

  通过@Import(AutoConfigurationImportSelector.class).借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration 可以帮助 SpringBoot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器。
在这里插入图片描述

  借助于 Spring 框架原有的一个工具类:SpringFactoriesLoader 的支持,@EnableAutoConfiguration 可以“智能”地自动配置功效才得以大功告成!

  在@Import(AutoConfigurationImportSelector.class)的AutoConfigurationImportSelector.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;
}

1.4 SpringFactoriesLoader类

  SpringFactoriesLoader类的主要作用是通过类路径下的META-INF/spring.factories文件获取工厂类接口的实现类,初始化并保存在缓存中,以供Springboot启动过程中各个阶段的调用。

public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
   Assert.notNull(factoryType, "'factoryType' must not be null");
   ClassLoader classLoaderToUse = classLoader;
   if (classLoaderToUse == null) {
      classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
   }
   List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
   if (logger.isTraceEnabled()) {
      logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
   }
   List<T> result = new ArrayList<>(factoryImplementationNames.size());
   for (String factoryImplementationName : factoryImplementationNames) {
      result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
   }
   AnnotationAwareOrderComparator.sort(result);
   return result;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
   ClassLoader classLoaderToUse = classLoader;
   if (classLoaderToUse == null) {
      classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
   }
   String factoryTypeName = factoryType.getName();
   return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
   Map<String, List<String>> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   result = new HashMap<>();
   try {
      Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryTypeName = ((String) entry.getKey()).trim();
            String[] factoryImplementationNames =
                  StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
            for (String factoryImplementationName : factoryImplementationNames) {
               result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                     .add(factoryImplementationName.trim());
            }
         }
      }
      // Replace all lists with unmodifiable lists containing unique elements
      result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
            .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
      cache.put(classLoader, result);
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
   return result;
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
spring-boot-autoconfigure-2.4.0.jar/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
...........

二、 SpringBoot核心自动配置原理

  
  1)SpringBoot启动时会加载大量写好的自动配置类

  2)然后看我们需要的功能有没有SpringBoot默认写好的自动配置类

  3)接着看这个自动配置类中到底配置了哪些组件(只要我们要用的组件有,我们就不需要再来配置,如果没有就需要自己写一个自动配置类)

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

    xxxAutoConfiguration:自动配置类

    xxxProperties:封装配置文件中相关属性

  Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。

@ComponentScan

  【配置自动扫描包,就可以让类上带有@Component 和 @Repository,@Service注解的java类创建出对象】
@ComponentScan 对应 XML 配置形式中的 <context:component-scan> 元素,用于配合一些元信息注解,比如 @Component 和 @Repository 等,将标注了这些元信息注解的 bean 定义类批量采集到 Spring 的 IoC 容器中。我们可以通过 basePackages 等属性来细粒度地定制 @ComponentScan 自动扫描的范围,如果不指定,则默认 Spring 框架实现会从声明 @ComponentScan 所在类的 package 进行扫描。
  如果我们当前应用没有任何 bean 定义需要通过 @ComponentScan 加载到当前 SpringBoot 应用对应使用的 IoC 容器,那么,除去 @ComponentScan 的声明,当前 SpringBoot 应用依然可以照常运行,功能对等。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值