当前位置:   article > 正文

谈起SpringBoot,面试官(Java)必问题:讲述一下 SpringBoot 自动装配原理?

谈起SpringBoot,面试官(Java)必问题:讲述一下 SpringBoot 自动装配原理?

SpringBoot自动配置原理是什么?

面试过程中问得最多的可能是自动装配的原理,而自动装配是在启动过程中完成,只不过在刚开始的时候我们选择性的跳过了,下面详细讲解自动装配的过程。

推荐学习:谈起SpringBoot,面试官(Java)必问题:讲述一下 SpringBoot 自动装配原理?

1、在springboot的启动过程中,有一个步骤是创建上下文,如果不记得可以看下面的代码:

  1. public ConfigurableApplicationContext run(String... args) {
  2. StopWatch stopWatch = new StopWatch();
  3. stopWatch.start();
  4. ConfigurableApplicationContext context = null;
  5. Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  6. configureHeadlessProperty();
  7. SpringApplicationRunListeners listeners = getRunListeners(args);
  8. listeners.starting();
  9. try {
  10. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  11. ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  12. configureIgnoreBeanInfo(environment);
  13. Banner printedBanner = printBanner(environment);
  14. context = createApplicationContext();
  15. exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
  16. new Class[] { ConfigurableApplicationContext.class }, context);
  17. //此处完成自动装配的过程
  18. prepareContext(context, environment, listeners, applicationArguments, printedBanner);
  19. refreshContext(context);
  20. afterRefresh(context, applicationArguments);
  21. stopWatch.stop();
  22. if (this.logStartupInfo) {
  23. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  24. }
  25. listeners.started(context);
  26. callRunners(context, applicationArguments);
  27. }
  28. catch (Throwable ex) {
  29. handleRunFailure(context, ex, exceptionReporters, listeners);
  30. throw new IllegalStateException(ex);
  31. }
  32. try {
  33. listeners.running(context);
  34. }
  35. catch (Throwable ex) {
  36. handleRunFailure(context, ex, exceptionReporters, null);
  37. throw new IllegalStateException(ex);
  38. }
  39. return context;
  40. }

2、在prepareContext方法中查找load方法,一层一层向内点击,找到最终的load方法

  1. //prepareContext方法
  2. private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
  3. SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
  4. context.setEnvironment(environment);
  5. postProcessApplicationContext(context);
  6. applyInitializers(context);
  7. listeners.contextPrepared(context);
  8. if (this.logStartupInfo) {
  9. logStartupInfo(context.getParent() == null);
  10. logStartupProfileInfo(context);
  11. }
  12. // Add boot specific singleton beans
  13. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  14. beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
  15. if (printedBanner != null) {
  16. beanFactory.registerSingleton("springBootBanner", printedBanner);
  17. }
  18. if (beanFactory instanceof DefaultListableBeanFactory) {
  19. ((DefaultListableBeanFactory) beanFactory)
  20. .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
  21. }
  22. if (this.lazyInitialization) {
  23. context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
  24. }
  25. // Load the sources
  26. Set<Object> sources = getAllSources();
  27. Assert.notEmpty(sources, "Sources must not be empty");
  28. //load方法完成该功能
  29. load(context, sources.toArray(new Object[0]));
  30. listeners.contextLoaded(context);
  31. }
  32. /**
  33. * Load beans into the application context.
  34. * @param context the context to load beans into
  35. * @param sources the sources to load
  36. * 加载bean对象到context中
  37. */
  38. protected void load(ApplicationContext context, Object[] sources) {
  39. if (logger.isDebugEnabled()) {
  40. logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
  41. }
  42. //获取bean对象定义的加载器
  43. BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
  44. if (this.beanNameGenerator != null) {
  45. loader.setBeanNameGenerator(this.beanNameGenerator);
  46. }
  47. if (this.resourceLoader != null) {
  48. loader.setResourceLoader(this.resourceLoader);
  49. }
  50. if (this.environment != null) {
  51. loader.setEnvironment(this.environment);
  52. }
  53. loader.load();
  54. }
  55. /**
  56. * Load the sources into the reader.
  57. * @return the number of loaded beans
  58. */
  59. int load() {
  60. int count = 0;
  61. for (Object source : this.sources) {
  62. count += load(source);
  63. }
  64. return count;
  65. }

3、实际执行load的是BeanDefinitionLoader中的load方法,如下:

  1. //实际记载bean的方法
  2. private int load(Object source) {
  3. Assert.notNull(source, "Source must not be null");
  4. //如果是class类型,启用注解类型
  5. if (source instanceof Class<?>) {
  6. return load((Class<?>) source);
  7. }
  8. //如果是resource类型,启动xml解析
  9. if (source instanceof Resource) {
  10. return load((Resource) source);
  11. }
  12. //如果是package类型,启用扫描包,例如@ComponentScan
  13. if (source instanceof Package) {
  14. return load((Package) source);
  15. }
  16. //如果是字符串类型,直接加载
  17. if (source instanceof CharSequence) {
  18. return load((CharSequence) source);
  19. }
  20. throw new IllegalArgumentException("Invalid source type " + source.getClass());
  21. }

4、下面方法将用来判断是否资源的类型,是使用groovy加载还是使用注解的方式

  1. private int load(Class<?> source) {
  2. //判断使用groovy脚本
  3. if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
  4. // Any GroovyLoaders added in beans{} DSL can contribute beans here
  5. GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
  6. load(loader);
  7. }
  8. //使用注解加载
  9. if (isComponent(source)) {
  10. this.annotatedReader.register(source);
  11. return 1;
  12. }
  13. return 0;
  14. }

5、下面方法判断启动类中是否包含@Component注解,但是会神奇的发现我们的启动类中并没有该注解,继续更进发现MergedAnnotations类传入了一个参数SearchStrategy.TYPE_HIERARCHY,会查找继承关系中是否包含这个注解,@SpringBootApplication-->@SpringBootConfiguration-->@Configuration-->@Component,当找到@Component注解之后,会把该对象注册到AnnotatedBeanDefinitionReader对象中

  1. private b
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/902329
推荐阅读
相关标签
  

闽ICP备14008679号