Spring Boot自动配置原理

本文详细介绍了SpringBoot的自动配置机制,包括@Conditional用于条件判断创建Bean,@Enable用于动态启用功能,以及@Import用于导入配置和Bean。特别关注了@EnableAutoConfiguration在自动配置中的关键作用和自定义启动器的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、 什么是自动配置


        Spring Boot自动配置是Spring Boot框架的一个核心特性,它可以帮助开发者简化应用程序配置的过程。自动配置是通过在classpath(类路径)中查找特定条件的bean,选择性地为开发者提供默认的bean定义和实例化,从而简化应用程序的配置和使用。

        例如,当我们使用Spring Boot框架开发一个web应用时,只需要在pom.xml中添加Spring Boot Starter Web依赖,Spring Boot就会自动配置好一个嵌入式的Tomcat服务器,我们只需要编写对应的Controller,便可以直接运行Web应用了。

        Spring Boot还提供了一系列的starter依赖,例如Spring Boot Starter Data JPA、Spring Boot Starter Security等,它们都是基于Spring Boot自动配置特性实现的,能够帮助我们快速集成相应的功能模块。

        SpringBoot 自动配置主要是由 @EnableAutoConfigeration 这个注解实现的。在了解这个注解之前我们首先需要明白 @Conditional、@Enable、@Import 这三个注解的含义及用法。

二、@Conditional注解


        Spring Boot 不可能一上来就把依赖中提供的所有的bean全部创建,这样不仅会造成严重的冗余还会影响性能,因此我们需要给Spring  Boot提供一个判断依据,让 Spring Boot 知道我们需要哪个bean,并创建这个bean,这个判断依据就是 Condition 。

        Condition 是在Spring 4.0 增加的条件判断功能,通过这个功能可以实现选择性的创建Bean操作。

为方便理解,我们自定义一个@MyConditional注解并实现应用:

传入需要的完全限定类名:

@Configuration
public class UserConfig {
    @Bean
    @MyConditional(value = {"xxx.xxx.xxx","xxx.xxx.xxx"})//使用@ConditionalOnProperty可以写入配置文件
    //传入需要的完全限定类名,返回值为true则创建bean
    public User user(){
        return new User();
    }
}

 自定义@MyConditional注解:

@Target({ElementType.TYPE,ElementType.METHOD})//注解可以修饰类和方法
@Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Documented//生成文档
@Conditional(value = MyCondition.class)//指定为自定义的实现类
public @interface MyConditional {
    String[] value();//字符数组,用于接收传入的完全限定类名
}

自定义Condition实现类MyCondition:

public class MyCondition implements Condition {
    //重写matches方法
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> map = metadata.getAnnotationAttributes(MyConditional.class.getName());
        //根据注解名获取注解属性,返回值为map(属性名为key,传入数据为value)
        String[] value = (String[]) map.get("value");//获得完全限定类名字符串数组
        boolean flag = true;
        try{
            for (String className : value) {
                Class<?> cls = Class.forName(className);//遍历并获得其Class对象
            }
        }catch (ClassNotFoundException e){
            flag = false;//失败返回false,说明没有相应坐标
        }
        return flag;//成功获得返回true
    }
}

三、@Enable注解


         SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的,而其底层原理时使用 @Import 注解导入一些配置类,实现Bean的动态加载。

@Import

         @Enable底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而 @Import 提供四种用法:

  • 导入Bean
  • 导入配置类
  • 导入 ImportSelector 实现类,一般用于加载配置文件中的类(自动配置会用到)
public interface ImportSelector {
    //将字符串数组中的类全部导入Spring容器
	String[] selectImports(AnnotationMetadata importingClassMetadata);

	@Nullable
	default Predicate<String> getExclusionFilter() {
		return null;
	}

}
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //目前字符串数组的内容是写死的,未来可以设置在配置文件中动态加载
        return new String[]{"xxx.xxx.xxx", "xxx.xxx.xxx"};
    }
}

  • 导入 ImportBeanDefinitionRegistrar 实现类
public interface ImportBeanDefinitionRegistrar {

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry,BeanNameGenerator importBeanNameGenerator) {

		registerBeanDefinitions(importingClassMetadata, registry);
	}

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}

}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //AnnotationMetadata注解
        //BeanDefinitionRegistry向spring容器中注入

        //1.获取user的definition对象
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();

        //2.通过beanDefinition属性信息,向spring容器中注册id为user的对象
        registry.registerBeanDefinition("user", beanDefinition);

    }
}

四、@EnableAutoConfigeration注解


         @EnableAutoConfigeration 注解是启动类注解@SpringBootApplication的重要组成之一,也是SpringBoot实现自动配置的关键注解。:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration//声明SpringBoot配置注解
@EnableAutoConfiguration//自动配置
//扫描Bean
@ComponentScan(excludeFilters = {//扫描Bean
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {...}

        @EnableAutoConfigeration 注解通过Spring提供的 @Import 注解导入了 AutoConfigurationImportSelector 类( @Import 注解可以导入配置类或者Bean到当前类中):

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage///添加该注解的类所在的package作为自动配置package进行管理
@Import(AutoConfigurationImportSelector.class)//加载配置文件中的类
public @interface EnableAutoConfiguration {...}

        AutoConfigurationImportSelector 类中 getCandidateConfigurations 方法会将所有自动配置类的信息以List的形式返回,这些配置信息会被Spring容器作Bean来管理。从提示信息可以看到这些自动配置类的信息是在 META-INF/spring.factories 文件下找到的:

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
		Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

META-INF/spring.factories 文件中的 EnableAutoConfiguration 后面就是自动配置类信息:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
.....................

进入这些类会发现基本每个类或方法上都有 @ConditionalOnProperty 或类似注解修饰,进行选择判断:

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
		AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
	public static class CglibAutoProxyConfiguration {

	}

}

 

五、 自定义启动器


使用项目中导入自定义启动器中转坐标:

        <dependency>
            <groupId>com.lxh</groupId>
            <artifactId>redis-spring-boot-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

 中转坐标导入自定义启动器坐标:

        <dependency>
            <groupId>com.lxh</groupId>
            <artifactId>redis-spring-boot-autoconfigure</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

resources/META-INF/spring.factories 内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.lxh.RedisAutoconfiguration

AutoConfigurationImportSelector 类根据配置信息加载对应类实现自动配置:

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoconfiguration {
    //注入jedis
    //@ConditionalOnBean
    @Bean
    public Jedis jedis(RedisProperties redisProperties){
        return new Jedis(redisProperties.getHost(),redisProperties.getPort());
    }
}

 同时也可通过配置修改默认配置:

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
    private String host="localhost";
    private int port=6379;

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }
}

### Spring Boot 自动配置原理解释 #### 启动过程中的自动配置加载 当应用程序启动时,`SpringApplication.run()`方法会触发一系列初始化操作。其中一项重要任务就是加载并执行自动配置逻辑。这一过程中,框架通过读取位于 `META-INF/spring.factories` 文件内的配置项来识别哪些组件应该被激活[^1]。 #### 配置文件解析与条件化装配 对于每一个可能参与自动配置的服务或功能模块(如AOP、Nacos、事务管理等),都有对应的Java配置类负责创建所需的Bean实例。这些配置类通常带有特定的注解标记,比如`@ConditionalOnClass` 或者 `@ConditionalOnMissingBean`,用于判断当前环境是否满足启用该服务的前提条件。只有在所有前置条件都成立的情况下,才会真正地向IoC容器注册相应的Bean对象[^2]。 #### 注解驱动的便捷开发体验 开发者只需简单声明某个特性相关的启停开关型注解——例如`@EnableXXXAutoConfiguration`,即可轻松控制对应特性的开启与否。这是因为此类注解内部集成了对多个潜在可用配置的支持,并能够依据实际运行上下文动态调整行为模式。这种设计极大地减少了手动编写繁琐XML配置的需求,使得项目搭建更加高效快捷[^3]。 ```java @SpringBootApplication // 包含了@EnableAutoConfiguration等功能 public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } ``` #### SPI机制支持灵活扩展 除了内置的功能外,第三方库也可以利用相同的SPI(Service Provider Interface)接口规范参与到Spring Boot自动配置体系当中去。只要遵循既定规则,在项目的依赖路径下放置合适的元数据描述文档(`META-INF/spring.factories`)以及实现具体的业务逻辑代码,则可以无缝集成新的能力到现有系统之中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值