Spring Boot 自动配置的深入解析

本文详细解析了Spring Boot的自动配置原理,包括其依赖于@Conditional注解实现的条件化Bean创建,以及如何根据环境、类路径、配置文件内容等进行自动配置。通过实例解释了如何使用@ConditionalOnClass、@ConditionalOnBean等注解控制自动配置的启用。最后,探讨了Spring Boot自动配置源码,展示了自动配置类的加载过程。
摘要由CSDN通过智能技术生成

Spring Boot是Spring旗下众多的子项目之一,其理念是约定优于配置,它通过实现了自动配置(大多数用户平时习惯设置的配置作为默认配置)的功能来为用户快速构建出标准化的应用。Spring Boot的特点可以概述为如下几点:

  • 内置了嵌入式的Tomcat、Jetty等Servlet容器,应用可以不用打包成War格式,而是可以直接以Jar格式运行。

  • 提供了多个可选择的”starter”以简化Maven的依赖管理(也支持Gradle),让您可以按需加载需要的功能模块。

  • 尽可能地进行自动配置,减少了用户需要动手写的各种冗余配置项,Spring Boot提倡无XML配置文件的理念,使用Spring Boot生成的应用完全不会生成任何配置代码与XML配置文件。

  • 提供了一整套的对应用状态的监控与管理的功能模块(通过引入spring-boot-starter-actuator),包括应用的线程信息、内存信息、应用是否处于健康状态等,为了满足更多的资源监控需求,Spring Cloud中的很多模块还对其进行了扩展。


有关Spring Boot的使用方法就不做多介绍了,如有兴趣请自行阅读官方文档Spring Boot或其他文章。


如今微服务的概念愈来愈热,转型或尝试微服务的团队也在如日渐增,而对于技术选型,Spring Cloud是一个比较好的选择,它提供了一站式的分布式系统解决方案,包含了许多构建分布式系统与微服务需要用到的组件,例如服务治理、API网关、配置中心、消息总线以及容错管理等模块。可以说,Spring Cloud”全家桶”极其适合刚刚接触微服务的团队。似乎有点跑题了,不过说了这么多,我想要强调的是,Spring Cloud中的每个组件都是基于Spring Boot构建的,而理解了Spring Boot的自动配置的原理,显然也是有好处的。


Spring Boot的自动配置看起来神奇,其实原理非常简单,背后全依赖于@Conditional注解来实现的。


什么是@Conditional?


@Conditional是由Spring 4提供的一个新特性,用于根据特定条件来控制Bean的创建行为。而在我们开发基于Spring的应用的时候,难免会需要根据条件来注册Bean。


例如,你想要根据不同的运行环境,来让Spring注册对应环境的数据源Bean,对于这种简单的情况,完全可以使用@Profile注解实现,就像下面代码所示:


@Configuration
public class AppConfig {
   @Bean
   @Profile("DEV")
   public DataSource devDataSource() {
       ...
   }
    
   @Bean
   @Profile("PROD")
   public DataSource prodDataSource() {
       ...
   }
}


剩下只需要设置对应的Profile属性即可,设置方法有如下三种:


  • 通过context.getEnvironment().setActiveProfiles("PROD")来设置Profile属性。

  • 通过设定jvm的spring.profiles.active参数来设置环境(Spring Boot中可以直接在application.properties配置文件中设置该属性)。

  • 通过在DispatcherServlet的初始参数中设置。


<servlet>
   <servlet-name>dispatcher</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <init-param>
       <param-name>spring.profiles.active</param-name>
       <param-value>PROD</param-value>
   </init-param>
</servlet>


但这种方法只局限于简单的情况,而且通过源码我们可以发现@Profile自身也使用了@Conditional注解。

package org.springframework.context.annotation;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class}) // 组合了Conditional注解
public @interface Profile {
   String[] value();
}
package org.springframework.context.annotation;
class ProfileCondition implements Condition {
   ProfileCondition() {
   }
   // 通过提取出@Profile注解中的value值来与profiles配置信息进行匹配
   public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
       if(context.getEnvironment() != null) {
           MultiValueMap attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
           if(attrs != null) {
               Iterator var4 = ((List)attrs.get("value")).iterator();
               Object value;
               do {
                   if(!var4.hasNext()) {
                       return false;
                   }
                   value = var4.next();
               } while(!context.getEnvironment().acceptsProfiles((String[])((String[])value)));
               return true;
           }
       }
       return true;
   }
}


在业务复杂的情况下,显然需要使用到@Conditional注解来提供更加灵活的条件判断,例如以下几个判断条件:


  • 在类路径中是否存在这样的一个类。

  • 在Spring容器中是否已经注册了某种类型的Bean(如未注册,我们可以让其自动注册到容器中,上一条同理)。

  • 一个文件是否在特定的位置上。

  • 一个特定的系统属性是否存在。

  • 在Spring的配置文件中是否设置了某个特定的值。


举个栗子,假设我们有两个基于不同数据库实现的DAO,它们全都实现了UserDao,其中JdbcUserDAO与MySql进行连接,MongoUserDAO与MongoDB进行连接。现在,我们有了一个需求,需要根据命令行传入的系统参数来注册对应的UserDao,就像java -jar app.jar -DdbType=MySQL会注册JdbcUserDao,而java -jar app.jar -DdbType=MongoDB则会注册MongoUserDao。使用@Conditional可以很轻松地实现这个功能,仅仅需要在你自定义的条件类中去实现Condition接口,让我们来看下面的代码。(以下案例来自:https://dzone.com/articles/how-springboot-autoconfiguration-magic-works)


public interface UserDAO {
   ....
}
public class JdbcUserDAO implements UserDAO {
   ....
}
public class MongoUserDAO implements UserDAO {
   ....
}
public class MySQLDatabaseTypeCondition implements 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值