SpringBoot自动装配

待我称王封你为后i 2023-06-24 08:06 77阅读 0赞

@SpringBootApplication

在我们开发SpringBoot项目时,项目的启动类往往被@SpringBootApplication 注解所修饰,那么这个注解到底有什么含义?

@SpringBootApplication 本质上是由三个注解组成的复合注解。

这三个注解分别是:@SpringBootConfiguration(本质是@Configuration)、@EnableAutoConfiguration、@ComponentScan。

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @SpringBootConfiguration
  6. @EnableAutoConfiguration
  7. @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  8. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
  9. public @interface SpringBootApplication {
  10. /**
  11. * Exclude specific auto-configuration classes such that they will never be applied.
  12. * @return the classes to exclude
  13. */
  14. @AliasFor(annotation = EnableAutoConfiguration.class)
  15. Class<?>[] exclude() default {};
  16. /**
  17. * Exclude specific auto-configuration class names such that they will never be
  18. * applied.
  19. * @return the class names to exclude
  20. * @since 1.3.0
  21. */
  22. @AliasFor(annotation = EnableAutoConfiguration.class)
  23. String[] excludeName() default {};
  24. /**
  25. * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
  26. * for a type-safe alternative to String-based package names.
  27. * @return base packages to scan
  28. * @since 1.3.0
  29. */
  30. @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
  31. String[] scanBasePackages() default {};
  32. /**
  33. * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
  34. * scan for annotated components. The package of each class specified will be scanned.
  35. * <p>
  36. * Consider creating a special no-op marker class or interface in each package that
  37. * serves no purpose other than being referenced by this attribute.
  38. * @return base packages to scan
  39. * @since 1.3.0
  40. */
  41. @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
  42. Class<?>[] scanBasePackageClasses() default {};
  43. }

@Configuration

我们知道,目前 Spring bean 支持两种配置方式,一种是基于 xml 文件方式,另一种就是 JavaConfig 方式。

任何一个标注了@Configuration 的 Java 类都是一个 JavaConfig 配置类,而在配置类中,任何一个标注了@Bean 的方法,其返回值都会作为 Bean 定义注册到 Spring IOC 容器中,方法名默认为这个 bean 的 id。

@ComponentScan

@ComponentScan 相当于 xml 配置文件中的 。主要作用就是扫描指定路径下标识了需要装配的类,自动装配到 Spring IOC容器中。

用于标识类需要装配的注解有:@Component、@Service、@Controller、@Repository。

如果在@ComponentScan注解中没有指定具体路径,默认会扫描当前类所在的包。

@EnableAutoConfiguration

@EnableAutoConfiguration 同样是一个复合注解。

@AutoConfigurationPackage 的作用是:该注解修饰的类所在的 package 会作为自动装配的 package 管理。

@Import 可以把多个分开的配置合并到一个配置中。

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @AutoConfigurationPackage
  6. @Import(AutoConfigurationImportSelector.class)
  7. public @interface EnableAutoConfiguration {
  8. String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  9. /**
  10. * Exclude specific auto-configuration classes such that they will never be applied.
  11. * @return the classes to exclude
  12. */
  13. Class<?>[] exclude() default {};
  14. /**
  15. * Exclude specific auto-configuration class names such that they will never be
  16. * applied.
  17. * @return the class names to exclude
  18. * @since 1.3.0
  19. */
  20. String[] excludeName() default {};
  21. }

@Import

@Import 可以把多个分开的配置合并到一个配置中。

它可以配置三种不同的 class:

1、被@Configuration 修饰的 JavaConfig 类,将配置中被@Bean修饰的 bean 注入。

  1. @Import(OtherConfig.class)
  2. @Configuration
  3. public class FirstConfig {
  4. @Bean
  5. public FirstBean firstBean() {
  6. return new FirstBean();
  7. }
  8. }
  9. @Configuration
  10. public class OtherConfig {
  11. @Bean
  12. public OtherBean otherBean() {
  13. return new OtherBean();
  14. }
  15. }

2、对实现了 ImportSelector 接口的class进行动态注入。

例如在前面提到的@EnableAutoConfiguration 注解中导入了 AutoConfigurationImportSelector 类,AutoConfigurationImportSelector 实现了 ImportSelector 接口的 selectImports 方法,在 selectImports 方法中可以按照自己的逻辑来装配bean,显然比第一种方式更加灵活了。

  1. public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
  2. ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  3. ......
  4. @Override
  5. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  6. // annotationMetadata 注解的元信息
  7. if (!isEnabled(annotationMetadata)) {
  8. return NO_IMPORTS;
  9. }
  10. // 自动装配元数据:基于SPI机制,加载META-INF/spring.factories配置文件中需要自动装配的配置
  11. AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
  12. .loadMetadata(this.beanClassLoader);
  13. AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
  14. annotationMetadata);
  15. // 返回需要装配的类的全路径名的数组
  16. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  17. }
  18. ......
  19. }

注:这里自动装配是基于Spring 的 SPI 机制,加载的是 META-INF/spring.factories 中配置的,且 key 为当前注解的全限定名(org.springframework.boot.autoconfigure.EnableAutoConfiguration)的所有 value,value 是需要加载的配置类,需要被 @Configuration 修饰,可以是多个,用逗号分隔。

  1. # Auto Configure
  2. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  3. org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
  4. org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
  5. org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
  6. org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
  7. ......

3、对实现了 ImportBeanDefinitionRegistrar 接口的 class 进行动态注入。

与上面的 AutoConfigurationImportSelector 相似,通过实现 ImportBeanDefinitionRegistrar 接口的 registerBeanDefinitions 方法来自定义注入 bean。

  1. public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
  2. @Override
  3. public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
  4. RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(FirstBean.class);
  5. // bean 名称
  6. String beanName = StringUtils.uncapitalize(FirstBean.class.getSimpleName());
  7. // 注册一个 rootBeanDefinition
  8. beanDefinitionRegistry.registerBeanDefinition(beanName, rootBeanDefinition);
  9. }
  10. }

发表评论

表情:
评论列表 (有 0 条评论,77人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Springboot自动装配

    一.自动装配 > 自动装配是springboot的核心,一般提到自动装配就会和springboot联系在一起。实际上 Spring Framework 早就实现了这个功能

    相关 SpringBoot自动装配

    微服务: 一个大的项目可以由多个业务模块构成(微服务),每一个业务模块可以作为一个项目,每个项目直接通过HTTP接口进行调用。spring boot可以快速开发微服务,即

    相关 SpringBoot自动装配

    SpringBoot中的自动装配 我们现在提到自动装配的时候,一般会和 Spring Boot 联系在一起。但是,实际上 Spring Framework 早就实现了这个