SpringBoot中的自動(dòng)裝配原理詳解
前言
通過兩個(gè)簡(jiǎn)單的案例:在Spring中集成MyBatis、在SpringBoot中集成MyBatis
找出兩者的差異,初探Spring發(fā)展到SpringBoot的部分演化過程
以MyBatis為例,簡(jiǎn)單梳理自動(dòng)配置過程
一、Spring整合MyBatis
1.1pom文件
pom.xml
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
?
<!-- MyBatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
?
<!-- MyBatis-Spring 整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
?
<!-- Druid數(shù)據(jù)庫連接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
?
<!-- MySQL數(shù)據(jù)庫驅(qū)動(dòng) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
1.2配置類
MyBatisConfig
package com.lazy.snail;
?
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
?
import javax.sql.DataSource;
?
/**
* @ClassName MyBatisConfig
* @Description TODO
* @Author lazysnail
* @Date 2024/11/5 16:20
* @Version 1.0
*/
@Configuration
@PropertySource("classpath:db.properties")
@MapperScan("com.lazy.snail.mapper")
public class MyBatisConfig {
@Value("${jdbc.url}")
private String url;
?
@Value("${jdbc.username}")
private String username;
?
@Value("${jdbc.password}")
private String password;
?
@Value("${jdbc.driverClassName}")
private String driverClassName;
?
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
// 基本配置
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
?
// Druid 高級(jí)配置
?
return dataSource;
}
?
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
return factoryBean.getObject();
}
}
1.3數(shù)據(jù)源屬性配置文件
# db.properties jdbc.url=jdbc:mysql://*.*.*.*:3306/snail_db jdbc.username=username jdbc.password=password jdbc.driverClassName=com.mysql.cj.jdbc.Driver
1.4mapper
UserMapper
package com.lazy.snail.mapper;
?
import com.lazy.snail.domain.User;
import org.apache.ibatis.annotations.Insert;
import org.springframework.stereotype.Repository;
?
/**
* @ClassName UserMapper
* @Description TODO
* @Author lazysnail
* @Date 2024/11/5 16:27
* @Version 1.0
*/
@Repository
public interface UserMapper {
@Insert("insert into user_info(user_id, name, email) values(#{userId}, #{name}, #{email})")
void insert(User user);
}
1.5測(cè)試類
SpringTest
package com.lazy.snail;
?
import com.lazy.snail.domain.User;
import com.lazy.snail.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
@Slf4j
public class SpringTest {
?
@Test
void test() {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
userService.createUser(new User(1, "lazysnail", "lazy_snail@aliyun.com"));
}
}
二、SpringBoot整合MyBatis
2.1pom文件
pom.xml
<!-- springboot集成mybatis 自動(dòng)為mybatis創(chuàng)建和配置所需的bean,簡(jiǎn)化mybatis的使用 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
?
<!-- 數(shù)據(jù)庫連接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
?
<!-- 數(shù)據(jù)庫驅(qū)動(dòng) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
2.2配置文件
application.yml
spring:
datasource:
druid:
url: jdbc:mysql://*.*.*.*:3306/snail_db
username: username
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
2.3mapper
UserMapper
package com.lazy.snail.mapper;
?
import com.lazy.snail.domain.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
?
/**
* @ClassName UserMapper
* @Description TODO
* @Author lazysnail
* @Date 2024/10/10 14:03
* @Version 1.0
*/
@Mapper
public interface UserMapper {
@Insert("insert into user_info(user_id, name, email) values(#{userId}, #{name}, #{email})")
void insert(User user);
}
2.4測(cè)試類
ApplicationTests
package com.lazy.snail;
import com.lazy.snail.domain.User;
import com.lazy.snail.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ApplicationTests {
@Autowired
private UserService userService;
@Test
void contextLoads() {
User user = new User();
user.setUserId(4);
userService.createUser(user);
}
}
三、Spring與SpringBoot整合MyBatis區(qū)別
3.1pom文件差異
- Spring中單獨(dú)引入功能模塊的依賴,如mybatis、mybatis-spring
- SpringBoot中引入starter概念,需要整合mybatis,只需要引入mybatis-spring-boot-starter依賴
Starter POMs are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology that you need, without having to hunt through sample code and copy paste loads of dependency descriptors. For example, if you want to get started using Spring and JPA for database access, just include the spring-boot-starter-data-jpa dependency in your project, and you are good to go.
通過引入 Starter,無需手動(dòng)查找和復(fù)制大量依賴,避免了我們?nèi)ヒ胍蕾囁鶐淼穆闊?/p>
3.2配置類及配置文件差異
Spring中我們需要自定義MyBatis的配置類,專門管理DataSource、SqlSessionFactory、MapperScannerConfigurer這些組件
SpringBoot集成MyBatis時(shí)沒有任何的相關(guān)配置類(基礎(chǔ)版),因?yàn)镾pringBoot提供了自動(dòng)配置功能,MyBatis 的大部分配置可以通過 mybatis-spring-boot-starter 自動(dòng)完成,只需要引入相關(guān)的依賴,并在 application.properties 或 application.yml 中進(jìn)行一些簡(jiǎn)單配置即可,Spring Boot 會(huì)自動(dòng)創(chuàng)建并配置所需的 Bean(如 SqlSessionFactory、MapperScannerConfigurer 等)。
四、SpringBoot怎么自動(dòng)配置MyBatis
4.1MyBatis自動(dòng)配置信息位置

4.2讀取自動(dòng)配置信息

4.3解析bean定義信息
4.3.1解析Application中@Import

/**
* 激活Spring應(yīng)用上下問的自動(dòng)配置功能,嘗試猜測(cè)和配置需要的bean。
* 自動(dòng)配置類通?;陬惵窂胶投x的bean來應(yīng)用。
* 例如:
* 類路徑下有tomcat-embedded.jar意味著可能想要一個(gè)TomcatServletWebServerFactory的bean
*(除非自定義了ServletWebServerFactory的bean)
*
* 當(dāng)使用@SpringBootApplication注解時(shí),上下文的自動(dòng)配置功能自動(dòng)開啟。
* 添加這個(gè)注解沒有額外的作用。
*
* 自動(dòng)配置盡可能的智能,當(dāng)你自定義了更多配置,自動(dòng)配置會(huì)慢慢弱化。
* 如果不想用某些配置,你可以通過excludeName手動(dòng)排除。
* 也可以通過exclude排除。
* 自動(dòng)配置總是在用戶定義bean注冊(cè)之后應(yīng)用。
*
* 用@EnableAutoConfiguration注解的類包(通常通過@SpringBootApplication)具有特定的意義,通常是默認(rèn)的。
* 例如:
* 它將在掃描@Entity類時(shí)使用。
* 通常建議將@EnableAutoConfiguration(如果不使用@SpringBootApplication)放在根包中,以便可以搜索所有子包和類。
*
* 自動(dòng)配置類是常規(guī)的Spring @Configuration bean。
* 它們是使用importcandidate和springfactoresloader機(jī)制定位的(與這個(gè)類相關(guān))。
* 通常自動(dòng)配置bean是@Conditional注解的bean(最常使用@ConditionalOnClass和@ConditionalOnMissingBean注解)。
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* 通過class排除不需要的自動(dòng)配置類
*/
Class<?>[] exclude() default {};
/**
* 通過類名稱排除不需要的自動(dòng)配置類
*/
String[] excludeName() default {};
}
4.3.1.1獲取自動(dòng)配置入口
// AutoConfigurationImportSelector
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// exclude excludeName
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 所有META-INF/spring.factories中的自動(dòng)配置類
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
// 去掉需要排除的
configurations.removeAll(exclusions);
// 過濾器過濾
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
4.3.1.2過濾器應(yīng)用

OnClassCondition過濾器會(huì)對(duì)配置類中@ConditionalOnClass和@ConditionalOnMissingClass進(jìn)行匹配過濾
// MybatisAutoConfiguration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
如果classpath下沒有SqlSessionFactory或者SqlSessionFactoryBean,該配置類將被過濾
OnBeanCondition過濾器會(huì)對(duì)配置類中@ConditionalOnSingleCandidate進(jìn)行匹配過濾
// MybatisAutoConfiguration @ConditionalOnSingleCandidate(DataSource.class)
應(yīng)用上下文中是否存在DataSource類型的單個(gè)候選 bean。如果存在且只有一個(gè)這樣的 bean,則條件匹配。
4.3.1.3過濾結(jié)果

4.3.1.4遍歷處理自動(dòng)配置類

后續(xù)就是將MybatisAutoConfiguration中涉及到的bean定義信息注冊(cè)到容器中。
五、總結(jié)
1. Spring Boot 啟動(dòng)和自動(dòng)配置的初始化
當(dāng) Spring Boot 啟動(dòng)時(shí),SpringApplication.run() 方法會(huì)被調(diào)用,Spring 容器會(huì)加載并處理配置類和自動(dòng)配置類。在自動(dòng)配置過程中,Spring Boot 會(huì)根據(jù) @EnableAutoConfiguration 注解和 @AutoConfiguration 注解掃描和加載符合條件的自動(dòng)配置類。
2. 自動(dòng)配置類的選擇
Spring Boot 自動(dòng)配置是通過 spring.factories 文件來加載的。spring.factories 文件列出了所有自動(dòng)配置類,在應(yīng)用啟動(dòng)時(shí),這些自動(dòng)配置類會(huì)被加載到 Spring 容器中。
spring.factories 文件
在 spring-boot-autoconfigure 模塊中,spring.factories 文件中會(huì)包含對(duì) MyBatis 自動(dòng)配置類 MybatisAutoConfiguration 的引用。該文件告訴 Spring Boot 當(dāng)滿足特定條件時(shí),加載 MybatisAutoConfiguration 類。
3. 處理 @EnableAutoConfiguration 和條件注解
Spring Boot 使用了許多條件注解(@Conditional 系列注解),來決定是否啟用某些配置類。
@ConditionalOnClass 和 @ConditionalOnMissingClass
MybatisAutoConfiguration 配置類上有 @ConditionalOnClass 注解,表示只有當(dāng) SqlSessionFactory、SqlSessionFactoryBean 等 MyBatis 相關(guān)類在類路徑中時(shí),才會(huì)啟用該自動(dòng)配置類。@ConditionalOnClass 由 OnClassCondition 過濾器進(jìn)行檢查,判斷類路徑中是否存在這些類。
MybatisAutoConfiguration 還會(huì)使用 @ConditionalOnSingleCandidate 注解來檢查 Spring 容器中是否存在且只有一個(gè) DataSource 類型的 Bean。如果條件滿足,則加載 MybatisAutoConfiguration。
4. 加載 MybatisAutoConfiguration 配置類
在滿足了 @ConditionalOnClass 和 @ConditionalOnSingleCandidate 等條件后,MybatisAutoConfiguration 類會(huì)被加載并注冊(cè)到 Spring 容器中。
5. 創(chuàng)建 DataSource 和 SqlSessionFactory
MybatisAutoConfiguration 配置類內(nèi)部有多個(gè) @Bean 方法,其中主要的兩個(gè)是 DataSource 和 SqlSessionFactory 的配置。Spring Boot 會(huì)根據(jù)現(xiàn)有的條件自動(dòng)配置這些 Bean。
DataSource
- 如果你沒有手動(dòng)配置
DataSource,Spring Boot 會(huì)使用默認(rèn)的數(shù)據(jù)庫連接池(如 HikariCP)自動(dòng)配置一個(gè)DataSourceBean。 - 如果已經(jīng)在應(yīng)用上下文中存在
DataSource,則自動(dòng)配置會(huì)跳過該步驟。
SqlSessionFactory
SqlSessionFactory 是 MyBatis 的核心對(duì)象,它需要 DataSource 來進(jìn)行初始化。MybatisAutoConfiguration 類會(huì)檢查容器中是否已有 SqlSessionFactory Bean,如果沒有,則會(huì)創(chuàng)建一個(gè)新的 SqlSessionFactory。
@ConditionalOnMissingBean 注解表示只有當(dāng)容器中沒有 SqlSessionFactory 時(shí),才會(huì)創(chuàng)建這個(gè) Bean。
6. 創(chuàng)建 SqlSessionTemplate
SqlSessionTemplate 是 MyBatis 與 Spring 整合的核心類,負(fù)責(zé)事務(wù)管理和會(huì)話的創(chuàng)建。MybatisAutoConfiguration 會(huì)根據(jù) SqlSessionFactory 創(chuàng)建一個(gè) SqlSessionTemplate:
如果容器中沒有 SqlSessionTemplate Bean,MybatisAutoConfiguration 會(huì)創(chuàng)建并注冊(cè)一個(gè) SqlSessionTemplate Bean。
7. 配置 MapperScannerConfigurer 或 @MapperScan
Spring Boot 中的 MyBatis 自動(dòng)配置已經(jīng)提供了自動(dòng)掃描 Mapper 的功能,通常不需要手動(dòng)配置 MapperScannerConfigurer。@MapperScan 注解用于指定掃描 Mapper 接口的包:
Spring Boot 會(huì)自動(dòng)掃描該包下的 Mapper 接口,并將其注冊(cè)為 Spring Bean。
以上就是SpringBoot中的自動(dòng)裝配原理詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot自動(dòng)裝配的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java?嵌入數(shù)據(jù)引擎從?SQLite?到?SPL詳解
這篇文章主要介紹了Java?嵌入數(shù)據(jù)引擎:從?SQLite?到?SPL,SQLite架構(gòu)簡(jiǎn)單,其核心雖然是C語言開發(fā)的,但封裝得比較好,對(duì)外呈現(xiàn)為一個(gè)小巧的Jar包,能方便地集成在Java應(yīng)用中,本文給大家介紹的非常詳細(xì),需要的朋友參考下2022-07-07
Java中JSONObject與JSONArray的使用區(qū)別詳解
這篇文章主要介紹了Java中JSONObject與JSONArray的使用區(qū)別詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
Springboot?Mybatis使用pageHelper如何實(shí)現(xiàn)分頁查詢
這篇文章主要介紹了Springboot?Mybatis使用pageHelper如何實(shí)現(xiàn)分頁查詢問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
SpringBoot中@ConditionalOnProperty注解的使用方法詳解
這篇文章主要介紹了SpringBoot中@ConditionalOnProperty注解的使用方法詳解,在開發(fā)基于SpringBoot框架的項(xiàng)目時(shí),會(huì)用到下面的條件注解,有時(shí)會(huì)有需要控制配置類是否生效或注入到Spring上下文中的場(chǎng)景,可以使用@ConditionalOnProperty注解來控制,需要的朋友可以參考下2024-01-01
MyBatis 探秘之#{} 與 ${} 參傳差異解碼(數(shù)據(jù)庫連接池筑牢數(shù)據(jù)交互
本文詳細(xì)介紹了MyBatis中的`#{}`和`${}`的區(qū)別與使用場(chǎng)景,包括預(yù)編譯SQL和即時(shí)SQL的區(qū)別、安全性問題,以及如何正確使用數(shù)據(jù)庫連接池來提高性能,感興趣的朋友一起看看吧2024-12-12

