手写SpringBoot(三)之自动配置

news2024/11/27 14:43:34

系列文章目录

手写SpringBoot(一)之简易版SpringBoot
手写SpringBoot(二)之动态切换Servlet容器
手写SpringBoot(三)之自动配置
手写SpringBoot(四)之bean动态加载

手写SpringBoot(三)之自动配置

文章目录

  • 系列文章目录
    • 手写SpringBoot(三)之自动配置

本节主要讲解SpringBoot的自动配置

回想一下,在用原生Spring aop功能的时候,需要哪些配置?

  1. 引入aspectjweaver jar包

  2. 定义切面

  3. 配置切面

  4. 开启aop功能

为什么在SpringBoot中,只需要引入spring-boot-starter-aop就可以直接使用aop功能?

那是因为SpringBoot定义了AOP配置类,帮我们完成了这些工作。

spring-boot-auto-configure jar包中有很多SpringBoot帮我们配置好的类,其中就有AopAutoConfiguration,提供自动开启aop等功能


现在来写一个简易版的Springboot自动配置

当我们新增了AopConfiguration配置类后,需要在MySpringBootApplication中显示import该类,如果AutoConfiguration配置类很多的情况,就要在import标签上面引入很多类。

利用ImportSelector接口,可以将需要加载的类在selectImports方法中,返回该类的全限定名称。

public class MyImportSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[0];
    }
}

在该方法中,可以显示的将需要引入的配置类全路径全部加载一个数组中,然后返回,再通过@MySpringBootApplication Import 该MyImportSelector类就可以加载MyImportSelector中返回的类。

这样实现的弊端,

  1. 在代码中写死加载的类,需要新增时,则需要动代码。
  2. 无法通过外部配置来动态加载所需要的bean。

这里我们利用java spi机制来实现动态加载

Java SPI(Service Provider Interface)是Java官方提供的一种服务发现机制,它允许在运行时动态地加载实现特定接口的类,而不需要在代码中显式地指定该类,从而实现解耦和灵活性

  1. 定义一个特定的接口类 AutoConfiguration
  2. 在resource文件夹下新建META-INF/services文件夹,在该services文件夹下新建AutoConfiguration全限定名的文件。
  3. 在AutoConfiguration全限定名的文件下配置需要加载的类全限定名称。
  4. 在MyImportSelector中实现加载java spi机制的类]
  5. 将@MySpringBootApplication 中的Import标签改为引入MyImportSelector

AutoConfiguration接口定义

package cn.axj.springboot.my.config;

/**
* 该类仅是一个标识作用
*/
public interface AutoConfiguration {
}

新建AutoConfiguration文件,cn.axj.springboot.my.config.AutoConfiguration, AutoConfiguration中配置如下

cn.axj.springboot.my.config.WebServerAutoConfiguration
cn.axj.springboot.my.config.AopAutoConfiguration

此时WebServerAutoConfiguration和AopAutoConfiguration需要实现AutoConfiguration

MyImportSelector实现

package cn.axj.springboot.my.config;

import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

public class MyImportSelector implements DeferredImportSelector {


    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        /**
         * 主要这里加载的是所有命名为cn.axj.springboot.my.config.AutoConfiguration的文件下的定义的类,不仅仅指该spring-boot jar包.
         * 其他jar包只要在resources目录下定义了AutoConfiguration的类,都会被加载进来。
         * 这里可以提供动态扩展能力。
         */
        ServiceLoader<AutoConfiguration> serviceLoader = ServiceLoader.load(AutoConfiguration.class);
        List<String> list = new ArrayList<>();

        for (AutoConfiguration autoConfiguration : serviceLoader) {
            list.add(autoConfiguration.getClass().getName());
        }
        return list.toArray(new String[0]);
    }
}

MySpringBootApplication中改造

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)
public @interface MySpringBootApplication {
}

当前结构图
在这里插入图片描述

启动user-service模块,并在MySpringApplication 中容器创建完成后,打开debug。
在这里插入图片描述

可以看到AopAutoConfiguration 和 WebServerAutoConfiguration 均加载到了容器中。


当其他三方组件也想整合进springboot怎么办?由于其他组件无法获取到AutoConfiguration接口,所以需要将AutoConfiguration接口提出来,专门弄一个jar包供三方组件使用。

新建模块 my-spring-boot-configuration,并将AutoConfiguration类迁移过去。

在my-spring-boot模块中引入 my-spring-boot-configuration
在这里插入图片描述

项目结构如上


下面来构建mybatis 整合springboot的jar包

spring整合mybatis 主要是整合SqlSessionFactoryBean,将这个bean交给Spring管理

SqlSessionFactoryBean 主要设置DataSourceMapperLocationConfigLocaltion等信息。

新建my-mybatis-spring-boot-starter模块

  1. 引入依赖jar包
<?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>cn.axj</groupId>
        <artifactId>spring-boot-base</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>my-mybatis-spring-boot-starter</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>

        <!-- MyBatis-Spring 整合包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.4</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.18</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.19</version>
        </dependency>

        <dependency>
            <groupId>cn.axj</groupId>
            <artifactId>my-spring-boot-configuration</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.18</version>
        </dependency>
    </dependencies>

</project>
  1. 创建自动配置的MybatisAutoConfiguration
package cn.axj.mybatis.springboot.config;

import cn.axj.springboot.my.config.AutoConfiguration;
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;
import java.io.IOException;

@Configuration
public class MyBatisAutoConfiguration implements AutoConfiguration {

    @Bean
    public DataSource dataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");
        druidDataSource.setUrl("jdbc:mysql://192.168.56.102:3306/springtest?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
        return druidDataSource;
    }


    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setDataSource(dataSource());
        sqlSessionFactoryBean.setConfigLocation(resourcePatternResolver.getResource("classpath:mybatis-config.xml"));
        sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources("classpath:mapper/*Mapper.xml"));
        return sqlSessionFactoryBean;
    }
}

为了简便,将数据源这些信息写死,这些信息可以提供配置类,给用户使用。

  1. 在resources目录下创建mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

</configuration>

  1. 创建自动配置的文件路径META-INF/services,并创建文件cn.axj.springboot.my.config.AutoConfiguration,并将MyBatisAutoConfiguration的全限定名称配置进去
cn.axj.mybatis.springboot.config.MyBatisAutoConfiguration

下面开始测试

在user-service模块中引入my-mybatis-spring-boot-starter,并引入mysql连接包

<dependency>
      <groupId>cn.axj</groupId>
      <artifactId>my-mybatis-spring-boot-starter</artifactId>
      <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.33</version>
</dependency>
  1. 创建Model包,并创建User
package cn.axj.user.model;

public class User {

    private Integer id;

    private String username;
	...省略get set 方法
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" +
                username + '\'' +
                '}';
    }
}

  1. 创建mapper包,并新增UserMapper
package cn.axj.user.mapper;

import cn.axj.user.model.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    User selectById(Integer id);
}

  1. 在resources目录下创建mapper文件,并创建UserMapper.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="cn.axj.user.mapper.UserMapper">


    <select id="selectById" resultType="cn.axj.user.model.User">
        select id,username from user where id = #{id}
    </select>
</mapper>
  1. 改造UserService,引入UserMapper查询User
package cn.axj.user.service;

import cn.axj.user.mapper.UserMapper;
import cn.axj.user.model.User;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserService {
    @Resource
    UserMapper userMapper;
    public User test(Integer id){
        User user = userMapper.selectById(id);
        return user;
    }
}

  1. 改造TestController,查询返回User对象
package cn.axj.user.controller;

import cn.axj.user.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class TestController {
    @Resource
    private UserService userService;
    @GetMapping(value = "/test")
    public String test(Integer id){
        return userService.test(id).toString();
    }
}

这里由于没有配置HttpMessageConverter,无法将对象转换成字符串形式,所以用了toString()方法。

  1. 在UserApplication主类上,新增@MapperScan(“cn.axj.user.mapper”),扫描mapper文件。

这个问题,我在MyBatisAutoConfiguration中配置MapperScannerConfigurer,但是无法生效,mybatis不会去扫描响应的包,并生成代理对象,不知道什么原因,但是加在主类上就可以?难道mybatis将SqlSessionFactoryBean交给Spring容器后,就不自己扫描了吗?

项目结构图如下

在这里插入图片描述

至此,启动项目。这里记得引入默认的tomcat 容器,不然会无法启动。

通过浏览器访问 localhost:8080/test?id=1 或者 id = 2

在这里插入图片描述

可以看到整合mybatis成功,成功返回数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1556864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内网穿透时报错【Bad Request This combination of host and port requires TLS.】的原因

目录 1.内网直接https访问&#xff08;可以正常访问&#xff09; 程序配置的证书 2.内网穿透后,通过外网访问 3.原因 4.内网非https的Web应用&#xff0c;使用https后&#xff0c;也变成了https访问 5.题外话 感觉自己的web应用配置了https&#xff0c;反而影响了内网穿…

单词频次-第12届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第44讲。 单词频次&#xf…

SpringAMQP-Exchange交换机

1、Fanout-Exchange的特点是&#xff1a;和它绑定的消费者都会收到信息 交换机的作用是什么? 接收publisher发送的消息将消息按照规则路由到与之绑定的队列不能缓存消息&#xff0c;路由失败&#xff0c;消息丢失FanoutExchange的会将消息路由到每个绑定的队列 声明队列、交…

MySQL 数据库基础操作详解

文章目录 MySQL 数据库基础操作详解1. 基本概念2. 库的操作3. 表的操作4. 数据操作5. 示例示例一&#xff1a;创建表和插入数据示例二&#xff1a;查询数据示例三&#xff1a;更新数据示例四&#xff1a;删除数据 MySQL 数据库基础操作详解 MySQL 是一种常用的关系型数据库管理…

linux查找指定目录下包含指定字符串文件,包含子目录

linux查找指定目录下包含指定字符串的文件&#xff0c;包含子目录 linux查找指定目录下包含指定字符串的指定文件格式&#xff0c;包含子目录 指定目录 cd /home/www/linux查找指定目录下包含指定字符串的文件&#xff0c;包含子目录 grep -r "指定字符串"注释 gr…

深入理解Happens-Before原则:以实例解析并发编程的基石

在最近的一次面试中面试官问到了Happens-Before原则&#xff0c;作此篇回顾下知识点。 在并发编程中&#xff0c;为了保证程序的正确性和可预测性&#xff0c;我们需要理解并遵循一系列内存访问规则。Happens-Before原则定义了线程间可见性和顺序性的保证。所有此篇文章将通过…

代码随想录训练营Day39:● 62.不同路径 ● 63. 不同路径 II

62.不同路径 题目链接 https://leetcode.cn/problems/unique-paths/description/ 题目描述 思路 dp[i][j] 表示的是走到&#xff08;i&#xff0c;j&#xff09;有多少种不同的路径 第一行和第一列都需要初始化为1&#xff0c;因为这些位置都只有一种路径 class Solution {…

头歌 实验一 关系数据库标准语言SQL湖北汽车工业学院 )

头歌 实验一 关系数据库标准语言SQL 制作不易&#xff01;点个关注呗&#xff01;为大家创造更多的价值&#xff01; 目录 头歌 实验一 关系数据库标准语言SQL**制作不易&#xff01;点个关注呗&#xff01;为大家创造更多的价值&#xff01;** 第一关&#xff1a;创建数据库第…

C++ :STL中deque的原理

deque的结构类似于哈希表&#xff0c;使用一个指针数组存储固定大小的数组首地址&#xff0c;当数据分布不均匀时将指针数组内的数据进行偏移&#xff0c;桶不够用的时候会像vector一样扩容然后将之前数组中存储的指针拷贝过来&#xff0c;从原理可以看出deque的性能是非常高的…

用Kimichat拆解雷军在小米汽车SU7发布会上的演讲技巧

小米SU7发布会可以说是非常成功。雷军的演讲技巧是发布会成功的重要因素之一&#xff0c;很值得借鉴学习。 可以借助Kimichat来深度拆解雷军在小米汽车SU7发布会上的演讲技巧。 在kimichat中输入提示词&#xff1a; 根据文件《雷军小米SU7发布会演讲文字稿》&#xff0c;总结…

C# 微软官方学习文档

链接&#xff1a;https://learn.microsoft.com/zh-cn/dotnet/csharp/ 在C#的学习过程中&#xff0c;我们可以参考微软官方的学习文档。它是一个免费的学习平台&#xff0c;提供了丰富的C#学习路径和教程&#xff08;如下图&#xff09;&#xff0c;对我们入门到高级应用开发都…

spring-boot之shiro安全框架配置使用

shiro架构&#xff08;外部&#xff09; shiro架构(内部) 具体API操作 获取当前的用户对象 Subject currentUser SecurityUtils.getSubject();通过当前用户拿到session Session session currentUser.getSession(); session.setAttribute("someKey", "aValu…

代码随想录算法训练营第二十五天| 回溯算法理论基础、LeetCode77.组合

一、216.组合总和III 题目链接/文章讲解/视频讲解&#xff1a; https://programmercarl.com/0216.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CIII.html 状态&#xff1a;已解决 1.思路 做过77题&#xff08;上篇博客&#xff09;后&#xff0c;这道题也就不难了&#xff0c;无非是多…

为什么在Python中总是使用【字典】这种类型呢?

你好&#xff0c;我是安然无虞。 文章目录 创建字典新增字典元素update 方法 删除字典元素pop 方法popitem 方法 查找字典元素in 和 in not 操作符get 方法thisdict[key] 修改字典元素遍历字典元素for循环遍历keys方法values方法items方法 合并字典字典中的key 字典常用接口汇…

深入解析大语言模型显存占用:训练与推理

深入解析大语言模型显存占用&#xff1a;训练与推理 文章脉络 估算模型保存大小 估算模型在训练时占用显存的大小 全量参数训练 PEFT训练 估算模型在推理时占用显存的大小 总结 对于NLP领域的从业者和研究人员来说&#xff0c;有没有遇到过这样一个场景&#xff0c;你的…

Java8之接口默认方法

Java8之接口默认方法 一、介绍二、代码1、接口2、实现类3、测试代码4、效果 一、介绍 在Java8中&#xff0c;允许为接口方法提供一个默认的实现。必须用default修饰符标记这样一个方法。默认方法也可以调用其他方法 二、代码 1、接口 public interface PersonService {void…

基于ARM内核的智能手环(day1)

整体介绍 智能手环由 ARM 内核 MCU(Cortex-M 系列)、TFTLCD 屏、温湿度传感器、心率传感器、 加速度传感器等主要几部分构成。该平台硬件采用 STM32 芯片&#xff0c;通过对温湿度传感器的驱动编写&#xff0c;获取周围温湿度数据&#xff0c;并在 LCD 屏显示&#xff0c;通过对…

算法学习——LeetCode力扣动态规划篇8

算法学习——LeetCode力扣动态规划篇8 300. 最长递增子序列 300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删…

会话跟踪技术(Session 以及Cookie)

一: 前提概要 1>会话: 会话指的是用户打开浏览器, 访问某些web服务器资源的时候, 会话就会进行建立, 直到有一方断开, 那么会话才会结束, 需要注意的一点是, 一次的会话可以有多次的请求以及响应 2>会话跟踪: 是一种用于维护浏览器状态的方法, 服务器需要识别多次的请求,…

基于springboot的船舶维保管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…