欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring @Bean实例的初始化

程序员文章站 2022-03-03 11:25:06
...

简介

我们都知道Spring有很多种创建bean的方式,包括使用@Component, @Service等注解,包括实现ImportSelector或ImportBeanDefinitionRegistrar接口,也可以调用AnnotationConfigApplicationContext#register手动注册bean,也可以在@Configuration类里定义bean。那么今天我们要说的就是在@Configuration配置类里@Bean实例化的原理。

首先来看下通常情况下在@Configuration类里配置@Bean的一个示例:

@Configuration
@EnableCaching
public class CachingConfig {

    @Bean
    public RedisCacheManager redisCacheManager(RedisTemplate<Object, Object> redisTemplate) {
        RedisTemplate<Object, Object> newRedisTemplate = new RedisTemplate<>();
        newRedisTemplate.setConnectionFactory(redisTemplate.getConnectionFactory());

        RedisCacheManager redisCacheManager = new RedisCacheManager(newRedisTemplate);
        redisCacheManager.setUsePrefix(true);
        redisCacheManager.setDefaultExpiration(600);
        return redisCacheManager;
    }
}

在这里@Configuration注解即标记这个类是一个配置类,在Spring启动的时候会读取这个类里面的bean定义并生成实例。

那么它是在哪加载bean,又是在哪去实例化的呢?

SpringBoot启动流程简介

首先我们来梳理下SpringBoot的一个启动流程。

入口在主类的main方法中:

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

创建environment

然后到达SpringApplication#run方法

首先是创建environment实例,在这里会加载一些初始的配置以及该使用哪个profile

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

其次在prepareEnvironment方法中会切入Spring Cloud的context的初始化,并且为SpringApplication实例添加AncestorInitializer初始化器,这样在SpringBoot的context初始化的时候会把SpringCoud的context设置为自己的父容器。

那么具体是在哪衔接起来的呢?

原来在prepareEnvironment方法里会发布ApplicationEnvironmentPreparedEvent事件,而SpringCloud在启动时会加载一个监听器为BootstrapApplicationListener,在这里会进行SpringCloud context的初始化。

private ConfigurableEnvironment prepareEnvironment(
      SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // Create and configure the environment
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   // 这里即是重点
   listeners.environmentPrepared(environment);
   if (!this.webEnvironment) {
      environment = new EnvironmentConverter(getClassLoader())
            .convertToStandardEnvironmentIfNecessary(environment);
   }
   return environment;
}

在Spring-cloud-context的spring.factories文件中有这么一行,因此这个listener会被加载到容器中。

org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener

创建context

其次创建ApplicationContext,然后做一些基本的填充工作

context = createApplicationContext();
prepareContext(context, environment, listeners, applicationArguments, printedBanner);

在创建ApplicationContext的时候就会加载一些Spring内置的bean,如及其重要的ConfigurationClassPostProcessor。

创建web application context的时候会创建的context类是org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext。

这个类的构造器如下

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

它会创建一个AnnotatedBeanDefinitionReader实例,这个类的构造器如下:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
   Assert.notNull(environment, "Environment must not be null");
   this.registry = registry;
   this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
   AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

最重要的就是最后一行,它会加载一些内置的类,最终调用的方法是registerAnnotationConfigProcessors,方法有点长,只贴出最重要的一行,在这里会注册bean ConfigurationClassPostProcessor,而这就是后面进行bean加载解析的基础。

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
      BeanDefinitionRegistry registry, Object source) {
    // ... 省略
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
       RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
       def.setSource(source);
       beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    // ... 省略
}

加载主类

private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {

   // 应用初始化器,如设置SpringCloud context为当前context父容器
   applyInitializers(context);

   // 获取主类并加载入registry
   Set<Object> sources = getSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[sources.size()]));
   listeners.contextLoaded(context);
}

刷新context

准备完context后就会进行context的刷新

refreshContext(context);

其实刷新context主要分为两个步骤,一个是加载bean定义,一个是bean的实例化(除了懒加载的bean,否则都是提前实例化的)。那么我们这里为了突出重点,就按照@Bean注解标记的方法被加载到registry中这条主要路径来解析。

然后会进入到AbstractApplicationContext#refresh方法,随即进入invokeBeanFactoryPostProcessors方法。

public void refresh() throws BeansException, IllegalStateException {

 // Invoke factory processors registered as beans in the context.
 invokeBeanFactoryPostProcessors(beanFactory);
}

最终会调用到方法PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, java.util.List<BeanFactoryPostProcessor> beanFactoryPostProcessors)

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

   // Invoke BeanDefinitionRegistryPostProcessors first, if any.
   Set<String> processedBeans = new HashSet<String>();

   if (beanFactory instanceof BeanDefinitionRegistry) {
      BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
      List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>();
      List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<BeanDefinitionRegistryPostProcessor>();

      for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
         if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
            BeanDefinitionRegistryPostProcessor registryProcessor =
                  (BeanDefinitionRegistryPostProcessor) postProcessor;
            registryProcessor.postProcessBeanDefinitionRegistry(registry);
            registryProcessors.add(registryProcessor);
         }
         else {
            regularPostProcessors.add(postProcessor);
         }
      }

      // 在这里会调用到ConfigurationClassPostProcessor
      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
      currentRegistryProcessors.clear();
}

    // 调用beanFactoryPostProcessor
}

因此,实际调用的是invokeBeanDefinitionRegistryPostProcessors

ConfigurationClassPostProcessor解析主类

这里会进入到方法ConfigurationClassPostProcessor#processConfigBeanDefinitions

首先会拿到配置类,因为之前加载了主类,而主类也是一个配置类,所以这里configCandidates里会有主类。

for (String beanName : candidateNames) {
   BeanDefinition beanDef = registry.getBeanDefinition(beanName);
   if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
         ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
      if (logger.isDebugEnabled()) {
         logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
      }
   }
   else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
      configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
   }
}

然后创建Configuration类解析器,并进行解析,这里会加载所有的类路径下的类

ConfigurationClassParser parser = new ConfigurationClassParser(
      this.metadataReaderFactory, this.problemReporter, this.environment,
      this.resourceLoader, this.componentScanBeanNameGenerator, registry);
      
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
parser.parse(candidates);  

对于每一个类都会创建ConfigurationClass对象,并且如果有@Bean标记的方法,则加入到ConfigurationClass的属性beanMethods中。

final class ConfigurationClass {
   private final Set<BeanMethod> beanMethods = new LinkedHashSet<BeanMethod>();
}   

然后通过ConfigurationClassBeanDefinitionReader来处理配置类,这里会把所有解析到的类都放到configClasses中并进行读取。

Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);

// Read the model and create bean definitions based on its content
if (this.reader == null) {
   this.reader = new ConfigurationClassBeanDefinitionReader(
         registry, this.sourceExtractor, this.resourceLoader, this.environment,
         this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);

加载配置类的bean定义

下面就进入了Configuration类的bean定义读取

ConfigurationClassBeanDefinitionReader

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
   TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
   for (ConfigurationClass configClass : configurationModel) {
      loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
   }
}


private void loadBeanDefinitionsForConfigurationClass(
      ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
   // 省略       
   // 这里是重点,处理配置类的@Bean标记的方法
   for (BeanMethod beanMethod : configClass.getBeanMethods()) {
      loadBeanDefinitionsForBeanMethod(beanMethod);
   }

    // 省略
}

loadBeanDefinitionsForBeanMethod定义如下,这里会为每个@Bean注解标记的方法创建一个bean定义容器, 类型为ConfigurationClassBeanDefinition,并加入到注册表registry中。

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {

   // 对于每一个被@Bean标记的方法,创建ConfigurationClassBeanDefinition对象
   ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
   beanDef.setResource(configClass.getResource());
   beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

   if (metadata.isStatic()) {
      // static @Bean method
      beanDef.setBeanClassName(configClass.getMetadata().getClassName());
      beanDef.setFactoryMethodName(methodName);
   }
   else {
      // 走这条分支  
      // instance @Bean method
      beanDef.setFactoryBeanName(configClass.getBeanName());
      beanDef.setUniqueFactoryMethodName(methodName);
   }
   beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
   beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);

   AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
 
   // 省略部分代码
   // 注册@Bean方法定义的bean
   this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

到此@Bean注解标记的类就被放入registry中了。

@Bean标记方法调用

那么调用初始化的流程是什么呢?

首先在我们都知道Bean实例化的时候调用AbstractBeanFactory#getBean方法。

注意,走到这里既可能是因为bean自己的提前实例化,也可能是因为被其他bean依赖从而实例化。

AbstractBeanFactory

public Object getBean(String name) throws BeansException {
   return doGetBean(name, null, null, false);
}

AbstractBeanFactory

protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {
    // 省略..      
    if (mbd.isSingleton()) {
       sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
          @Override
          public Object getObject() throws BeansException {
             try {
                return createBean(beanName, mbd, args);
             }
             catch (BeansException ex) {
                // Explicitly remove instance from singleton cache: It might have been put there
                // eagerly by the creation process, to allow for circular reference resolution.
                // Also remove any beans that received a temporary reference to the bean.
                destroySingleton(beanName);
                throw ex;
             }
          }
       });
       bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    // 省略..
} 

然后调用createBean方法

AbstractAutowireCapableBeanFactory#createBean

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
   // 省略.. 
   Object beanInstance = doCreateBean(beanName, mbdToUse, args);
   if (logger.isDebugEnabled()) {
      logger.debug("Finished creating instance of bean '" + beanName + "'");
   }
   return beanInstance;
}

doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      // 调用具体方法创建bean 
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   // ...
} 

这里重点来了,记得上文设置bean的factoryMethod为Configuration类的对应方法吗,这里会反射调用,具体方法即为instantiateUsingFactoryMethod。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
   // Make sure bean class is actually resolved at this point.
   Class<?> beanClass = resolveBeanClass(mbd, beanName);

   if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
   }

   // 走这里
   if (mbd.getFactoryMethodName() != null)  {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }
   // ...
}   


protected BeanWrapper instantiateUsingFactoryMethod(
      String beanName, RootBeanDefinition mbd, Object[] explicitArgs) {

   return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}

ConstructorResolver#instantiateUsingFactoryMethod

   // 找到具体的方法factoryMethodToUse
   Object beanInstance;

   if (System.getSecurityManager() != null) {
      final Object fb = factoryBean;
      final Method factoryMethod = factoryMethodToUse;
      final Object[] args = argsToUse;
      beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
         @Override
         public Object run() {
            return beanFactory.getInstantiationStrategy().instantiate(
                  mbd, beanName, beanFactory, fb, factoryMethod, args);
         }
      }, beanFactory.getAccessControlContext());
   }
   else {
      // 走这里 
      beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(
            mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse);
   }

   if (beanInstance == null) {
      return null;
   }
   bw.setBeanInstance(beanInstance);
   return bw;

最终反射调用方法

SimpleInstantiationStrategy

@Override
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner,
      Object factoryBean, final Method factoryMethod, Object... args) {

   try {
      if (System.getSecurityManager() != null) {
         AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
               ReflectionUtils.makeAccessible(factoryMethod);
               return null;
            }
         });
      }
      else {
         ReflectionUtils.makeAccessible(factoryMethod);
      }

      Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
      try {
         currentlyInvokedFactoryMethod.set(factoryMethod);
         // 这里进行方法调用并返回
         return factoryMethod.invoke(factoryBean, args);
      }
      finally {
         if (priorInvokedFactoryMethod != null) {
            currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
         }
         else {
            currentlyInvokedFactoryMethod.remove();
         }
      }
   }
   catch (IllegalArgumentException ex) {
      throw new BeanInstantiationException(factoryMethod,
            "Illegal arguments to factory method '" + factoryMethod.getName() + "'; " +
            "args: " + StringUtils.arrayToCommaDelimitedString(args), ex);
   }
   catch (IllegalAccessException ex) {
      throw new BeanInstantiationException(factoryMethod,
            "Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex);
   }
   catch (InvocationTargetException ex) {
      String msg = "Factory method '" + factoryMethod.getName() + "' threw exception";
      if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory &&
            ((ConfigurableBeanFactory) owner).isCurrentlyInCreation(bd.getFactoryBeanName())) {
         msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " +
               "declaring the factory method as static for independence from its containing instance. " + msg;
      }
      throw new BeanInstantiationException(factoryMethod, msg, ex.getTargetException());
   }
}

那么到这里,完整的bean定义加载及实例化就完成了,后面会进行一些其他的Spring常规处理。