SpringBoot 整合 MyBatis 与自动配置原理详解

一、SpringBoot 整合 MyBatis

1. 需求分析

  • 通过使用 SpringBoot+MyBatis整合实现一个对数据库中的 users 表的 CRUD

2. 工程搭建

2.1 创建工程

工程名:04_springboot_mybatis

2.2 pom.xml 配置

xml

XML 复制代码
​
<?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 http://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>2.3.2.RELEASE</version>
    </parent>
    
    <groupId>com.hg</groupId>
    <artifactId>04_springboot_mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!-- springBoot 的启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Mybatis 启动器 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>
        <!-- mysql 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <!-- druid 数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.9</version>
        </dependency>
        <!-- 添加 junit 环境的 jar 包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

    </dependencies>
</project>

​
2.3 application.properties 配置

properties

XML 复制代码
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=1111
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource


logging.level.com.hg.mapper=DEBUG

mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.hg.pojo
2.4 启动类

java

运行

复制代码
@SpringBootApplication
@MapperScan("com.hg.mapper") // 扫描MyBatis的Mapper接口
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

3. 核心功能实现

3.1 添加用户
3.1.1 数据表设计

mysql

复制代码
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nam` varchar(255) DEFAULT NULL,
  `sex` int(11) DEFAULT NULL,
  `pwd` varchar(255) DEFAULT NULL,
  `birth` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
3.1.2 实体类(pojo)

java

复制代码
public class User {
    private Integer id;
    private String nam;
    private String pwd;
    private Integer sex;
    private Date birth;
    // 省略getter/setter
}
3.1.3 Mapper 层

接口:

复制代码
public interface UserMapper {
    public void insertUser(User user);
}

XML 映射文件:

xml

复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hg.mapper.UserMapper">
    <insert id="insertUser" parameterType="user">
		insert into user(nam,pwd,sex,birth) values(#{nam},#{pwd},#{sex},#{birth})
	</insert>
</mapper>
3.1.4 Service 层

java

复制代码
@Service
@Transactional
public class UserServiceImpl implements UserService {
	@Autowired
	private UserMapper userMapper;

	@Override
	public void addUser(User user) {
		this.userMapper.insertUser(user);
	}
}
3.1.5 测试(JUnit)

运行

复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes={App.class})
public class UserServiceTest {

	@Autowired
	private UserService userService;
	
	@Test
	public void testAddUser(){
		User user = new User();
		user.setId(1);
		user.setNam("二狗");
		user.setPwd("111");
        user.setSex(1);
		user.setBirth(new Date());
		this.userService.addUser(user);
	}
}
3.1.6 Controller 层

java

复制代码
@Controller
@RequestMapping("/user")
public class UserController {
	@Autowired
	private UserService userService;

	/**
	 * 页面跳转
	 */
	@RequestMapping("/{page}")
	public String showPage(@PathVariable String page) {
		return page;
	}

	/**
	 * 添加用户
	 */
	@RequestMapping("/addUser")
	public String addUser(User user) {
		this.userService.addUser(user);
		return "ok";
	}
}
3.1.7 前端页面(add.html)

html

复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>添加用户</title>
</head>
<body>
<h3>新增用户</h3>
<hr/>
<form th:action="@{/user/addUser}" method="post">
    姓名:<input type="text" name="nam"/><br/>
    密码:<input type="text" name="pwd"/><br/>
    性别:<input type="radio" name="sex" value="1"/>女
         <input type="radio" name="sex" value="0"/>男<br/>
    生日:<input type="text" name="birth"/><br/>
    <input type="submit" value="确定"/><br/>
</form>
</body>
</html>
3.2 查询用户
3.2.1 Mapper 层

接口新增方法:

java

复制代码
public List<User> listUser();

XML 新增映射:

xml

复制代码
<select id="listUser" resultType="user">
	select * from user
</select>
3.2.2 Service 层

java

复制代码
@Override
public List<User> listUser() {
	return userMapper.listUser();
}
3.2.3 Controller 层

java

复制代码
/**
 * 查询全部用户
 */
@RequestMapping("/listUser")
public String listUser(Model model) {
	List<User> list = this.userService.listUser();
	model.addAttribute("list", list);
	return "list";
}
3.2.4 前端页面(list.html)

html

复制代码
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <style type="text/css">
        table {border-collapse: collapse; font-size: 14px; width: 80%; margin: auto}
        table, th, td {border: 1px solid darkslategray;padding: 10px}
    </style>
</head>
<body>
<div style="text-align: center">
    <span style="color: darkslategray; font-size: 30px">欢迎光临!</span>
    <hr/>
    <table class="list">
        <tr>
            <th>id</th>
            <th>姓名</th>
            <th>密码</th>
            <th>性别</th>
            <th>生日</th>
        </tr>
        <tr th:each="user : ${list}">
            <td th:text="${user.id}"></td>
            <td th:text="${user.nam}"></td>
            <td th:text="${user.pwd}"></td>
            <td th:text="${user.sex==1}?'男':'女'"></td>
            <td th:text="${#dates.format(user.birth,'yyyy-MM-dd')}"></td>
        </tr>
    </table>
</div>
</body>
</html>
3.3 用户登录
3.3.1 Mapper 层

接口新增方法:

java

复制代码
public User login(User user);

XML 新增映射:

xml

复制代码
<select id="login" parameterType="User" resultType="User">
	select * from user where nam=#{nam} and pwd=#{pwd}
</select>
3.3.2 Service 层

java

复制代码
@Override
public User login(User user) {
	return userMapper.login(user);
}
3.3.3 Controller 层

java

复制代码
@RequestMapping("/login")
public String login(Model model, User user, HttpSession session) {
	User loginUser = usersService.login(user);
	if(user!=null){
		session.setAttribute("loginUser", loginUser);
		return "redirect:/user/listUser";
	}
	return "login";
}

新增页面跳转 Controller:

java

复制代码
@Controller
public class PageController {
	@RequestMapping("/")
	public String showLogin(){
		return "login";
	}
}
3.3.4 前端页面(login.html)

html

复制代码
<html>
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<center>
	<h3>登录</h3>
	<hr/>
	<form th:action="@{/user/login}" method="post">
		账号:<input type="text" name="nam" /><br/>
		密码:<input type="text" name="pwd"/><br/>
		<input type="submit" value="确定"/><br/>
	</form>
</center>
</body>
</html>

4. 扩展功能

4.1 日期转换器
4.1.1 自定义转换器

java

复制代码
import java.text.ParseException;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
import org.apache.commons.lang3.time.DateUtils;

public class DateConverter implements Converter<String, Date>{
	@Override
	public Date convert(String str) {
	      String[] patterns = new String[]{
	              "yyyy-MM-dd","yyyy-MM-dd hh:mm:ss","yyyy/MM/dd","yyyy/MM/dd hh:mm:ss",
	              "MM-dd-yyyy","dd-MM-yyyy"};
	      
		try {
			Date date = DateUtils.parseDate(str, patterns);
			return date;
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}
}
4.1.2 配置转换器

java

复制代码
import com.hg.converter.DateConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Component
public class MyConfig implements WebMvcConfigurer {
	@Override
	public void addFormatters(FormatterRegistry registry) {
		registry.addConverter(new DateConverter());
	}
}
4.2 拦截器
4.2.1 自定义拦截器

java

复制代码
package com.hg.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.hg.pojo.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class LoginInterceptor implements HandlerInterceptor{
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler)throws Exception {
		User user = (User) request.getSession().getAttribute("user");
		if(user!=null){
			return true;
		}
		response.sendRedirect("/");
		return false;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                           Object handler,ModelAndView modelAndView) throws Exception {}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                                Object handler, Exception ex)throws Exception {}
}
4.2.2 配置拦截器

修改 MyConfig 类:

java

复制代码
// 配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
	registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/emp/**");
}

二、SpringBoot 自动配置原理(重点)

1. 核心注解解析

1.1 @SpringBootApplication
复制代码
@SpringBootConfiguration// 标识当前类为配置类
@EnableAutoConfiguration // 开启自动配置
@ComponentScan(excludeFilters = {// 扫描路径配置
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
	@Filter(type = FilterType.CUSTOM, classes =AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}    
1.2 @SpringBootConfiguration

包含@Configuration注解,可配合@Bean以注解形式替代 XML 配置注入 Bean:

复制代码
@Configuration
public class MockConfiguration{
    @Bean
    public MockService mockService(){
        return new MockServiceImpl();
    }
}
1.3 @EnableAutoConfiguration
复制代码
@AutoConfigurationPackage// 扫描启动类所在包及子包组件到IOC容器
@Import(EnableAutoConfigurationImportSelector.class)// 核心自动配置逻辑
public @interface EnableAutoConfiguration {
...
}
  • @AutoConfigurationPackage:通过AutoConfigurationPackages.Registrar扫描启动类所在包及子包的组件。
  • @Import(EnableAutoConfigurationImportSelector.class):读取META-INF/spring.factories中的配置类,加载到 IOC 容器完成自动配置。
1.4 @ComponentScan

默认扫描@ComponentScan注解所在类的包路径,也可通过basePackages属性自定义扫描范围。

2. 模拟自动配置

2.1 工程搭建

pom.xml:

xml

复制代码
<?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 http://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>2.3.2.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.hg</groupId>
    <artifactId>06_springboot_AutoConfiguration</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

启动类:

复制代码
import com.springboot.bean.MyBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext ac = SpringApplication.run(App.class, args);
        ac.getBean("myBean", MyBean.class).myMethod();
    }
}
2.2 单配置类实现
  • 自定义 Bean:

    public class MyBean {
    public void myMethod(){
    System.out.println("MyBean.myMethod方法执行了......");
    }
    }

  • 自动配置类:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    public class MyBeanAutoConfiguration {
    @Bean
    public MyBean myBean(){
    return new MyBean();
    }
    }

  • 导入配置类:

    @SpringBootApplication
    @Import(MyBeanAutoConfiguration.class)
    public class App {
    public static void main(String[] args) {
    ConfigurableApplicationContext ac = SpringApplication.run(App.class, args);
    ac.getBean("myBean", MyBean.class).myMethod();
    }
    }

2.3 多配置类优化(ImportSelector)
  • 自定义 ImportSelector:

java

复制代码
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.hg.config.MyBeanAutoConfiguration"};
    }
}
  • 导入 ImportSelector:

java

复制代码
@SpringBootApplication
@Import(MyImportSelector.class)
public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext ac = SpringApplication.run(App.class, args);
        ac.getBean("myBean", MyBean.class).myMethod();
    }
}
2.4 解耦合(spring.factories)
  • 编写 spring.factories(resources/META-INF/):

properties

复制代码
com.hg.config.MyBeanAutoConfiguration
  • 改造 ImportSelector:

java

复制代码
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        List<String> imports = new ArrayList<>();
        InputStream is = MyImportSelector.class
            .getClassLoader().getResourceAsStream("spring.factories");
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String line = null;
        while (true){
            try {
                line = br.readLine();
                if (line == null) break;
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
            imports.add(line);
        }
        if (br!=null){
            try {
                br.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return imports.toArray(new String[0]);
    }
}
  • 自定义注解(可选):

    import org.springframework.context.annotation.Import;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(MyImportSelector.class)
    public @interface MyEnableAutoConfiguration {
    }

  • 使用自定义注解:

    @SpringBootApplication
    @MyEnableAutoConfiguration
    public class App {
    public static void main(String[] args) {
    ConfigurableApplicationContext ac = SpringApplication.run(App.class, args);
    ac.getBean("myBean", MyBean.class).myMethod();
    }
    }

2.5 复用 Spring 原生机制
  • 修改 spring.factories:

properties

复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.hg.config.MyBeanAutoConfiguration
  • 简化启动类:

运行

复制代码
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext ac = SpringApplication.run(App.class, args);
        ac.getBean("myBean", MyBean.class).myMethod();
    }
}

总结

本文详细讲解了 SpringBoot 整合 MyBatis 的完整流程(包含用户 CRUD、日期转换器、拦截器),同时深入剖析了 SpringBoot 自动配置的核心原理,并通过模拟实现让大家直观理解自动配置的底层逻辑。掌握这些内容,能帮助开发者更好地基于 SpringBoot 进行项目开发和定制化配置。

相关推荐
拾荒的小海螺2 小时前
JAVA:Spring Boot3 集成 Spring AI 实现 Prompt 提示词工程
java·spring boot·spring
恼书:-(空寄2 小时前
Seata TCC 生产级(空回滚+悬挂+幂等)+ AT/TCC 混合使用
java·seata·分布式事务
超级无敌大好人2 小时前
程序运行卡住排查
java·spring ai·qdrant
AI服务老曹2 小时前
源码级解耦:基于 Spring Boot/Vue 的 AI 视频平台二次开发指南与私有化部署实践
vue.js·人工智能·spring boot
NGC_66112 小时前
深入解析 ConcurrentHashMap 设计思想:高并发下的线程安全哈希表
java·开发语言
无极低码2 小时前
纯Java、无任何第三方依赖、直接可用的 SQLite 工具类
java·jvm·sqlite
weixin_425023002 小时前
Spring Boot 2.7 + JDK 8 实现 WebSocket 集群分布式部署(基于 Redis Pub/Sub 方案)
java·spring boot·websocket
高级盘丝洞2 小时前
Spring Boot 使用 WebServiceTemplate 调用 WebService 完整教程
java·spring boot·后端
人道领域4 小时前
Day | 11 【苍穹外卖统计业务的实现:含详细思路分析】
java·数据库·后端·苍穹外卖