SpringBoot自动装配

自动装配可以理解为:通过注解或者一些简单的配置就能在SpringBoot的帮助下实现某块功能。

SpringBoot是如何实现自动装配的呢?

SpringBoot 的核心注解 SpringBootApplication。

// 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 {
	XXXXXX
}

// SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration  //SpringBootConfiguration实际上是一个配置类
public @interface SpringBootConfiguration {   
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

SpringBootApplication可以看作是Configuration,EnableAutoConfiguration,ComponentScan的集合。根据 SpringBoot 官网,这三个注解的作用分别是:

  • Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
  • EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
  • ComponentScan:扫描被@Component (@Service、@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 。同时特可以自定义不扫描某些 bean,如下图所示,容器中将排除TypeExcludeFilter和AutoConfigurationExcludeFilter

@EnableAutoConfiguration 是实现自动装配的重要注解,我们以这个注解入手。

@EnableAutoConfiguration:实现自动装配的核心注解

EnableAutoConfiguration只是一个组合注解,自动装配的核心功能的实现类其实是AutoConfigurationImportSelector类。

··

image-20230923172838340

问:@Import注解有什么作用?(知识补充,不想了解的可以跳过!)

在Spring框架中到处都可以见到这个注解。那么这个注解的作用到底是什么呢?

  • @Import 注解提供了类似 @Bean 注解的功能。

  • 从代码的注释中可以看到:Import可以配合 Configuration, ImportSelector, ImportBeanDefinitionRegistrar 来使用,也可以用于一个普通类的导入。

  • 使用方式有三种:

    • 普通类注入Spring容器

    • 实现了ImportSelector接口的类注入Spring容器。
    • 实现了ImportBeanDefinitionRegistrar接口的类注入Spring容器。
普通类注入
public class NorMal {

	private String msg;

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
}

import作用于某个类上

@Configuration
@Import(NorMal.class)
public class MyConfigure {
}

这样NorMal就相当于注入到Spring容器中了。

实现了ImportSelector接口
/**
 * 批量导入对象到容器的类
 */
public class MySelectImport implements ImportSelector {
    //返回需要导入Spring容器中的所有对象全限定名称
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        String[] strings = new String[]{
                "com.zhong.pojo.NorMal",
                "com.zhong.pojo.User"
        };
        return strings;
    }
}

@SpringBootApplication
@Import(MySelectImport.class)
public class Springboot08ImportAnnoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Springboot08ImportAnnoApplication.class, args);
        NorMal normal = context.getBean(NorMal.class);
        System.out.println(normal);

    }
}
实现了ImportBeanDefinitionRegistrar
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(NorMal.class);
		AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
		registry.registerBeanDefinition("normal", beanDefinition);
	}
  
  //或者 使用如下的方法也可以,自动生成beanName
  @Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(NorMal.class);
		AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
		String beanName = importBeanNameGenerator.generateBeanName(beanDefinition, registry);
		registry.registerBeanDefinition(beanName, beanDefinition);
	}
}

@Import就不再详细介绍了,感兴趣的可以看看这位大佬的文章

AutoConfigurationImportSelector:加载自动装配类

可以看出AutoConfigurationImportSelector实现了DeferredImportSelector接口,而DeferredImportSelector接口继承ImportSelector类。也就是说AutoConfigurationImportSelector也实现了selectImports方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类都会被加载到IOC容器中。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
}

public interface DeferredImportSelector extends ImportSelector {
}

public interface ImportSelector {
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}

AutoConfigurationImportSelector中可以看到selectImports()

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // 判断自动装配是否开启
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    // 获取所有需要装配的bean
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
            .loadMetadata(this.beanClassLoader);
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
            annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

重点关注getAutoConfigurationEntry(),这个方法主要负责加载自动配置类的。

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   //1
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   // 2
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   // 3
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   // 4
   configurations = removeDuplicates(configurations);
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   configurations = filter(configurations, autoConfigurationMetadata);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

1、判断自动装配是否开启。默认spring.boot.enableautoconfiguration=true,可在 application.properties 或 application.yml中设置

2、用于获取EnableAutoConfiguration注解中的 exclude 和 excludeName。(获取需要排除的类)

3、读取需要自动装配的所有配置类,读取META-INF/spring.factories

不光是这个依赖下的META-INF/spring.factories被读取到,所有 Spring Boot Starter 下的META-INF/spring.factories都会被读取到。

4、“spring.factories中这么多配置,每次启动都要全部加载么?”。

很明显,这是不现实的。我们 debug 到后面你会发现,configurations 的值变小了。

因为,这一步有经历了一遍筛选,@ConditionalOnXXX 中的所有条件都满足,该类才会生效

@Configuration
// 检查相关的类:RabbitTemplate 和 Channel是否存在
// 存在才会加载
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
}

总结

Spring Boot 通过@EnableAutoConfiguration开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories中的自动配置类实现自动装配,自动配置类其实就是通过@Conditional按需加载的配置类,想要其生效必须引入spring-boot-starter-xxx包实现起步依赖

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值