手写SpringBoot(三)之自动配置
系列文章目录
手写SpringBoot(一)之简易版SpringBoot
手写SpringBoot(二)之动态切换Servlet容器
手写SpringBoot(三)之自动配置
手写SpringBoot(四)之bean动态加载
手写SpringBoot(三)之自动配置
文章目录
- 系列文章目录
- 手写SpringBoot(三)之自动配置
本节主要讲解SpringBoot的自动配置
回想一下,在用原生Spring aop功能的时候,需要哪些配置?
-
引入aspectjweaver jar包
-
定义切面
-
配置切面
-
开启aop功能
为什么在SpringBoot中,只需要引入spring-boot-starter-aop
就可以直接使用aop功能?
那是因为SpringBoot定义了AOP配置类,帮我们完成了这些工作。
在spring-boot-auto-configur
e jar包中有很多SpringBoot帮我们配置好的类,其中就有AopAutoConfiguration
,提供自动开启aop等功能
现在来写一个简易版的Springboot自动配置
当我们新增了AopConfiguration配置类后,需要在MySpringBootApplication中显示import该类,如果AutoConfiguration配置类很多的情况,就要在import标签上面引入很多类。
利用ImportSelector接口,可以将需要加载的类在selectImports方法中,返回该类的全限定名称。
public class MyImportSelector implements DeferredImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[0];}
}
在该方法中,可以显示的将需要引入的配置类全路径全部加载一个数组中,然后返回,再通过@MySpringBootApplication Import 该MyImportSelector类就可以加载MyImportSelector中返回的类。
这样实现的弊端,
- 在代码中写死加载的类,需要新增时,则需要动代码。
- 无法通过外部配置来动态加载所需要的bean。
这里我们利用java spi机制来实现动态加载
Java SPI
(Service Provider Interface)是Java官方提供的一种服务发现机制
,它允许在运行时动态地加载实现
特定接口的类,而不需要在代码中显式地指定该类,从而实现解耦和灵活性
。
- 定义一个特定的接口类 AutoConfiguration
- 在resource文件夹下新建META-INF/services文件夹,在该services文件夹下新建AutoConfiguration全限定名的文件。
- 在AutoConfiguration全限定名的文件下配置需要加载的类全限定名称。
- 在MyImportSelector中实现加载java spi机制的类]
- 将@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 {@Overridepublic 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 主要设置DataSource
,MapperLocation
,ConfigLocaltion
等信息。
新建my-mybatis-spring-boot-starter模块
- 引入依赖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>
创建自动配置的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 {@Beanpublic 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;}@Beanpublic 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;}
}
为了简便,将数据源这些信息写死,这些信息可以提供配置类,给用户使用。
- 在resources目录下创建mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration></configuration>
- 创建自动配置的文件路径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>
- 创建Model包,并创建User
package cn.axj.user.model;public class User {private Integer id;private String username;...省略get set 方法@Overridepublic String toString() {return "User{" +"id=" + id +", username='" +username + '\'' +'}';}
}
- 创建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);
}
- 在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>
- 改造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 {@ResourceUserMapper userMapper;public User test(Integer id){User user = userMapper.selectById(id);return user;}
}
- 改造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 {@Resourceprivate UserService userService;@GetMapping(value = "/test")public String test(Integer id){return userService.test(id).toString();}
}
这里由于没有配置HttpMessageConverter
,无法将对象转换成字符串形式,所以用了toString()方法。
- 在UserApplication主类上,新增@MapperScan(“cn.axj.user.mapper”),扫描mapper文件。
这个问题,我在MyBatisAutoConfiguration中配置MapperScannerConfigurer,但是无法生效,mybatis不会去扫描响应的包,并生成代理对象,不知道什么原因
,但是加在主类上就可以?难道mybatis将SqlSessionFactoryBean交给Spring容器后,就不自己扫描了吗?
项目结构图如下
至此,启动项目。这里记得引入默认的tomcat 容器,不然会无法启动。
通过浏览器访问 localhost:8080/test?id=1 或者 id = 2
可以看到整合mybatis成功,成功返回数据。
相关文章:
手写SpringBoot(三)之自动配置
系列文章目录 手写SpringBoot(一)之简易版SpringBoot 手写SpringBoot(二)之动态切换Servlet容器 手写SpringBoot(三)之自动配置 手写SpringBoot(四)之bean动态加载 手写SpringBoot…...
vitepress builld报错
问题:build时报错:document/window is not defined。 背景:使用vitepress展示自定义的组件,之前build是没有问题了,由于新增了qr-code以及quill富文本组件,导致打包时报错。 原因:vitepress官…...
redis分布式锁-----基于Redis的SETNX命令的简单分布式锁实现
Redis的SETNX命令的简单分布式锁实现的Java示例 首先,确保你已经引入了Jedis这个Java Redis客户端库。你可以通过Maven或Gradle来添加依赖。 1、Maven依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifact…...
HTTP请求头中的Host表示是什么?
表示处理请求的服务器地址,由于一台服务器可能部署多个网站,如果通过域名访问,host就是域名...
apk被play protect blocked的解决方案(ADB+Appium+webdriverio)
起因:公司有海外项目,需要推广apk ,数量多,但是由于被play protect阻止安装,初版解决方案 apk加固、换签名、垃圾代码、修改资源文件的MD5,但是由于原生代码标记过于严重,推广成本高,又换了一种…...
【BlossomRPC】手把手教你写一个RPC协议
文章目录 新的开始什么是RPC?设计一个RPC需要些什么? 新的开始 经常会遇到一些项目,看着看着就发现看不懂文档了,也就是会出现一些跳过讲解的文章,使得自己很难了解某种中间件的开发全貌,所以想着自己先设计一个比较…...
算法之美:堆排序原理剖析及应用案例分解实现
这段时间持续更新关于“二叉树”的专栏文章,关心的小伙伴们对于二叉树的基本原理已经有了初步的了解。接下来,我将会更深入地探究二叉树的原理,并且展示如何将这些原理应用到更广泛的场景中去。文章将延续前面文章的风格,尽量精炼…...
Net8 ABP VNext完美集成FreeSql、SqlSugar,实现聚合根增删改查,完全去掉EFCore
没有基础的,请参考上一篇 彩蛋到最后一张图里找 参考链接 结果直接上图,没有任何业务代码 启动后,已经有了基本的CRUD功能,还扩展了批量删除,与动态查询 动态查询截图,支持分页,排序 实现原理…...
yolov8直接调用zed相机实现三维测距(python)
yolov8直接调用zed相机实现三维测距(python) 1. 相关配置2. 版本一2.1 相关代码2.2 实验结果 3. 版本二3.1 相关代码3.2 实验结果 相关链接 此项目直接调用zed相机实现三维测距,无需标定,相关内容如下: 1.yolov5直接调…...
element跑马灯/轮播图,第一页隐藏左边按钮,最后一页隐藏右边按钮(vue 开箱即用)
图示: 第一步: <el-carousel :class"changeIndex0?leftBtnNone:changeIndeximgDataList.length-1? rightBtnNone:" height"546px" :autoplay"false" change"changeNext"><el-carousel-item v-for…...
下载及安装PHP,composer,phpstudy,thinkPHP6.0框架
文章目录 目录 文章目录 前言 一、下载PHP 二、下载composer 三、下载PHPstudy 四、下载think PHP 1.下载 2.多应用开发 前言 thinkPHP是一款开源的PHP框架,它是基于MVC(Model-View-Controller)设计模式构建的。thinkPHP提供了丰富的…...
volatile使用场景总结
volatile关键字在Java中用于确保变量的可见性以及防止指令重排序,特别是在没有使用锁定机制时对变量进行读写的多线程环境中。以下是需要使用volatile修饰的一些场景: 确保变量的可见性 当一个变量被多个线程访问,且至少有一个线程在写&…...
AcWing 1413. 矩形牛棚(每日一题)
原题链接:1413. 矩形牛棚 - AcWing题库 作为一个资本家,农夫约翰希望通过购买更多的奶牛来扩大他的牛奶业务。 因此,他需要找地方建立一个新的牛棚。 约翰购买了一大块土地,这个土地可以看作是一个 R 行(编号 1∼R&…...
macOS Sonoma 14.4.1 (23E224) 正式版发布,ISO、IPSW、PKG 下载
macOS Sonoma 14.4.1 (23E224) 正式版发布,ISO、IPSW、PKG 下载 2024 年 3 月 26 日凌晨,macOS Sonoma 14.4.1 更新修复了一个可能导致连接到外部显示器的 USB 集线器无法被识别的问题。它还解决了可能导致 Java 应用程序意外退出的问题,并修…...
WPF使用外部字体,思源黑体,为例子
1.在工程中新建文件夹,命名为“Font"。 2.将下载好的字体文件复制到Font文件夹。 3.在工程中,加入静态资源 <Window.Resources><FontFamily x:Key"SYBold">/AnalyzeImage;Component/Font/#思源黑体 CN Bold</FontFamily…...
9、jenkins微服务持续集成(一)
文章目录 一、流程说明二、源码概述三、本地部署3.1 SpringCloud微服务部署本地运行微服务本地部署微服务3.2 静态Web前端部署四、Docker快速入门一、流程说明 Jenkins+Docker+SpringCloud持续集成流程说明 大致流程说明: 开发人员每天把代码提交到Gitlab代码仓库Jenkins从G…...
VOC(客户之声)赋能智能家居:打造个性化、交互式的未来生活体验
随着科技的飞速发展,智能家居已成为现代家庭不可或缺的一部分。然而,如何让智能家居更好地满足用户需求,提供更贴心、更智能的服务,一直是行业关注的焦点。在这个背景下,VOC(客户之声)作为一种用…...
时序预测 | Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测
时序预测 | Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测 目录 时序预测 | Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测(完整源码和数据…...
node.js学习(2)
版权声明 以下文章为尚硅谷PDF资料,B站视频链接:【尚硅谷Node.js零基础视频教程,nodejs新手到高手】仅供个人学习交流使用。如涉及侵权问题,请立即与本人联系,本人将积极配合删除相关内容。感谢理解和支持,…...
【pytest】测试数据存储在 Excel 或 TXT 文件中,如何参数化
如果测试数据存储在 Excel 或 TXT 文件中,你可以使用外部库来读取这些数据,并将其转化为参数化测试所需的格式。下面我将分别展示如何从这两种文件中读取数据,并用于参数化测试。 从 Excel 文件中读取测试数据 你可以使用 pandas 库来读取 …...
ubuntu22.04@Jetson Orin Nano安装配置VNC服务端
ubuntu22.04Jetson Orin Nano安装&配置VNC服务端 1. 源由2. 环境3. VNC安装Step 1: update and install xserver-xorg-video-dummyStep 2: Create config for dummy virtual displayStep3: Add the following contents in xorg.conf.dummyStep 4: Update /etc/X11/xorg.con…...
面向对象特征二:继承
继承的概述 生活中的继承 财产继承: 绿化:前人栽树,后人乘凉 “绿水青山,就是金山银山” 样貌: 继承之外,是不是还可以"进化": 继承有延续(下一代延续上一代的基因、财…...
宝塔面板CentOS Stream 8 x86 下如何安装openlitespeed
宝塔自带的软件商店里如果没办法安装,那么我们可以通过指令来手动安装: 第一步: yum install epel-release Package epel-release-8-19.el8.noarch is already installed. Dependencies resolved. Nothing to do. Complete! 第二步&#…...
LeetCode 2952.需要添加的硬币的最小数量:贪心(排序)
【LetMeFly】2952.需要添加的硬币的最小数量:贪心(排序) 力扣题目链接:https://leetcode.cn/problems/minimum-number-of-coins-to-be-added/ 给你一个下标从 0 开始的整数数组 coins,表示可用的硬币的面值ÿ…...
基于SpringBoot + Vue实现的在线装修管理系统设计与实现+毕业论文
介绍 系统包含用户、装修队、管理员三个角色 管理员: 管理员管理:管理其他管理员的账号和权限,确保系统管理的层次化和安全性。 装修队管理:审核装修队的资质,管理装修队的人员信息,监控工程进度ÿ…...
阿里云安全产品简介,Web应用防火墙与云防火墙产品各自作用介绍
在阿里云的安全类云产品中,Web应用防火墙与云防火墙是用户比较关注的安全类云产品,二则在作用上并不是完全一样的,Web应用防火墙是一款网站Web应用安全的防护产品,云防火墙是一款公共云环境下的SaaS化防火墙,本文为大家…...
作业 二维数组-定位问题
图形相似度 描述 给出两幅相同大小的黑白图像(用0-1矩阵)表示,求它们的相似度。 说明:若两幅图像在相同位置上的像素点颜色相同,则称它们在该位置具有相同的像素点。 两幅图像的相似度定义为相同像素点数占总像素点数…...
通过Jmeter准备压测数据-mysql示例
1、新建线程组 总共30万条数据 2、创建jdbc链接 创建jdbc连接配置 配置mysql连接 需要在jmeter安装的路径\apache-jmeter-5.6.3\lib\ext 目录下添加mysql 驱动 3、创建jdbc请求 jdbc链接名称需要与上一步中的保持一致,同时添加insert语句 例如 INSERT INTO test…...
如何系统的自学python?
系统地自学Python是一个循序渐进的过程,以下是一份详细的指南,帮助你从零开始逐步掌握这门语言: 1、了解Python及其应用场景: 阅读关于Python的简介,理解它为何流行,以及在哪些领域(如Web开发…...
记录一个写自定义Flume拦截器遇到的错误
先说结论: 【结论1】配置文件中包名要写正确 vim flume1.conf ... a1.sources.r1.interceptors.i1.type com.atguigu.flume.interceptor.MyInterceptor2$MyBuilder ... 标红的是包名,表黄的是类名,标蓝的是自己加的内部类名。这三个都…...
泉州网站关键词推广/学seo如何入门
Java之封装与访问权限控制(一)对于封装的概念,我总觉得自己还是挺了解的,但是真要我说,还真说不出个啥来。我只能默默地通过身边的例子加上书本理论完善我对封装的认识。就比如,我们在玩游戏的时候,我们只能通过完成指…...
网站建设与维护的软件/长沙网站托管优化
首先有一个登录界面: 这个登录界面还有一个效果就是关闭的时候缓慢消失,视觉上更好看一点: QPropertyAnimation *animation new QPropertyAnimation(this,"windowOpacity");animation->setDuration(1000);animation->setSt…...
修改wordpress后台文字/泰州seo网络公司
urllib 模块提供的 urlretrieve() 函数。urlretrieve() 方法直接将远程数据下载到本地。 >>> help(urllib.urlretrieve) Help on function urlretrieve in module urllib: urlretrieve(url, filenameNone, reporthookNone, dataNone) 参数 finename 指定了保存本地路径…...
自助建网站/雅思培训机构哪家好机构排名
根据调研机构Gartner公司的预计,2020年全球云存储收入将以每年超过28%的速度增长,将达到650亿美元。其驱动力是为了实现规模经济,使基于云计算的解决方案能够提供比内部部署系统更具成本效益的主存储和备份存储。根据调研机构Gartner公司的预…...
网站建设怎么样让图片翻滚/怎么注册域名网址
我不明白的是bBar(a)。它是做什么的?律师怎么会拿a作为论据?这不意味着酒吧继承自a吗?Bar.Foo1Foo是什么?是否意味着Foo1是类Foo()的实例?当Foo1本身是一个对象时,我们如何访问它?b.arg.variabl…...
discuz可以做公司网站/发稿服务
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼public List> findMovieByType(String moviename,String type,String spinnerActor,String spinnerYear,String television,int pageNum,int numSize){int m (pageNum-1)*DbUtil.page_Num;List> movieTypeList new ArrayLi…...