SpringBoot源码分析(一)启动类注解

454 阅读4分钟

前言:

本文SpringBoot版本:2.5.12

SpringBoot

  • 约定大于配置
  • 减少繁琐的配置,开箱即用
@SpringBootApplication
public class DemoApplication {  
    public static void main(String[] args) {
          SpringApplication.run(DemoApplication.class, args);
    }         
}            

@SpringBootApplication

SpringBoot应用标注在某个类上,说明这个类是SpringBoot的主配置类,运行这个类的main方法启动SpringBoot应用

ps:项目中使用了@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})的写法。

exclude 排除此类的AutoConfig,即禁止 SpringBoot 自动注入数据源配置。

这是因为DataSourceAutoConfiguration.class 会自动查找 application.yml 或者 properties 文件里的 spring.datasource.* 相关属性并自动配置单数据源。

排查掉自动配置,通过@ConfigurationProperties("spring.datasource手动配置多个数据源。

我们继续查看@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 {}

其中前4个都是元注解,我们重点看后面的@EnableAutoConfiguration:

  • @Target(ElementType.TYPE):接口作用域
  • @Retention(RetentionPolicy.RUNTIME):生命周期,运行时有效
  • @Documented:文档注释
  • @Inherited:子类可以继承该注解

@SpringBootConfiguration:表示这是一个SpringBoot配置类,其实就是Configuration配置类。

@EnableAutoConfiguration

  • SpringBoot的核心注解,开启自动配置功能
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

@AutoConfigurationPackage 自动配置包

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}

他的核心是AutoConfigurationPackages.Registrar。

@Import(AutoConfigurationPackages.Registrar.class)默认将主配置类中(@SpringBootApplication)所在的包及其子包里面的所有组件扫描到Spring容器中。具体实现是如下代码:AutoConfigurationPackages

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
       //默认将会扫描@SpringBootApplication标注的主配置类所在的包及其子包下所有组件
      register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
   }

   @Override
   public Set<Object> determineImports(AnnotationMetadata metadata) {
      return Collections.singleton(new PackageImports(metadata));
   }

}

我们重点看这个registerBeanDefinitions方法。他最终会通过BeanDefinitionRegistry 调用registerBeanDefinition()方法

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
   if (registry.containsBeanDefinition(BEAN)) {
      BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
      beanDefinition.addBasePackages(packageNames);
   }
   else {
      registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
   }
}

BeanDefinitionRegistry 是一个接口,他有三个实现类

  • DefaultListableBeanFactory
  • GenericApplicationContext
  • SimpleBeanDefinitionRegistry

查看BeanDefinitionRegistry提供的方法,我们能知道他具有的主要能力有

  1. 以Map<String, BeanDefinition>的形式注册bean
  2. 根据beanName 删除和获取 beanDefiniation
  3. 得到持有的beanDefiniation的数目
  4. 根据beanName 判断是否包含beanDefiniation
public interface BeanDefinitionRegistry extends AliasRegistry {

        // 向beanFactory中注册一个BeanDefinition
        void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
                        throws BeanDefinitionStoreException;

        // 根据beanName从beanFactory中移除一个已经注册的BeanDefinition
        void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

        // 根据beanName从beanFactory中获取一个BeanDefinition
        BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

        // 判断beanFactory中是否有beanName的BeanDefinition
        boolean containsBeanDefinition(String beanName);

        // 获取beanFactory中的所有BeanDefinition的beanName
        String[] getBeanDefinitionNames();

        // 获取beanFactory中的BeanDefinition的数量
        int getBeanDefinitionCount();

        // 判断beanFactory中的beanName是否被占用
        boolean isBeanNameInUse(String beanName);
}

其中GenericApplicationContext我们可以发现其实他最终也是通过DefaultListableBeanFactory 调用的。

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
    private final DefaultListableBeanFactory beanFactory;
}

那么DefaultListableBeanFactory和SimpleBeanDefinitionRegistry的主要区别是什么呢?

SimpleBeanDefinitionRegistry: 通过代码,我们可以知道SimpleBeanDefinitionRegistry是一个非常简单的实现方式,没有内置的beanFactory。实际上这个类是提供给我们测试使用的,开发情况下Spring不会使用这个类

DefaultListableBeanFactory : 这个类非常重要,我们重点看这个类。它是BeanDefinitionResgitry接口的基本实现,实际上很多beanFactory最后都是调用DefaultListableBeanFactory类的registryBeanFactory来注册beanDefinition

分析完之后,我们查看最重要的registryBeanFactory()方法,也就是向beanFactory中注册BeanDefinition。

下面来看下这个方法到底做了哪些事情。其实也很简单。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
                implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
        ...
        /** Map of bean definition objects, keyed by bean name */
        private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
        // 保存所有的Bean名称
        /** List of bean definition names, in registration order */
        private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

        // 注册Bean定义信息~~
        @Override
        public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
                        throws BeanDefinitionStoreException {
                ...
                BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName);
                // 如果不为null,说明这个beanName对应的Bean定义信息已经存在了~~~~~
                if (oldBeanDefinition != null) {
                        // 是否允许覆盖(默认是true 表示允许的)
                        if (!isAllowBeanDefinitionOverriding()) {
                                // 抛异常
                        }
                        // 若允许覆盖  那还得比较下role  如果新进来的这个Bean的role更大                        
                        // 比如老的是ROLE_APPLICATION(0)  新的是ROLE_INFRASTRUCTURE(2) 
                        // 最终会执行到put,但是此处输出一个warn日志,告知你的bean被覆盖啦~~~~~~~(我们自己覆盖Spring框架内的bean显然就不需要warn提示了)
                        else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                                // 仅仅输出一个logger.warn
                        }
                        // 最终会执行put,但是内容还不相同  那就提醒一个info信息吧
                        else if (!beanDefinition.equals(oldBeanDefinition)) {
                                // 输出一个info信息
                        }
                        else {
                                // 输出一个debug信息
                        }
                        // 最终添加进去 (哪怕已经存在了~) 
                        // 从这里能看出Spring对日志输出的一个优秀处理,方便我们定位问题~~~
                        this.beanDefinitionMap.put(beanName, beanDefinition);
                        // 请注意:这里beanName并没有再add了,因为已经存在了  没必要了嘛
                }
                else {
                        // hasBeanCreationStarted:表示已经存在bean开始创建了(开始getBean()了吧~~~)
                        if (hasBeanCreationStarted()) {
                        // 注册过程需要synchronized,保证数据的一致性       
                                 synchronized (this.beanDefinitionMap) {
                                        this.beanDefinitionMap.put(beanName, beanDefinition); // 放进去
                                        List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                                        updatedDefinitions.addAll(this.beanDefinitionNames);
                                        updatedDefinitions.add(beanName);
                                        this.beanDefinitionNames = updatedDefinitions;
                        
                                        // 
                                        if (this.manualSingletonNames.contains(beanName)) {
                                                Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                                                updatedSingletons.remove(beanName);
                                                this.manualSingletonNames = updatedSingletons;
                                        }
                                }
                        }
                        else {
                                // Still in startup registration phase
                                // 表示仍然在启动  注册的状态~~~就很好处理了 put仅需,名字add进去
                                this.beanDefinitionMap.put(beanName, beanDefinition);
                                this.beanDefinitionNames.add(beanName);
                                
                                // 手动注册的BeanNames里面移除~~~ 因为有Bean定义信息了,所以现在不是手动直接注册的Bean单例~~~~
                                this.manualSingletonNames.remove(beanName);
                        }

                        // 这里的意思是:但凡你新增了一个新的Bean定义信息,之前已经冻结的就清空呗~~~
                        this.frozenBeanDefinitionNames = null;
                }
                
                // 最后异步很有意思:老的bean定义信息不为null(beanName已经存在了),或者这个beanName直接是一个单例Bean了~
                if (oldBeanDefinition != null || containsSingleton(beanName)) {
                        // 做清理工作:
                        // clearMergedBeanDefinition(beanName)
                        // destroySingleton(beanName);  销毁这个单例Bean  因为有了该bean定义信息  最终还是会创建的
                        // Reset all bean definitions that have the given bean as parent (recursively).  处理该Bean定义的getParentName  有相同的也得做清楚  所以这里是个递归
                        resetBeanDefinition(beanName);
                }
        }

        @Override
        public void removeBeanDefinition(String beanName) throws                 NoSuchBeanDefinitionException {
                // 移除整体上比较简单:beanDefinitionMap.remove
                // beanDefinitionNames.remove
                // resetBeanDefinition(beanName);

                BeanDefinition bd = this.beanDefinitionMap.remove(beanName);
                // 这里发现移除,若这个Bean定义本来就不存在,事抛异常,而不是返回null 需要注意~~~~
                if (bd == null) {
                        throw new NoSuchBeanDefinitionException(beanName);
                }

                if (hasBeanCreationStarted()) {
                        synchronized (this.beanDefinitionMap) {
                                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames);
                                updatedDefinitions.remove(beanName);
                                this.beanDefinitionNames = updatedDefinitions;
                        }
                } else {
                        this.beanDefinitionNames.remove(beanName);
                }
                this.frozenBeanDefinitionNames = null;
                resetBeanDefinition(beanName);
        }

        // 这个实现非常的简单,直接从map里拿
        @Override
        public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
                BeanDefinition bd = this.beanDefinitionMap.get(beanName);
                if (bd == null) {
                        throw new NoSuchBeanDefinitionException(beanName);
                }
                return bd;
        }

@Override
        public boolean containsBeanDefinition(String beanName) {
                return this.beanDefinitionMap.containsKey(beanName);
        }
        @Override
        public int getBeanDefinitionCount() {
                return this.beanDefinitionMap.size();
        }
        @Override
        public String[] getBeanDefinitionNames() {
                String[] frozenNames = this.frozenBeanDefinitionNames;
                if (frozenNames != null) {
                        return frozenNames.clone();
                }
                else {
                        return StringUtils.toStringArray(this.beanDefinitionNames);
                }
        }
        
        // ==========这个方法非常有意思:它是间接的实现的===============
        // 因为BeanDefinitionRegistry有这个方法,而它的父类AbstractBeanFactory也有这个方法,所以一步小心,就间接的实现了这个接口方法
        public boolean isBeanNameInUse(String beanName) {
                // 增加了一个hasDependentBean(beanName);  或者这个BeanName是依赖的Bean 也会让位被使用了
                return isAlias(beanName) || containsLocalBean(beanName) || hasDependentBean(beanName);
        }
}

通过代码分析,我们可以得到一个beanDefinitionMap。他是一个ConcurrentHashMap能保证线程安全,没错,这个就是我们常说的Spring的IOC容器。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);他的Key是String类型的,就是我们常说的Bean的名称,Value就是对象本身,还包含对象的属性:是否单例,是否懒加载等。所以封装成了BeanDefinition对象。BeanDefinition存储着真正的对象,以及对象的相关属性。

到这里,我们跟完了@AutoConfigurationPackage,知道了这里会注册Bean放入IOC容器中。

接着我们重新查看@EnableAutoConfiguration注解的@Import(AutoConfigurationImportSelector.class)

AutoConfigurationImportSelector.class又是我们需要重点关注的一个地方,这个类就是SpringBoot能够自动装配的原因。

我们主要看selectImports()方法

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   // 判断SpringBoot是否开启自动配置
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   // 获取需要被引入的自动配置信息
   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
   // 判断是否开启自动配置
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   // 获取@EnableAutoConfiguration注解的属性
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   // 从spring.factories文件中获取配置类的全限定名数组
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   // 去重
   configurations = removeDuplicates(configurations);
   // 获取注解中exclude或excludeName排除的类集合
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   // 检查被排除类是否可以实例化,是否被自动配置所使用,否则抛出异常
   checkExcludedClasses(configurations, exclusions);
   // 去除被排除的类
   configurations.removeAll(exclusions);
   // 使用spring.factories配置文件中配置的过滤器对自动配置类进行过滤
   configurations = getConfigurationClassFilter().filter(configurations);
   // 抛出事件
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

再继续深入源码,我们重点看getCandidateConfigurations(annotationMetadata, attributes)方法。

最终来到SpringFactoriesLoader的loadSpringFactories。

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

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 {
      //从类路径的META-INF/spring.factories中加载所有默认的自动配置类
      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()) {
             //获取EnableAutoConfiguration指定的所有值,也就是EnableAutoConfiguration.class的值
            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;
}

通过这段代码,我们知道了SpringBoot启动的时候,会从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,并将这些值作为自动配置类导入到容器中,自动配置类就会生效,最后完成自动配置工作。

EnableAutoConfiguration默认在spring-boot-autoconfigure这个包中,如下图。

这里附上一张自动装配的流程图。

到现在,我们重点分析了 @EnableAutoConfiguration,这个注解的主要作用就是开启自动装配。

而这个注解我们又主要关注两点:

  1. @AutoConfigurationPackage:这个里面我们主要关注registerBeanDefinition,注册Bean信息,存入IOC容器中。
  2. @Import(AutoConfigurationImportSelector.class):加载配置文件。扫描读取配置文件META-INF/spring.factories。

总结:

通过上文的学习,我们已经对SpringBoot启动项上的注解做的事情有了详细的了解,我们主要分析了将Bean注册到IOC容器中的方法,以及SpringBoot的自动装配。接下来的章节,我们继续分析SpringBoot的启动过程到底是怎么样的。

巨人的肩膀

www.cnblogs.com/java-chen-h…

blog.csdn.net/weixin_4388…

blog.csdn.net/xiaojie_570…

blog.csdn.net/f641385712/…

juejin.cn/post/703552…

mp.weixin.qq.com/s/f6oED1hbi…

spring.io/quickstart