【万字长文】SpringBoot整合MyBatis搭建MySQL多数据源完整教程(提供Gitee源码)

前言:在我往期的博客介绍了2种关于如何使用SpringBoot搭建多数据源操作,本期博客我参考的是目前主流的框架,把最后一种整合多数据源的方式以博客的形式讲解完,整合的过程比较传统和复杂,不过我依旧会把每个实体类的思路都给大家讲解清楚的,项目的最后我都会提供Gitee源码地址。

往期博客:

第一种:SpringBoot+Jpa配置Oracle多数据源(提供Gitee源码)

第二种:SpringBoot+Mybatis搭建Oracle多数据源配置简述(提供Gitee源码)

目录

一、导入pom依赖

二、yml配置文件

三、数据源枚举类 

四、Spring工具类

五、配置类

5.1、数据源切换处理类

5.2、动态数据源路由类

5.3、Druid配置属性

5.4、多数据源核心配置类 

六、自定义多数据源切换注解

七、动态数据源的切面类 

八、项目完整截图

九、使用方法

十、Gitee源码 

十一、总结


一、导入pom依赖

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Lombok驱动依赖 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- MySQL驱动依赖 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version></dependency><!--Mybatis依赖--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><!-- 阿里数据库连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency><!--aop依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

二、yml配置文件

# Mybatis配置
mybatis:# 配置mapper的扫描,找到所有的mapper.xml映射文件mapper-locations: classpath:mapper/*/*.xml# 数据源配置
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverdruid:# 主库数据源master:url: jdbc:mysql://localhost:3306/master?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username:password: # 从库数据源slave:# 从数据源开关/默认关闭enabled: trueurl: jdbc:mysql://localhost:3306/slave?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username:password:# 初始连接数initialSize: 5# 最小连接池数量minIdle: 10# 最大连接池数量maxActive: 20# 配置获取连接等待超时的时间maxWait: 60000# 配置连接超时时间connectTimeout: 30000# 配置网络超时时间socketTimeout: 60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒timeBetweenEvictionRunsMillis: 60000# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis: 300000# 配置一个连接在池中最大生存的时间,单位是毫秒maxEvictableIdleTimeMillis: 900000# 配置检测连接是否有效validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsewebStatFilter:enabled: truestatViewServlet:enabled: true# 设置白名单,不填则允许所有访问allow:url-pattern: /druid/*filter:stat:enabled: true# 慢SQL记录log-slow-sql: trueslow-sql-millis: 1000merge-sql: truewall:config:multi-statement-allow: true

三、数据源枚举类 

public enum DataSourceType
{/*** 主库*/MASTER,/*** 从库*/SLAVE
}

四、Spring工具类

主要作用是提供通过名字获取Bean实例的静态方法。

1、实现BeanFactoryPostProcessor和ApplicationContextAware接口,在Spring容器初始化时,将ConfigurableListableBeanFactory和ApplicationContext的实例保存在静态变量中。

@Component标记此工具类交给Spring容器托管。

@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware{/** Spring应用上下文环境 */private static ConfigurableListableBeanFactory beanFactory;private static ApplicationContext applicationContext;}

2、postProcessBeanFactory方法会在Bean定义加载完成但实例化之前执行,这时保存BeanFactory实例。

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{SpringUtils.beanFactory = beanFactory;
}

3、setApplicationContext会在上下文准备完成后执行,这时保存ApplicationContext实例。

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{SpringUtils.applicationContext = applicationContext;
}

4、 提供getBean方法,根据名字从静态的BeanFactory中获取Bean实例。

@SuppressWarnings("unchecked")表示去抑制未检查的转型、参数化变量相关的警告。

@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{return (T) beanFactory.getBean(name);
}

完整代码: 

package com.example.multiple.utils;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;/*** spring工具类 方便在非spring管理环境中获取bean*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{/** Spring应用上下文环境 */private static ConfigurableListableBeanFactory beanFactory;private static ApplicationContext applicationContext;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException{SpringUtils.beanFactory = beanFactory;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException{SpringUtils.applicationContext = applicationContext;}/*** 获取对象** @param name* @return Object 一个以所给名字注册的bean的实例* @throws BeansException**/@SuppressWarnings("unchecked")public static <T> T getBean(String name) throws BeansException{return (T) beanFactory.getBean(name);}}

五、配置类

5.1、数据源切换处理类

1、定义了一个ThreadLocal类型的CONTEXT_HOLDER,它将为每个线程提供一个独立的副本storage。

private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

2、setDataSourceType方法用于设置当前线程要使用的数据源类型,会把类型存入CONTEXT_HOLDER这个ThreadLocal中。

public static void setDataSourceType(String dsType)
{log.info("切换到{}数据源", dsType);CONTEXT_HOLDER.set(dsType);
}

3、getDataSourceType用于获取当前线程所使用的数据源类型,是从CONTEXT_HOLDER这个ThreadLocal中获取。

public static String getDataSourceType()
{return CONTEXT_HOLDER.get();
}

4、clearDataSourceType用于清空当前线程的数据源类型信息。

public static void clearDataSourceType()
{CONTEXT_HOLDER.remove();
}

这样使用ThreadLocal就能实现一个线程内部共享这个数据源类型变量,并且每个线程的变量都是独立的。 

完整代码:

package com.example.multiple.config.datasource;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** 数据源切换处理*/
public class DynamicDataSourceContextHolder
{public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);/*** 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。*/private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();/*** 设置数据源的变量*/public static void setDataSourceType(String dsType){log.info("切换到{}数据源", dsType);CONTEXT_HOLDER.set(dsType);}/*** 获得数据源的变量*/public static String getDataSourceType(){return CONTEXT_HOLDER.get();}/*** 清空数据源变量*/public static void clearDataSourceType(){CONTEXT_HOLDER.remove();}
}

5.2、动态数据源路由类

1、这个DynamicDataSource类继承了AbstractRoutingDataSource,实现了一个动态切换数据源的路由Datasource。

public class DynamicDataSource extends AbstractRoutingDataSource{}

2、构造方法中调用父类的方法设置默认数据源和所有目标数据源Map。

public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
{super.setDefaultTargetDataSource(defaultTargetDataSource);super.setTargetDataSources(targetDataSources);super.afterPropertiesSet();
}

3、实现了determineCurrentLookupKey方法,在此方法中,通过DynamicDataSourceContextHolder工具类获取当前线程上的数据源类型,然后将当前Lookup Key设置为这个数据源类型。

@Override
protected Object determineCurrentLookupKey()
{return DynamicDataSourceContextHolder.getDataSourceType();
}

最后,AbstractRoutingDataSource会根据这个Lookup Key,在目标数据源Map中查找对应的DataSource,作为获取连接的源。这样通过determineCurrentLookupKey的实现,动态返回当前线程上的DataSource类型。配合DynamicDataSourceContextHolder来切换设置线程DataSource类型。就能根据线程运行时的数据源类型,动态切换到不同的数据源上获取连接。实现了根据当前运行情况动态切换多个数据源的功能。

完整代码:

package com.example.multiple.config.datasource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;
import java.util.Map;/*** 动态数据源*/
public class DynamicDataSource extends AbstractRoutingDataSource
{public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources){super.setDefaultTargetDataSource(defaultTargetDataSource);super.setTargetDataSources(targetDataSources);super.afterPropertiesSet();}@Overrideprotected Object determineCurrentLookupKey(){return DynamicDataSourceContextHolder.getDataSourceType();}
}

5.3、Druid配置属性

从配置中加载属性,并设置到DruidDataSource中,创建一个可用的DataSource实例,代码上都有注释,这边就不多做讲解了。

完整代码:

package com.example.multiple.config.properties;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;/*** druid 配置属性**/
@Configuration
public class DruidProperties
{@Value("${spring.datasource.druid.initialSize}")private int initialSize;@Value("${spring.datasource.druid.minIdle}")private int minIdle;@Value("${spring.datasource.druid.maxActive}")private int maxActive;@Value("${spring.datasource.druid.maxWait}")private int maxWait;@Value("${spring.datasource.druid.connectTimeout}")private int connectTimeout;@Value("${spring.datasource.druid.socketTimeout}")private int socketTimeout;@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")private int timeBetweenEvictionRunsMillis;@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")private int minEvictableIdleTimeMillis;@Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")private int maxEvictableIdleTimeMillis;@Value("${spring.datasource.druid.validationQuery}")private String validationQuery;@Value("${spring.datasource.druid.testWhileIdle}")private boolean testWhileIdle;@Value("${spring.datasource.druid.testOnBorrow}")private boolean testOnBorrow;@Value("${spring.datasource.druid.testOnReturn}")private boolean testOnReturn;public DruidDataSource dataSource(DruidDataSource datasource){/** 配置初始化大小、最小、最大 */datasource.setInitialSize(initialSize);datasource.setMaxActive(maxActive);datasource.setMinIdle(minIdle);/** 配置获取连接等待超时的时间 */datasource.setMaxWait(maxWait);/** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */datasource.setConnectTimeout(connectTimeout);/** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */datasource.setSocketTimeout(socketTimeout);/** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);/** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);/*** 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。*/datasource.setValidationQuery(validationQuery);/** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */datasource.setTestWhileIdle(testWhileIdle);/** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */datasource.setTestOnBorrow(testOnBorrow);/** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */datasource.setTestOnReturn(testOnReturn);return datasource;}
}

5.4、多数据源核心配置类 

 1、使用@Configuration标记此类为核心配置类。

@Configuration
public class DruidConfig{
}

2、注册一个创建master主数据源的Bean

第一步、@ConfigurationProperties注解加载名称为“spring.datasource.druid.master”的属性配置。

第二步、通过DruidDataSourceBuilder创建一个DruidDataSource实例。

第三步、将DruidDataSource实例传入DruidProperties的dataSource方法中。

第四步、DruidProperties会根据加载的属性配置,设置DruidDataSource的各种属性,如最大连接数,最小连接数等。

第五步、dataSource方法会返回设置好属性的DruidDataSource实例。

最后这个配置好的DruidDataSource将作为masterDataSource Bean的实例,所以它实现了使用Spring Boot的属性配置方式,加载druid.master的配置,并设置到DruidDataSource中,创建一个可用的master数据源Bean。 

@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(DruidProperties druidProperties)
{DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);
}

3、注册一个创建salve从数据源的Bean,整体步骤和上面类似,不多做阐述。

@ConditionalOnProperty注解的作用是,根据给定的条件来决定这个Bean是否创建。只有当配置了spring.datasource.druid.slave.enabled=true时,这个slaveDataSource的Bean才会被创建。 

@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties)
{DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);
}

4、设置数据源的方法setDataSource

第一步、方法接收一个Map对象targetDataSources,数据源名称sourceName和数据源Bean名称beanName作为参数。

第二步、从Spring容器中通过SpringUtils工具类获取beanName对应的DataSource Bean实例。

最后、将得到的DataSource实例根据sourceNamekey,存入targetDataSources这个Map中。 

public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName){try{DataSource dataSource = SpringUtils.getBean(beanName);targetDataSources.put(sourceName, dataSource);}catch (Exception e){e.printStackTrace();}}

这样通过beanName加载DataSource,并使用自定义的sourceName作为key存储到targetDataSources中。目标是构建一个自定义名称与数据源实例的映射关系,存在targetDataSources这个容器中。这可以实现多数据源的配置管理,通过不同的sourceName获取对应的数据源实例。 

5.5、实现动态数据源的配置

第一步、创建一个Map用于存放目标数据源,并将名为masterDataSource的Bean作为主数据源放入Map。

第二步、调用setDataSource方法将名为slaveDataSource的Bean放入Map,Key设置为SLAVE。

最后、使用主数据源实例和数据源Map创建DynamicDataSource实例,并设置为Primary,即默认的数据源。

@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource)
{Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(masterDataSource, targetDataSources);
}

完整代码:

package com.example.multiple.config;import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;import com.example.multiple.config.datasource.DynamicDataSource;
import com.example.multiple.enums.DataSourceType;
import com.example.multiple.config.properties.DruidProperties;
import com.example.multiple.utils.SpringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;/*** druid 配置多数据源*/
@Configuration
public class DruidConfig
{@Bean@ConfigurationProperties("spring.datasource.druid.master")public DataSource masterDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}@Bean@ConfigurationProperties("spring.datasource.druid.slave")@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")public DataSource slaveDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource(DataSource masterDataSource){Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(masterDataSource, targetDataSources);}/*** 设置数据源** @param targetDataSources 备选数据源集合* @param sourceName 数据源名称* @param beanName bean名称*/public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName){try{DataSource dataSource = SpringUtils.getBean(beanName);targetDataSources.put(sourceName, dataSource);}catch (Exception e){e.printStackTrace();}}}

六、自定义多数据源切换注解

1、@Target和@Retention表示该注解可以用于方法和类上,并且可以保留到运行时。

2、@Documented表示该注解会包含在javadoc中。

3、@Inherited表示该注解可以被子类继承。

注解只有一个value属性,类型是DataSourceType枚举,默认值为MASTER。

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{/*** 切换数据源名称*/public DataSourceType value() default DataSourceType.MASTER;
}

七、动态数据源的切面类 

1、@Aspect标记此类为切面类、@Order指定Bean的加载顺序、@Component标记此类交给Spring容器托管。

@Aspect
@Order(1)
@Component
public class DataSourceAspect{
}

2、@Pointcut定义了切点,这里是匹配所有@DataSource注解的方法或类。

@Pointcut("@annotation(com.example.multiple.annotation.DataSource)"+ "|| @within(com.example.multiple.annotation.DataSource)")
public void dsPointCut()
{
}

3、获取需要切换的数据源

第一步、通过point.getSignature()获取方法签名,并转成MethodSignature类型。

第二步、调用AnnotationUtils的findAnnotation方法,以方法为目标,获取其上的@DataSource注解。

第三步、如果注解不为空,直接返回该注解。

最后、如果方法上没有注解,则以方法所在类为目标再次查找@DataSource注解,并返回。

public DataSource getDataSource(ProceedingJoinPoint point)
{MethodSignature signature = (MethodSignature) point.getSignature();DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);if (Objects.nonNull(dataSource)){return dataSource;}return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
}

4、@Around定义了切点的处理逻辑为环绕增强 

第一步、调用getDataSource方法获取目标方法需要的DataSource注解。

第二步、判断如果注解不为空,则调用DynamicDataSourceContextHolder的setDataSourceType方法,将注解value的值(数据源类型)设置到其中。

第三步、调用ProceedingJoinPoint的proceed方法,执行目标方法。

最后、在finally中,调用DynamicDataSourceContextHolder的clearDataSourceType方法,清空线程本地的DataSourceType。

 @Around("dsPointCut()")public Object around(ProceedingJoinPoint point) throws Throwable{DataSource dataSource = getDataSource(point);if (dataSource != null){DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());}try{return point.proceed();}finally{// 销毁数据源 在执行方法之后DynamicDataSourceContextHolder.clearDataSourceType();}}

完整代码:

package com.example.multiple.aspectj;import com.example.multiple.annotation.DataSource;
import com.example.multiple.config.datasource.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.util.Objects;/*** 多数据源处理*/
@Aspect
@Order(1)
@Component
public class DataSourceAspect
{protected Logger logger = LoggerFactory.getLogger(getClass());@Pointcut("@annotation(com.example.multiple.annotation.DataSource)"+ "|| @within(com.example.multiple.annotation.DataSource)")public void dsPointCut(){}@Around("dsPointCut()")public Object around(ProceedingJoinPoint point) throws Throwable{DataSource dataSource = getDataSource(point);if (dataSource != null){DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());}try{return point.proceed();}finally{// 销毁数据源 在执行方法之后DynamicDataSourceContextHolder.clearDataSourceType();}}/*** 获取需要切换的数据源*/public DataSource getDataSource(ProceedingJoinPoint point){MethodSignature signature = (MethodSignature) point.getSignature();DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);if (Objects.nonNull(dataSource)){return dataSource;}return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);}
}

八、项目完整截图

以上就把多数据源的核心配置讲解完毕了,剩下的就是一些常规整合MyBatis的操作了,我都放在码云上了,这边就不过多写了,完整的项目建包就这样。

九、使用方法

在Mapper层或者Service层上使用@DataSource自定义注解切换到指定数据源即可。

@Mapper
@DataSource(DataSourceType.SLAVE)
public interface SlaveMapper {public List<Logger> select();}

运行结果如下: 

十、Gitee源码 

在yml文件配置好自己的主从数据源一键启动项目即可

项目地址:SpringBoot整合MyBatis搭建MySQL多数据源

十一、总结

以上就是我对于SpringBoot如何整合多数据源的技术分析,也是比较传统化的方式,比较复杂,如有问题欢迎评论区讨论!

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

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

相关文章

利用鸿鹄可观测性监控Istio Ingress网关

一、需求描述 在上一篇《利用Vector和鸿鹄搭建微服务应用的可观测性平台》中&#xff0c;阐述了微服务的基本概念、优点及如何利用鸿鹄来处理分布式应用的日志。本文将进一步讨论微服务架构面临的问题、服务网格及鸿鹄处理Istio Gateway的独特优势。 1.1 微服务架构面临的挑战 …

基于SpringBoot+Vue的地方废物回收机构管理系统设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

企业生产管理的核心工作是什么?

作为管理者&#xff0c;一谈到生产管理&#xff0c;你可能会想到很多生产过程中的问题&#xff1a; 产量无法实时统计&#xff1b; 计划不能跟踪进度&#xff1b; 质量追溯无法实现...... 等等一系列核心问题。 结合这些核心痛点&#xff0c;分享一套符合现在生产的智能化解决…

初阶结构体(超详解)

初阶结构体 1. 结构体的声明1.1 结构的基础知识1.2 结构的声明1.3 结构成员的类型1.4 结构体变量的初始化和定义 2. 结构体的访问3. 结构体传参 1. 结构体的声明 1.1 结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量 结…

cmake 配置Visual studio的调试命令

配置代码如截图&#xff1a; set_property(TARGET ${TARGET_NAME} PROPERTY VS_DEBUGGER_COMMAND "./consoleTest.exe") set_property(TARGET ${TARGET_NAME} PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "./config/labelDriver.cfg") set_propert…

服务机器人有哪些品类

服务机器人是指具备自主运动、感知环境、实现人机交互等能力的机器人&#xff0c;它可以被应用于不同的场景&#xff0c;如餐饮、医疗、物流等行业。根据其功能和应用场景的不同&#xff0c;服务机器人可以分为以下几类&#xff1a;1. 餐饮服务机器人 随着社会发展和人们需…

Jmeter阶梯式加压测试

熟悉阿里云性能测试PTS的都可以看到压测配置设置如下图&#xff0c;相比Jmeter简单的线程设置&#xff0c;要合理更直观。 但是我们会去研究&#xff0c;性能测试中&#xff0c;有时需要模拟一种实际生产中经常出现的情况&#xff0c; 即&#xff1a;从某个值开始不断增加压力…

IC秋招| 秋招怎么做准备,这份攻略请收下!

近期大家关注的就是秋招了&#xff0c;如今一些企业已经开启了提前批招聘&#xff0c;不少同学已经投递了简历&#xff0c;但内心还是非常焦虑&#xff0c;那么今年的秋招到底该如何准备呢&#xff1f; 简历投递思路建议 刚开始对大厂没有把握的话&#xff0c;可以先约初创或…

redis安装,开启自启动(Windows)

1、下载redis包 Releases tporadowski/redis GitHub 2、配置环境变量 path中添加redis解压后的路径&#xff1b; 3、配置文件修改 注释掉 bind 127.0.0.1 使其他ip可以远程访问&#xff1b; 修改redis密码 4、启动Redis服务 redis-server.exe --service-install redis.w…

百度地图点标记加调用

先看效果 PHP代码 <?phpnamespace kds_addons\edata\controller;use think\addons\Controller; use think\Db;class Maps extends Controller {// 经纬度计算面积function calculate_area($points){$totalArea 0;$numPoints count($points);if ($numPoints > 2) {f…

React组件进阶之children属性,props校验与默认值以及静态属性static

React组件进阶之children属性,props校验与默认值以及静态属性static 一、children属性二、props校验2.1 props说明2.2 prop-types的安装2.3 props校验规则2.4 props默认值 三、静态属性static 一、children属性 children 属性&#xff1a;表示该组件的子节点&#xff0c;只要组…

Java集合详解

Java集合详解 一、基本概要1.什么是集合2.常见的集合有哪些 二、基本知识1.集合框架底层数据结构1.1 Collection1.2 Map 2.线程安全的集合 三、集合具体讲解0.前沿知识0. ❤关于Iterator 迭代器1.Iterator迭代器是什么&#xff1f;用来干什么&#xff1f;&#xff08;用来遍历集…

Node.js之express框架学习心得

Node.js&#xff1a;颠覆传统的服务器端开发 Node.js是基于Chrome V8引擎构建的JavaScript运行时&#xff0c;它采用了完全不同的开发模型。Node.js使用事件驱动和非阻塞I/O的方式处理请求&#xff0c;通过单线程和异步机制&#xff0c;实现高效的并发处理。这意味着在Node.js中…

AdvancedInstaller打包程序

文章目录 1. AdvancedInstaller 下载2. AdvancedInstaller 启动3. 新建工程4. 配置安装包详细信息5. 配置安装参数6. 添加要打包的文件7. 设置安装完成后启动程序8. 构建打包 1. AdvancedInstaller 下载 下载网址&#xff1a;https://www.advancedinstaller.com/ 2. AdvancedIn…

error: #5: cannot open source input file “core_cmInstr.h“

GD32F103VET6和STM32F103VET6引脚兼容。 GD32F103VET6工程模板需要包含头文件&#xff1a;core_cmInstr.h和core_cmFunc.h&#xff0c;这个和STM32F103还是有区别的&#xff0c;否则会报错&#xff0c;如下&#xff1a; error: #5: cannot open source input file "core…

Springboot之把外部依赖包纳入Spring容器管理的两种方式

前言 在Spring boot项目中&#xff0c;凡是标记有Component、Controller、Service、Configuration、Bean等注解的类&#xff0c;Spring boot都会在容器启动的时候&#xff0c;自动创建bean并纳入到Spring容器中进行管理&#xff0c;这样就可以使用Autowired等注解&#xff0c;…

MQ面试题3

1、讲一讲Kafka与RocketMQ中存储设计的异同&#xff1f; Kafka 中文件的布局是以 Topic/partition &#xff0c;每一个分区一个物理文件夹&#xff0c;在分区文件级别实现文件顺序写&#xff0c;如果一个Kafka集群中拥有成百上千个主题&#xff0c;每一个主题拥有上百个分区&am…

《Vue3+Typescript》一个简单的日历组件实现

这是一个没有套路的前端博主&#xff0c;热衷各种前端向的骚操作&#xff0c;经常想到哪就写到哪&#xff0c;如果有感兴趣的技术和前端效果可以留言&#xff5e;博主看到后会去代替大家踩坑的&#xff5e; 主页: oliver尹的主页 格言: 跌倒了爬起来就好&#xff5e; 目录 一、…

架构的分类

目录 一、 RUP41 架构 1.1 RUP41架构方法概述 1.2 RUP41架构总体 1.3 RUP41架构方法内容 1.3.1 逻辑视图 1.3.2 开发视图 1.3.3 物理视图 1.3.4 处理视图 1.3.5 场景视图 ​二、 TOGAF9 架构 2.1 TOGAF9 架构概述 2.2 TOGAF9 架构分类 2.2.1 业务架构 2.2.2 数据架…