从 SPI 到 Spring Boot 自动装配

一、从 SPI 到 Spring Boot 自动装配

1,什么是 SPI

SPI (Service Provider Interface) 是 Java 提供的一种 服务发现机制,用于实现模块之间的解耦,基本思想是:

        我来定义接口,由你来提供实现,并通过标准配置注册,我再用 ServiceLoader 动态加载你的实现类。

这种机制常用于第三方模块的自动发现和加载,典型场景包括 JDBC 驱动,日志系统等

这就是 Java SPI 的核心机制:接口标准化、实现解耦、运行时动态加载

    2,SPI 示例 Demo

    定义一个支付接口:

    public interface PayService {
        void pay(double amount);
    }

    第三方提供实现

    public class AlipayService implements PayService {
        public void pay(double amount) {
            System.out.println("Paid " + amount + " via Alipay.");
        }
    }

    在资源目录下添加配置文件

    • 路径:resources/META-INF/services/com.example.PayService
    • 内容:
    --- 文件名:com.example.PayService
    com.example.impl.AlipayService

    调用方式:

    public static void main(String[] args) {
        // ServiceLoad 加载第三方实现类
        ServiceLoader<PayService> pays = ServiceLoader.load(PayService.class);
        // 调用第三方类的实现
        pays.iterator().next().pay(99.99);
    }
    3,SPI 优势与局限:

    优势:

    • 支持接口和实现的完全解耦
    • 不修改主程序代码,仅通过引入不同的 Jar 包即可切换实现

    局限性:

    • 不支持实现类优先级排序,存在多个实现时无法指定用哪个
    • 不支持注入参数(如配置文件中的值),不具备依赖注入能力
    • 配置能力有限,缺乏条件控制与运行时灵活性
    4,Spring Boot 的演进思路

    Spring Boot 在设计自动装配机制时,借鉴了 SPI 思想,但做了大量增强:

    • 定义统一的自动配置接口标准(通常是 @Configuration 类)
    • 借助 spring.factories 声明自动装配类
    • 结合 Spring IOC 容器,通过条件注解决定是否注入
    • 借助 @ConfigurationProperties 实现动态参数注入
    • 最终实现按需,灵活,可配置,可扩展的自动装配能力

    二、Spring Boot 是如何发现和加载这些自动装配类的

    Spring Boot 项目的启动类标注了 @SpringBootApplication 注解,它是三个注解的组合:

    • @SpringBootConfiguration
    • @ComponentScan
    • @EnableAutoConfiguration

    其中,最关键的是 @EnableAutoConfiguration,它是 Spring Boot 核心自动装配的入口。

    自动装配的加载流程( 以 Spring Boot 2.x 为例):

    1,触发导入选择器 AutoConfigurationImportSelector

            @EnableAutoConfiguration 背后由 AutoConfigurationImportSelector 执行,负责扫描并导入所有声明为自动装配类的配置

    2,读取所有 Jar 包中的 META-INF/spring.factories

            Spring Boot 会从所有依赖 Jar 包中加载路径为 META-INF/spring.factories 的配置文件,读取 EnableAutoConfiguration 对应的配置类列表:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
    ...

            Spring Boot 扫描到这些类后,并不是全部直接生效,而是要结合条件注解进行 按需激活 。

    3,根据条件注解判断是否需要激活配置类

            自动装配的关键能力是 按需注入,Spring Boot 提供了一整套 @Conditional 注解,用来实现 Bean 的有条件注册:

    • @ConditionalOnClass:某个类存在才加载(典型用法:第三方依赖是否存在)
    • @ConditionalOnProperty:某个配置项为 true 才加载
    • @ConditionalOnMissingBean:若容器中没有某个 Bean 时才注入(提供默认实现)
    • @ConditionalOnWebApplication:当前项目是 Web 应用时加载

    示例:

    @Bean
    @ConditionalOnMissingBean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
    

    这个条件表示:如果用户没有自定义ObjectMapper,那么就提供一个默认的,如果用户手写了:

    @Bean
    public ObjectMapper myObjectMapper() {
        ...
    }

    那么 Spring Boot 就不会注册默认的那个。

    4,将自动装配类注册为 Spring Bean,并注入配置参数

            被激活的配置类其实就是一个普通的 @Configuration 配置类,里面会通过 @Bean 方法,将这些对象注入到 Spring 容器中。

    示例:

    @Bean
    public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        return template;
    }
    

    有些组件还需要从配置文件中读取参数(如连接池大小,超时时间等),此时,Spring Boot 提供了强大的配置绑定机制@ConfigurationProperties,实现类型安全的注入。

    @ConfigurationProperties配置绑定机制:

    @ConfigurationProperties 可以将整个配置前缀统一绑定为一个 Java 对象,支持嵌套对象,列表,Map等复杂结构。

    示例:

    @ConfigurationProperties(prefix = "spring.redis")
    public class RedisProperties {
        private String host;
        private int port;
        private int timeout;
        // getter/setter
    }
    

    在自动配置类中即可注入使用:

    @Bean
    public RedisConnectionFactory redisConnectionFactory(RedisProperties props) {
        return new LettuceConnectionFactory(props.getHost(), props.getPort());
    }
    

    Spring Boot 会自动从配置文件中读取 spring.redis.* 的值并在 bean 对象的实例化之前注入,开发者无需手动解析配置。

    自动装配的关键特性
    • 统一的 Bean 注册入口:所有第三方提供的 Bean 都通过标准配置类注入
    • 灵活的条件控制机制:能够根据环境、依赖、配置等条件动态决定是否启用
    • 与配置文件的集成:可通过 @ConfigurationProperties 自动绑定配置
    • 模块解耦与替换能力强:原始 Bean 可以通过 @ConditionalOnMissingBean 覆盖

    三、Spring Boot 如何通过自动装配接管 Spring MVC —— DispatcherServlet 的全自动注册过程

    在传统的 Spring MVC 项目中,我们需要在 web.xml 中手动注册 DispatcherServlet,非常繁琐:

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/applicationContext.xml</param-value>
        </init-param>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    Spring Boot 的目标是 零配置,我们只需要引入:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    就能自动完成 DispatcherServlet 的创建和注册。背后的完整流程如下:

    1,spring.factories 中声明自动配置类

            在 spring-boot-autoconfig 包中的 META-INF/spring.factories 文件中,Spring Boot 提前注册了用于 Web 环境的自动配置类:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
    ...
    

    这意味着,一旦 EnableAutoConfiguration 被启用( 通过@SpringBootApplication 间接开启 ),这个类就具备被 Spring 容器加载的资格。

    2,启动时加载配置类

            Spring Boot 启动时,会通过  AutoConfigurationImportSelector 扫描所有依赖包的 spring.factories 文件,并将这些配置类导入容器中。

    这时,DispatcherServletAutoConfiguration 会作为候选自动配置类被注册。

    3,DispatcherServletAutoConfiguration 的主要职责

            这个类其实做了两件事:

    • 定义 DispatcherServlet Bean
    @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
    	DispatcherServlet dispatcherServlet = new DispatcherServlet();
    	dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
        dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
        dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
        dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
       return dispatcherServlet;
    }

    DispatcherServlet 是 Spring MVC 的核心调度器,用于接收请求、分发处理、调用控制器,它的注册是 Web 应用初始化的关键步骤。本质上就是从 spring-webmvc 提供的 DispatcherServlet 类创建的。

    • 定义 ServletRegistrationBean,用于注册 Servlet
    @Bean
    @ConditionalOnBean(DispatcherServlet.class)
    @ConditionalOnMissingBean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
    public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
        ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(dispatcherServlet, this.serverProperties.getServlet().getPath());
        registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
        return registration;
    }
    

    这个 ServletRegisterationBean  就相当于 Spring Boot 版的 web.xml,它会把 DispatcherServlet 注册到内嵌的 Tomcat 中,完成路径映射。

    4,条件注解保障默认行为和可扩展性
    • @ConditionalOnMissingBean:如果你自己没有定义 DispatcherServlet 或 ServletRegisterationBean ,Spring Boot 才会帮你注册
    • @ConditionalOnBean:保证在存在 DispatcherServlet 的前提下才进行注册。

    这种设计非常灵活,如果我们自己写一个 @Bean,Spring Boot 就 让位,否则就用默认的。

    5,结合配置文件,实现个性化控制

    Spring Boot 还支持通过配置文件中的 spring.mvc.* 属性对 DispatcherServlet 的行为进行定制:

    spring:
      mvc:
        servlet:
          path: /api/*           # 修改 DispatcherServlet 映射路径
        throw-exception-if-no-handler-found: true
    

    这些配置会在自动配置类中通过 @ConfigurationProperties 注入,影响 Servlet 的创建逻辑,做到动态、可配置、类型安全。

    小结:Spring Boot 用一套自动装配链路实现了实现了传统 Spring MVC 的 Servlet 注册逻辑,不仅减少了冗余配置,还通过条件注解,配置绑定等机制,实现了默认即用,按需替换,灵活扩展的开发体验

    四、总结

    Spring Boot 的自动装配不仅是对传统 Java SPI 思想的继承,更是在其基础上进行了条件控制,配置绑定,IOC 集成等一系列增强,形成了现代化的组件解耦与模块治理机制。

    我们日常开发时只是 @Autowired 一行代码,但背后都是一次次精妙的自动装配过程。

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值