美文网首页
Springboot启动扫描包的原理

Springboot启动扫描包的原理

作者: WAHAHA402 | 来源:发表于2019-05-17 11:59 被阅读0次

参考链接1
参考链接2
所参照代码为Springboot2.1.1

​ 默认情况下,扫描范围是主类xxxApplication所在包及其子目录,可以在后面的具体实现中看到。

​ 从主类中的SpringApplication.run(xxxApplication.class, args);一直点击进入run方法的实现,这里可以看到run方法里有几个关于context的方法分别是:

  • createApplicationContext()

  • prepareContext(xxx,xx)

  • refreshContext(context)

public ConfigurableApplicationContext run(String... args) {
        ...
        try {
            ...
            context = createApplicationContext();
            ...
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            refreshContext(context);
            ....
        catch (Throwable ex) {
        
        }

逐个分析:

1.createApplicationContext()

这个方法返回一个类型为AnnotationConfigServletWebServerApplicationContext的context,可以点进去看到这个class为AnnotationConfigServletWebServerApplicationContext类型。

protected ConfigurableApplicationContext createApplicationContext() {
        ...
                case SERVLET:
                    contextClass = Class.forName(**DEFAULT_SERVLET_WEB_CONTEXT_CLASS**);
                    break;
                case ...
                }
            }
            ....
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

该类的一个父类GenericApplicationContext中,创建了一个beanFactory,这个beanFactory实现了BeanDefinitionRegistry的接口,后面会用到。

public GenericApplicationContext() {
   this.beanFactory = new DefaultListableBeanFactory();
}

createApplicationContext返回的语句中调用了AnnotationConfigServletWebServerApplicationContext类的构造函数,

public AnnotationConfigServletWebServerApplicationContext() {
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

进入构造函数中的第一个AnnotatedBeanDefinitionReader,一直点到registerAnnotationConfigProcessors的方法:

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
      BeanDefinitionRegistry registry, @Nullable Object source) {
   //创建了一个beanFactory
   DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
   if (beanFactory != null) {
      .....
   }

   Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

   if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
      def.setSource(source);
       //添加了一个类型为CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME的beanDefinition,下面一段代码可以看到它的名字为internalConfigurationAnnotationProcessor,(ConfigurationClassPostProcessor.class)为类型的beanDefinition 这个后面会用到
      beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
   }

   ....
   }
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
      "org.springframework.context.annotation.internalConfigurationAnnotationProcessor";

接着进入到构造函数中的new ClassPathBeanDefinitionScanner(),

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
      Environment environment, @Nullable ResourceLoader resourceLoader) {
    ....
    //入口处默认useDefaultFilters为true
   if (useDefaultFilters) {
      registerDefaultFilters();
   }
   setEnvironment(environment);
   setResourceLoader(resourceLoader);
}

进入registerDefaultFilters方法,

protected void registerDefaultFilters() {
    //这里把Component.class作为默认的includeFilter加入了过滤器
   this.includeFilters.add(new AnnotationTypeFilter(Component.class));
   ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
   try {
      ......
}

2.prepareContext()

prepareContext()方法里有个load函数,进入load函数的具体实现,

private int load(Object source) {
   Assert.notNull(source, "Source must not be null");
   if (source instanceof Class<?>) {
      return load((Class<?>) source);
   }
   if (source instanceof Resource) {
      return load((Resource) source);
   }
   if (source instanceof Package) {
      ....
}

因为主类的run方法传入的是xxxxApplication.class,所以这里走第一个分支

private int load(Class<?> source) {
   .....
   //isComponent里面判断是否有Component的注解,启动类是有的,所以这里将启动类加载进来了
   if (isComponent(source)) {
      this.annotatedReader.register(source);
      return 1;
   }
   return 0;
}

3.refreshContext()

点击进入refresh的具体实现,看到一个invokeBeanFactoryPostProcessors的方法

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         //这里写着注册bean
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         initMessageSource();

         ....

进入其实现,这里传入的beanFactory的就是1.createApplicationContext()中第一个方法创建的的beanFactory

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

   .....
}

进入这个方法,因为1.createApplicationContext中创建的beanFactory实现了BeanDefinitionRegistry接口,所以我们进入这个分支看一看,

public static void invokeBeanFactoryPostProcessors(
      ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

   Set<String> processedBeans = new HashSet<>();
    //BeanDefinitionRegistry
   if (beanFactory instanceof BeanDefinitionRegistry) {
      .....
      for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
      //BeanDefinitionRegistryPostProcessor
         if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
            BeanDefinitionRegistryPostProcessor registryProcessor =
                  (BeanDefinitionRegistryPostProcessor) postProcessor;
            registryProcessor.postProcessBeanDefinitionRegistry(registry);
            .....
      }

还是1.createApplicationContext()中创建的ConfigurationClassPostProcessor类,刚好实现了BeanDefinitionRegistryPostProcessor,所以这里我们直接看ConfigurationClassPostProcessor累的postProcessBeanDefinitionRegistry方法,可以看到这里调用了processConfigBeanDefinitions方法,

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
   ....

   processConfigBeanDefinitions(registry);
}

进入这个方法:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   .....

   Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
   Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
   do {
      parser.parse(candidates);
      parser.validate();

      .....
      }

进入parse(candidates),这里再前面创建ConfigurationClassPostProcessor的时候给它生成了一个Holder,而这个beanDefinition的类型就是AnnotatedBeanDefinition,所以走这个分支的parse方法

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
          //这个分支
         if (bd instanceof AnnotatedBeanDefinition) {
            parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
         }
         else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
           ....
}

一路点下去,进入doProcessConfigurationClass方法,发现这里就是用ComponentScan来解析bean对象的

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {
    .....

   // Process any @ComponentScan annotations
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      for (AnnotationAttributes componentScan : componentScans) {
         // 进入这个方法
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
         
      }
   }

在这个parse方法的最后,可以看到basePackages.isEmpty()的判断,当前这个basePackages就是空的,因为没有配置@ComponentScan的这个属性,这里就会给basePackages添加这个启动类的名字的最后一个点之前的路径,比如启动类是A.B.C.D.class, 那么路径就是A.B.C.D,加入它所在的包名

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
   ....
   if (basePackages.isEmpty()) {
      basePackages.add(ClassUtils.getPackageName(declaringClass));
   }

   scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
      @Override
      protected boolean matchClassName(String className) {
         return declaringClass.equals(className);
      }
   });
   return scanner.doScan(StringUtils.toStringArray(basePackages));
}

最后进入doScan方法,这里有个findCandidateComponents的方法,得到这些候选的bean就会注册到Spring容器中。

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   ....
   for (String basePackage : basePackages) {
       //这个方法
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         ....
   return beanDefinitions;
}

最后进入scanCandidateComponents方法,这里Spring会通过传入的路径来遍历下面的每一个class,这个方法所在类为ClassPathScanningCandidateComponentProvider,不过所有的basePackage上面那个方法(所在类ClassPathBeanDefinitionScanner)打断点查看。

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
      boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();
      for (Resource resource : resources) {
         if (traceEnabled) {
            logger.trace("Scanning " + resource);
         }
         if (resource.isReadable()) {
            try {
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                //这里判断了是否是Component.class
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     .....
         }

由于上面提到过的默认的includeFilters中加入了Componet.class,所以在启动类的同级目录下,添加Component注解及其子注解的类会被加入到Spring容器中。

/**
     * Determine whether the given class does not match any exclude filter
     * and does match at least one include filter.
     * @param metadataReader the ASM ClassReader for the class
     * @return whether the class qualifies as a candidate component
     */
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
   for (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return false;
      }
   }
   for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}

结论

最终实现类路径组件扫描的是ClassPathScanningCandidateComponentProvider类下的scanCandidateComponents方法,查看全部扫描包可以在它的上级调用方法ClassPathBeanDefinitionScanner类的doScan(String... basePackages)方法打断点查看。

相关文章

网友评论

      本文标题:Springboot启动扫描包的原理

      本文链接:https://www.haomeiwen.com/subject/bmvnaqtx.html