深入浅出地理解SpringBoot自动装配原理

简介: 摘要: SpringBoot通过起步依赖和自动配置简化了JavaWeb开发。起步依赖(如spring-boot-starter-web)集成关联依赖,减少手动配置;自动配置基于@Conditional条件装配Bean,避免繁琐声明。文章解析了自动配置源码(如@EnableAutoConfiguration导入AutoConfigurationImportSelector),并演示如何手写自定义starter。

1.SpringBoot的优越性

Spring是目前世界上主流的JavaWeb开发框架,它可以帮助我们更快速构建Java项目,在Spring家族中提供了很多优秀的框架,而所有的框架都是基于一个基础框架的SpringFrameWork(也就是Spring框架),不过,直接基于Spring框架进行开发是一个比较繁琐的过程,一方面,在pom.xml中进行依赖配置比较繁琐,在开发过程中,我们需要手动去配置大量依赖并保证其不起冲突,另一方面,我们在Spring框架的开发过程中,需要在Spring的配置文件中做大量配置,这是一个非常繁琐且成本较高的过程。

由此,我们的SpringBoot框架应运而生,它使得我们能够把更多精力放到业务本身,而不是把时间花费在框架本身的配置使用上。SpringBoot的优越性体现在其底层提供的两个重要功能:起步依赖和自动配置,起步依赖可以大大简化在pom.xml文件中的配置,自动配置可以大大简化框架在使用过程中Bean的声明和配置。

2.起步依赖

SpringBoot提供了大量封装好的起步依赖,比如,spring-boot-start-web是Web开发的起步依赖,在此依赖中集成了json,web,tomcat等依赖,我们只需要引入这个起步依赖,其他的依赖都会自动通过Maven的依赖传递进入项目。

image.gif 编辑

3. 自动装配

SpringBoot的自动装配就是当Spring容器启动后,一些配置类,Bean就会自动装配到IOC容器中,无需我们手动声明,从而简化开发,省去繁琐的配置操作。

3.1 实现自动配置的方案

方案一:

示例如下:

在common模块下封装JWT的Bean:

image.gif 编辑

在web模块下如果想使用JwtUtil的Bean,则需要在Web启动类上添加注解:

@ComponentScan("com.zl.common")
或者 
@SpringBootApplication(scanBasePackages = {"com.zl.web", "com.zl.common"})

image.gif

如果采用上述方案完成自动配置,当需要引入大量第三方依赖时就需要在启动类上扫描大量包,这将会非常繁琐并且大面积扫描会降低性能,因此SpringBoot并未采用这种方式进行自动装配。

方案二:

使用@Import注解导入,可以导入普通类,配置类,以及ImportSelector接口的实现类。

示例一:在web模块启动类中导入common模块的JwtUtil Bean将其装配到web模块使用

image.gif 编辑

示例二:在web模块的启动类上导入common模块的MinIo配置类,将此配置类以及在配置类中注册的Bean都装配到web模块中使用。

image.gif 编辑

image.gif 编辑

示例三:在common模块的MyImportSelector中定义要注入的类的全类名,在web模块启动类上将此类注入。

image.gif 编辑

image.gif 编辑

示例四:让common模块提供启动注解(@EnableXXX),该注解中封装@Import注解,只需在web模块启动类上增加@EnableXXX注解即可

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)//指定要导入哪些bean对象或配置类
public @interface EableJwtConfig {
}

image.gif

@EableJwtConfig

image.gif

3.2 自动装配源码剖析

SpringBoot上的核心注解@SpringBootApplication: image.gif 编辑

自动配置核心注解@EnableAutoConfiguration:

image.gif 编辑

image.gif 编辑其中,该注解使用了@Import注解,导入了实现了ImportSelector接口的类

AutoConfigurationImportSelector类重写了ImportSelector接口的selectImports方法

image.gif 编辑

selectImports()方法底层调用getAutoConfigurationEntry()方法,获取可自动配置的配置类集合信息

image.gif 编辑

getAutoConfigurationEntry()方法通过调用getCandidateConfigurations(annotationMetadata, attributes)方法获取在配置文件中配置的所有自动配置类的集合

image.gif 编辑

getCandidateConfigurations方法获取图中路径文件下配置类的集合,在我们的起步依赖下可以找到对应的该文件,该文件指定了第三方依赖的配置类。

image.gif 编辑

当SpringBoot程序启动时,会加载配置文件中定义的配置类并将配置类信息(全类名)封装到String类型数组中,最终由@Import注解将配置类信息加载到IOC容器中。

但是,配置类集合文件中声明的所有配置类并不一定会全部加载,在声明Bean对象时,还会加一个@Conditional注解,这个注解的作用是提供按条件进行装配,只有满足注解中的条件时,才能将Bean注册到IOC容器中。

@Conditional注解是一个父注解,派生出大量子注解:

@ConditionalOnClass:判断环境中是否对应的字节码文件,有才注册到IOC容器中

@ConditionalOnMissingBean:判断环境中没有对应的Bean,没有才注册到IOC容器中

@ConditionalOnProperty:判断配置文件中有对应属性和值才注册到IOC容器中

image.gif 编辑 image.gif 编辑 image.gif 编辑

自动配置过程总结:

image.gif 编辑

4.手写一个自定义strarter

所谓starter指的就是SpringBoot中的起步依赖,在实际项目中,我们可能会使用到很多第三方技术,但是并非所有第三方技术都提供了与SpringBoot整合的starter依赖,如果这些又非常通用,我们完全可以自己封装这些技术的starter依赖然后传到Maven仓库中,这样,不同项目组的开发者都可以直接引入此依赖进行使用。

这里,我已封装IP监控功能的自定义starter去演示

1.首先定义monitor-spring-boot-autoconfiguration,在里面封装业务逻辑操作

image.gif 编辑

引入必须依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zl</groupId>
    <artifactId>monitor-spring-boot-autoconfiguration</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>monitor-spring-boot-autoconfiguration</name>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.zl</groupId>
            <artifactId>zl-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

image.gif

监控逻辑:

import com.zl.common.utils.ipUtils.AddressUtil;
import com.zl.common.utils.ipUtils.IPUtil;
import jakarta.servlet.http.HttpServletRequest;
import java.util.*;
public class MonitorOperator {
    private TestProperties properties;
    public MonitorOperator(TestProperties properties) {
        this.properties = properties;
    }
    /**
     * 实时IP地址监测接口
     * @param request HttpServletRequest
     * @return IP和归属地信息
     */
    public Map<String, String> monitorIp(HttpServletRequest request) {
        Map<String, String> result = new HashMap<>();
        String ip = IPUtil.getIpAddr(request); // 获取客户端IP
        String location = AddressUtil.getRealAddressByIP(ip); // 获取地址
        result.put("username", properties.getUsername());
        result.put("password", properties.getPassword());
        result.put("ip", ip);
        result.put("location", location);
        return result;
    }
}

image.gif

配置properties信息:

@Data
@ConfigurationProperties("test")
public class TestProperties {
    private String username;
    private String password;
}

image.gif

配置类:

@Configuration
//将配置文件TestProperties注册为Bean
@EnableConfigurationProperties(TestProperties.class)
public class MonitorConfiguration {
    @Bean
    public MonitorOperator monitorOperator(TestProperties testProperties) {
        return new MonitorOperator(testProperties);
    }
}

image.gif

在resource对应路径下的imports文件中指定要自动装配的类名

image.gif 编辑

image.gif 编辑

2.定义monitor-spring-boot-starter,此模块只保留pom.xml文件,并且在依赖引入autoconfig模块依赖

image.gif 编辑

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zl</groupId>
    <artifactId>monitor-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>monitor-spring-boot-starter</name>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.zl</groupId>
            <artifactId>monitor-spring-boot-autoconfiguration</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    
</project>

image.gif

3.定义测试模块,并在测试模块中引入monitor-spring-boot-starter依赖,进行测试

image.gif 编辑

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zl</groupId>
    <artifactId>test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>test</name>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- SpringBoot相关依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.zl</groupId>
            <artifactId>monitor-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

image.gif

在application.yml中指定配置

test:
  username: GuihaoLv
  password: 123456

image.gif

定义测试接口:

@RestController("/monitor")
public class MonitorController {
    @Autowired
    private MonitorOperator monitorOperator;
    @GetMapping("/getMonitor")
    public Map<String, String> map(HttpServletRequest request){
        return monitorOperator.monitorIp(request);
    }
}

image.gif

测试结果:

image.gif 编辑

这样,我们就完成了监测功能的starter定义,其他项目若想调用这个模块的功能,引入依赖,配置一下test信息就可以直接注入使用了。

相关文章
|
SQL 关系型数据库 数据库
学习分布式事务Seata看这一篇就够了,建议收藏
学习分布式事务Seata看这一篇就够了,建议收藏
26090 2
|
6月前
|
SQL 关系型数据库 MySQL
深入MySQL底层3-事务与锁机制
本文系统介绍了MySQL事务机制及锁实现原理。主要内容包括:1. 事务基础:定义事务为不可分割的操作集合,详解ACID四大特性(原子性、一致性、隔离性、持久性)及其实现机制,分析脏读、不可重复读、幻读等并发问题及四种隔离级别的解决方案。2. 日志系统:阐述redo log(重做日志)保障持久性的WAL机制,以及undo log(回滚日志)实现原子性和MVCC的逻辑。3. MVCC原理:通过隐藏字段(DB_TRX_ID,DB_ROLL_PTR)、undo log版本链和ReadView实现多版本并发控制,详细
|
12月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
3046 0
|
6月前
|
存储 SQL 关系型数据库
深入MySQL底层1-存储引擎与索引
MySQL体系结构分为连接层、服务层、引擎层和存储层,其中引擎层采用插件式架构,支持多种存储引擎。InnoDB是默认引擎,支持事务、行锁和外键,其数据以B+树结构存储;MyISAM适合读多写少场景;Memory引擎将数据存于内存。索引是提高查询效率的关键数据结构,主要包括B+树和Hash两种结构,B+树适合范围查询,Hash适合等值查询。合理使用索引需遵循最左前缀法则,避免索引失效情况,并考虑覆盖索引、前缀索引和联合索引的应用。索引设计原则包括:选择高区分度列、控制索引数量、优先使用联合索引等,以平衡查询性
|
10月前
|
监控 Java 开发者
Spring Boot 核心原理解析与实践(含代码示例)
Spring Boot基于“约定优于配置”理念,通过自动配置、Starter依赖和内嵌服务器,简化Spring应用的搭建与开发。支持快速集成Web、数据访问、安全等模块,并提供Actuator监控、分布式事务等生产级特性,助力高效构建微服务系统。(238字)
1554 17
|
9月前
|
JSON 安全 Java
SpringBoot.鉴权
本文介绍如何在Spring Boot项目中结合JWT与Spring Security实现安全鉴权。涵盖Token生成与验证、自定义认证流程、角色权限控制(RBAC),并通过过滤器实现登录拦截与请求认证,保障接口安全。
512 2
|
canal 缓存 NoSQL
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
根据对一致性的要求程度,提出多种解决方案:同步删除、同步删除+可靠消息、延时双删、异步监听+可靠消息、多重保障方案
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
|
人工智能 Java 数据库
如何保证接口幂等性?
在分布式系统中,接口幂等性至关重要。本文详解其定义、重要性及实现方案,包括唯一索引、Token机制、分布式锁、状态机与版本号机制,并提供最佳实践建议,助你提升系统可靠性与用户体验。
2524 1
|
缓存 Java 开发者
SpringBoot自动装配原理
SpringBoot自动装配原理
1648 1

热门文章

最新文章