手写SpringBoot(三)之自动配置

系列文章目录

手写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成功,成功返回数据。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

重构销售话术和知识库,容联云找到了大模型的“钉子”

科技云报道原创。 从ChatGPT诞生起&#xff0c;大模型在营销、客服等场景的落地就被予以众望。然而在经历了一年多的“百模大战”洗礼之后&#xff0c;人们发现无论是算力成本还是内容生成的安全合规问题&#xff0c;都让大模型很难直接应用于机器与人对话的实际业务中。 这其…

linux基础命令篇:Linux基础命令讲解——文件浏览(cat、less、head、tail和grep)

Linux基础命令讲解——文件浏览&#xff08;cat、less、head、tail和grep&#xff09; 本文详细介绍Linux中的cat、less、head、tail和grep命令&#xff0c;这些命令在日常工作中非常实用&#xff0c;以下是关于这些命令的详细介绍&#xff1a; 1. cat命令&#xff1a;用于查看…

光伏发电量预测(Python代码,CNN结合LSTM,TensorFlow框架)

1.数据集&#xff08;开始位置&#xff09;&#xff0c;数据集免费下载链接&#xff1a;https://download.csdn.net/download/qq_40840797/89051099 数据集一共8列&#xff0c;第一列是时间&#xff0c;特征列一共有6列&#xff1a;"WindSpeed" - 风速 "Sunshi…

利用HIVE的窗口函数进行SQL查询中出现的问题记录

student_info部分数据 score_info部分数据 course_info 1、问题复现 --完整SQL selectsti.stu_id,sti.stu_name,concat_ws(",",collect_set(ci.course_name)) over(partition by sti.stu_id) fromstudent_info sti left joinscore_info sci onsti.stu_idsci.stu_id l…

kubernetes(K8S)学习(六):K8S之Dashboard图形界面

K8S之Dashboard图形界面 一、Dashboard简介二、k8s安装Dashboard(1)下载Dashboard镜像&#xff08;可选&#xff09;(2)根据yaml文件创建资源(3)查看资源(4)生成登录需要的token(5)使用火狐 / 搜狗浏览器访问&#xff08;个人用的搜狗&#xff09; 一、Dashboard简介 官网&…

基于哈希槽的docker三主三从redis集群配置

目录 一、三主三从redis集群配置 1、关闭防火墙启动docker后台服务 2、新建6个docker容器redis实例 3、进入容器redis-node-1为6台机器构建集群关系 3.1进入容器 3.2构建主从关系 4、查看集群状态 4.1链接进入6381作为切入点 二、主从容错切换迁移按例 1、数据读写存储…

【蓝桥杯选拔赛真题50】C++简易炸弹超人 第十四届蓝桥杯青少年创意编程大赛 算法思维 C++编程选拔赛真题解析

目录 C简易炸弹超人 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、推荐资料 C简易炸弹超人 第十四届蓝桥杯青少年创意编程大赛C选拔赛真题 一、题目要求 1、编程实现 有一块矩形游戏场地&#x…

【动态规划】1223. 掷骰子模拟

作者推荐 视频算法专题 LeetCode1223. 掷骰子模拟 有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数。 不过我们在使用它时有个约束&#xff0c;就是使得投掷骰子时&#xff0c;连续 掷出数字 i 的次数不能超过 rollMax[i]&#xff08;i 从 1 开始编号&#xff09…

FreeRTOS学习 -- 12、内存管理

1、内存管理简介 &#xff08;1&#xff09;创建任务、队列、信号量等对象时&#xff0c;两种内存创建方法&#xff1a; 动态内存创建&#xff1a;自动地从FreeRTOS管理的内存堆中申请创建对象所需的内存&#xff0c;并且在对象删除后&#xff0c;可将这块内存释放回FreeRTOS管…

unbuntu mysql8.0新建用户及开启远程连接

MySQL更新到8.0以上版本后&#xff0c;在创建连接远程的用户的时候和之前5.x的版本有了很大的不同&#xff0c;不能使用原来同时创建用户和授权的命令。 以下是记录的MySQL8.0创建用户并授权的命令&#xff1a; 查看用户表&#xff1a; user mysql; select host,user,authen…

【医学嵌入模型】中文医疗文本处理大模型 PCL-MedBERT

中文医疗文本处理大模型 PCL-MedBERT 提出背景对ELECTRA限制的深入分析eHealth的创新方法实体识别关系抽取 总结 最近再做医学项目&#xff0c;需要从文本中抽取医学概念和关系&#xff0c;通用模型的抽取效果还可以。 但还想找医学嵌入模型&#xff0c;能够更准确地从文本中识…

每天学习一个Linux命令之scp

每天学习一个Linux命令之scp 在Linux系统中&#xff0c;scp&#xff08;Secure Copy&#xff09;是一个用于在本地主机和远程主机之间进行文件传输的命令行工具。它基于SSH协议&#xff0c;通过加密方式传输文件&#xff0c;确保传输的安全性和完整性。scp命令非常强大且使用简…

mapbox-gl扩展sprites图片

在mapbox-gl.js中&#xff0c;通过在styles中设置sprite和glyphs&#xff0c;实现样式图标和字体的加载。而一旦style加载完成&#xff0c;如果重置地图中的style&#xff0c;则会导致地图全部重新加载&#xff0c;图层的顺序&#xff0c;地图上的要素&#xff0c;都会丢失&…

InputStreamReader类详解

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

(十二)图像的Sobel梯度锐化

环境&#xff1a;Windows10专业版 IDEA2021.2.3 jdk11.0.1 OpenCV-460.jar 系列文章&#xff1a; &#xff08;一&#xff09;PythonGDAL实现BSQ&#xff0c;BIP&#xff0c;BIL格式的相互转换 &#xff08;二&#xff09;BSQ,BIL,BIP存储格式的相互转换算法 &#xff08;三…

Science Robotics 逼真面部表情的机器人

人类可以产生数千种不同的面部表情来传达无数微妙的情绪状态&#xff0c;这种能力是人类社会互动中最有效和最有效的界面之一。在 2019 年冠状病毒病流行期间&#xff0c;口罩使社交互动变得尴尬&#xff0c;因为它们掩盖了面部表情。同时&#xff0c;当摄像机打开时&#xff0…

鸿蒙OS开发实例:【消息传递】

介绍 在HarmonyOS中&#xff0c;参考官方指导&#xff0c;其实你会发现在‘指南’和‘API参考’两个文档中&#xff0c;对消息传递使用的技术不是一对一的关系&#xff0c;那么今天这篇文章带你全面了解HarmonyOS 中的消息传递 概况 参照官方指导&#xff0c;我总结了两部分…

java中的继承和组合

继承 在java中继承指的是让类与类之间产生父子关系&#xff0c;被继承的类叫做父类或者基类、超类&#xff0c;继承的类叫做子类或者派生类。这里所说的继承和现实生活中的继承可以理解为同一个意思。当子类继承父类时&#xff0c;子类就能使用父类之中的非私有成员&#xff0c…

Topaz Video AI for mac 视频增强软件

Topaz Video AI for Mac是一款专为Mac用户设计的视频增强软件&#xff0c;它利用先进的人工智能技术和机器学习算法&#xff0c;为用户提供卓越的视频编辑和增强体验。 软件下载&#xff1a;Topaz Video AI for mac v4.2.2激活版 这款软件能够快速提高视频的清晰度、色彩饱和度…