SpringBoot国际化配置组件支持本地配置和数据库配置
文章目录
- 0. 前言
- i18n-spring-boot-starter
- 1. 使用方式
- 0.引入依赖
- 1.配置项
- 2.初始化国际化配置表
- 3.如何使用
- 2. 核心源码
- 实现一个拦截器I18nInterceptor
- I18nMessageResource 加载国际化配置
- 3.源码地址
0. 前言
写个了原生的SpringBoot国际化配置组件支持本地配置和数据库配置
背景:最近花时间把项目用到的国际化组件Starter 重构了一下,使用更简单。基本上支持从本地配置读取和数据库配置读取,支持web端和小程序等移动端的国际化需求。
i18n-spring-boot-starter
1. 使用方式
Spring Boot 国际化组件
0.引入依赖
代码在本地打包后
给需要国际化的工程引入
<dependency><groupId>com.bdkjzx.project</groupId><artifactId>i18n-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>
1.配置项
#添加国际化
spring.ex.i18n.enable=true
# 如果未翻译是否将code 初始化入库
spring.ex.i18n.mark=false
spring.ex.i18n.default-locale=zh-CN
spring.ex.i18n.data-source=primary
spring.ex.i18n.config-table=config_i18n_message
2.初始化国际化配置表
CREATE TABLE `config_i18n_message` (`code` varchar(128) NOT NULL,`zh-CN` varchar(128) DEFAULT NULL,`zh-TW` varchar(128) DEFAULT NULL,`en-US` varchar(1024) DEFAULT NULL COMMENT '英文',PRIMARY KEY (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='国际化配置表'
如果本地配置的话使用原生配置方式.缺点是需要手动更新,并且每个服务都需要配置。建议使用数据库表配置
messages_zh_CN.properties , messages_en_US.properties
3.如何使用
I.n("操作成功")
或者在返回的统一结果对象上,以下是个示例,你需要加在你的项目的统一响应中
public class ApiResponse<T> {private int code;private String message;private T data;private ErrorDetails error;public ApiResponse() {}/*** message给消息进行国际化包装* @param message*/public ApiResponse(int code, String message, T data, ErrorDetails error) {this.code = code;this.message = I.n(message);this.data = data;this.error = error;}// Getter and Setter methodspublic int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMessage() {return message;}/*** 给消息进行国际化包装* @param message*/public void setMessage(String message) {this.message = I.n(message);}public T getData() {return data;}public void setData(T data) {this.data = data;}public ErrorDetails getError() {return error;}public void setError(ErrorDetails error) {this.error = error;}
}
5.扩展请看入口
com.bdkjzx.project.i18n.config.I18nAutoConfig
2. 核心源码
package com.bdkjzx.project.i18n.config;import com.bdkjzx.project.i18n.I18nHolder;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = "spring.ex.i18n")
@Setter
@Getter
public class I18nProperties {/*** 是否启用国际化功能:<br>* - 启用:会创建和国际化相关的数据源、缓存等等;<br>* - 不启用:{@link I18nHolder} 可以正常使用,返回原值,不会创建国际化相关的各种Bean<br>** 默认:不启用,需要手动开启*/private Boolean enable = false;/*** 国际化数据表所在的数据源,入需指定,则写入数据源名称。<br>* 此配置的作用是允许多个服务通过共享一个i18n配置表,从而共用一套i18n翻译。<br>* 默认为空,表示使用primary数据源。*/private String dataSource = "primary";/*** 默认地区(语言)*/private String defaultLocale = "zh_CN";/*** 查询i18n配置表的名称,用于自定义修改表。<br>* 默认:config_i18n_message*/private String configTable = "config_i18n_message";/*** i18n配置表的字段名。根据i18n配置表决定此配置<br>* 默认:code*/private String configCodeColumn = "code";/*** i18n缓存更新时间(小时数),会提供手工刷新缓存的入口,所以不必频繁刷新<br>* 默认值为-1,表示长期有效。<br>*/private Integer cacheHours = -1;/*** 当未找到i18n的code时,是否将其记录到表中,以便统一处理<br>* 默认:关闭*/private Boolean mark = false;/*** 用于记录无效code的线程池缓冲区大小*/private Integer markPoolSize = 2000;/*** 是否在 {@link com.bdkjzx.project.i18n.repository.I18nMessageResource} 未找到配置时,再使用Spring默认方案,* 从本地加载国际化资源。* 默认:关闭*/private Boolean useLocale = false;}
package com.bdkjzx.project.i18n.config;import com.bdkjzx.project.i18n.I18nHolder;
import com.bdkjzx.project.i18n.filter.I18nFilter;import com.bdkjzx.project.i18n.repository.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.context.MessageSourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import com.bdkjzx.project.i18n.interceptor.I18nInterceptor;import javax.sql.DataSource;
import java.util.Locale;
import java.util.concurrent.Executor;@Configuration
@EnableConfigurationProperties({I18nProperties.class})
@Slf4j
public class I18nAutoConfig {@Beanpublic I18nHolder getI18nUtil(@Autowired(required = false) I18nMessageResource messageSource,@Autowired(required = false) I18nLocaleHolder i18nLocaleHolder,@Autowired I18nProperties i18NProperties) {// 不论是否启用都会配置,保证这个工具类不会报错return i18NProperties.getEnable() ? new I18nHolder(messageSource, i18nLocaleHolder) : new I18nHolder();}@ConditionalOnProperty(prefix = "spring.ex.i18n", name = "enable", havingValue = "true")@Configurationstatic class I18nFilterConfig {@Autowiredprivate I18nLocaleHolder i18nLocaleHolder;@Beanpublic I18nFilter i18nFilter() {I18nFilter i18nFilter = new I18nFilter();I18nInterceptor interceptor = new I18nInterceptor();interceptor.setI18nLocaleHolder(i18nLocaleHolder);i18nFilter.setI18nInterceptor(interceptor);return i18nFilter;}}@ConditionalOnProperty(prefix = "spring.ex.i18n", name = "enable", havingValue = "true")@Configuration@EnableCaching@ComponentScan("com.bdkjzx.project.i18n")static class I18nResourceConfig {/*** 采用默认的配置文件配置 messages开头的文件,编码为utf8<br>* 如 messages_zh_CN.properties , messages_en_US.properties** @return {@link MessageSourceProperties}*/@Beanpublic MessageSourceProperties messageSourceProperties() {return new MessageSourceProperties();}@Beanpublic ResourceBundleMessageSource initResourceBundleMessageSource(MessageSourceProperties messageSourceProperties) {ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();resourceBundleMessageSource.setBasename(messageSourceProperties.getBasename());resourceBundleMessageSource.setDefaultEncoding(messageSourceProperties.getEncoding().name());return resourceBundleMessageSource;}@Bean@Autowiredpublic I18nMessageResource initMessageResource(ResourceBundleMessageSource resourceBundleMessageSource,I18nLocaleHolder i18NLocaleSettings) {I18nMessageResource i18nMessageResource = new I18nMessageResource(i18NLocaleSettings.getDefaultLocale());i18nMessageResource.setParentMessageSource(resourceBundleMessageSource);return i18nMessageResource;}@Bean@Autowiredpublic I18nLocaleHolder getI18nLocaleSetting(I18nProperties i18nProperties) {Locale locale;try {locale = new Locale.Builder().setLanguageTag(i18nProperties.getDefaultLocale().replace("_", "-").toLowerCase()).build();} catch (Exception e) {log.error(String.format("解析默认语言时出现错误, setting = %s", i18nProperties.getDefaultLocale()), e);throw new IllegalArgumentException("解析默认语言时出现错误,请查看日志");}return new I18nLocaleHolder(locale);}@Bean(name = "i18nJdbcTemplate")@ConditionalOnMissingBean(name = "i18nJdbcTemplate")public JdbcTemplate getJdbcTemplate(@Autowired(required = false) @Qualifier("i18nDataSource") DataSource i18nDataSource) {try {if (i18nDataSource == null) {log.error("未配置国家化数据源,请使用@Bean构造一个名为i18nDataSource的DataSource或者直接重新此方法");}return new JdbcTemplate(i18nDataSource);} catch (BeansException e) {log.error("无效的数据源{}", i18nDataSource, e);throw new IllegalArgumentException("创建数据源时出现错误,请查看日志");}}@Autowired@Bean(name = "defaultI18nDataLoadService")public I18nConfigDbLoader getI18nDataLoadService(I18nProperties i18nProperties,@Qualifier("i18nJdbcTemplate") JdbcTemplate jdbcTemplate) {return new SimpleI18NConfigDbLoaderImpl(i18nProperties.getConfigCodeColumn(),i18nProperties.getConfigTable(), jdbcTemplate);}@Autowired@Bean(name = "i18nCacheManager")public CacheManager getCacheManager(I18nProperties i18nProperties) {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();if (i18nProperties.getCacheHours() > 0) {// 缓存创建后,经过固定时间(小时),更新caffeineCacheManager.setCacheSpecification(String.format("refreshAfterWrite=%sH", i18nProperties.getCacheHours()));}return caffeineCacheManager;}/*** 线程池配置*/@ConditionalOnProperty(prefix = "spring.ex.i18n", name = "mark", havingValue = "true")@Configuration@EnableAsyncstatic class I18nInvalidMarkerConfig {@Bean("i18nExecutor")@Autowiredpublic Executor getAsyncExecutor(I18nProperties i18NProperties) {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(0);executor.setMaxPoolSize(2);executor.setQueueCapacity(i18NProperties.getMarkPoolSize());executor.setThreadNamePrefix("i18n-executor-");executor.initialize();return executor;}@Beanpublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new SimpleAsyncUncaughtExceptionHandler();}}}}
实现一个拦截器I18nInterceptor
作用是实现国际化(i18n)功能的拦截器。用于处理Web应用程序的国际化,即根据用户的语言设置显示对应的国际化资源文件。
- 从请求的cookie或header中获取语言设置。
- 将语言设置存储到i18nLocaleHolder中,以便在后续的请求处理中使用。
- 在请求处理完成后,清除i18nLocaleHolder中的语言设置。
package com.bdkjzx.project.i18n.interceptor;import com.bdkjzx.project.i18n.repository.I18nLocaleHolder;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;import static org.slf4j.LoggerFactory.getLogger;/*** 国际化拦截器,用于处理Web应用的国际化(i18n)。*/
@Slf4j
public class I18nInterceptor implements HandlerInterceptor {private I18nLocaleHolder i18nLocaleHolder;private final Map<String, Locale> localeMap = new HashMap<>(8);private static final String NAME_OF_LANGUAGE_SETTING = "lang";/*** 在实际处理程序方法调用之前执行的预处理方法。* 从请求的cookie或header中获取语言设置,并将其设置到i18nLocaleHolder中。* 如果语言设置为空或无效,则返回true以允许请求继续进行。*/@Overridepublic boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {String lang = getLangFromCookies(request);if (StringUtils.isEmpty(lang)) {lang = getLangFromHeader(request);}if (StringUtils.isEmpty(lang)) {return true;}try {i18nLocaleHolder.setThreadLocale(getLocaleByLang(lang));} catch (Exception e) {log.error("无效的语言设置:{}", lang, e);}return true;}/*** 在完成请求处理后执行的方法。* 清除i18nLocaleHolder中的语言设置。*/@Overridepublic void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, Exception ex) {try {i18nLocaleHolder.clear();} catch (Exception e) {log.error("清理语言设置时遇到错误:", e);}}public I18nLocaleHolder getI18nLocaleHolder() {return i18nLocaleHolder;}public void setI18nLocaleHolder(I18nLocaleHolder i18nLocaleHolder) {this.i18nLocaleHolder = i18nLocaleHolder;}/*** 根据语言设置获取Locale对象。** @param lang 语言设置* @return Locale对象*/private Locale getLocaleByLang(String lang) {return Optional.ofNullable(localeMap.get(lang)).orElseGet(() -> {Locale locale = new Locale.Builder().setLanguageTag(lang).build();localeMap.put(lang, locale);return locale;});}/*** 从cookie中获取国际化语言设置。** @param request HttpServletRequest对象* @return 国际化语言设置*/private static String getLangFromCookies(HttpServletRequest request) {String lang = Optional.ofNullable(request.getCookies()).flatMap(cookies -> Arrays.stream(cookies).filter(cookie -> NAME_OF_LANGUAGE_SETTING.equals(cookie.getName())).findFirst()).map(Cookie::getValue).orElse("");return lang;}/*** 从header中获取国际化语言设置。** @param request HttpServletRequest对象* @return 国际化语言设置*/private String getLangFromHeader(HttpServletRequest request) {String acceptLanguage = request.getHeader("Accept-Language");return Optional.ofNullable(acceptLanguage).map(lang -> lang.split(",")).filter(array -> array.length > 0).map(array -> array[0]).orElse("");}}
I18nMessageResource 加载国际化配置
支持本地和数据库
package com.bdkjzx.project.i18n.repository;import com.bdkjzx.project.i18n.config.I18nProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;import javax.annotation.PostConstruct;
import java.text.MessageFormat;
import java.util.*;
import java.util.function.BiFunction;@Slf4j
public class I18nMessageResource extends AbstractMessageSource implements ResourceLoaderAware {private final Locale defaultLocale;@Autowiredprivate List<I18nConfigDbLoader> i18NConfigDbLoaders;@Autowiredprivate I18nProperties i18NProperties;@Lazy@Autowired(required = false)private I18nConfigDbLoader i18nConfigDbLoader;private final List<BiFunction<String, Locale, String>> getTextFunctionList = new ArrayList<>();public I18nMessageResource(Locale defaultLocale) {this.defaultLocale = defaultLocale;}@PostConstructpublic void init() {if (this.i18NProperties.getEnable()) {getTextFunctionList.add(this::normalFinder);getTextFunctionList.add(this::languageFinder);getTextFunctionList.add(this::defaultLocaleFinder);if (i18NProperties.getUseLocale() && getParentMessageSource() != null) {getTextFunctionList.add(this::localFinder);getTextFunctionList.add(this::localDefaultFinder);}}}@Overridepublic void setResourceLoader(@NonNull ResourceLoader resourceLoader) {}@Overrideprotected MessageFormat resolveCode(@NonNull String code, @NonNull Locale locale) {String msg = getText(code, locale);return createMessageFormat(msg, locale);}@Overrideprotected String resolveCodeWithoutArguments(@NonNull String code, @NonNull Locale locale) {return getText(code, locale);}/*** 这是加载国际化变量的核心方法,先从自己控制的内存中取,取不到了再到资源文件中取** @param code 编码* @param locale 本地化语言* @return 查询对应语言的信息*/private String getText(String code, Locale locale) {String result = getTextWithOutMark(code, locale);if (StringUtils.isEmpty(result)) {return result;}// 确实没有这项配置,确定是否要记录logger.warn("未找到国际化配置:" + code);if (i18NProperties.getMark()) {i18nConfigDbLoader.markInvalidCode(code);}//如果最终还是取不到,返回了NULL,则外面会用默认值,如果没有默认值,最终会返回给页面变量名称,所以变量名称尽量有含义,以作为遗漏配置的最后保障return code;}public String getTextWithOutMark(String code, Locale locale) {String result = "";// 从 function list中依次使用各种策略查询for (BiFunction<String, Locale, String> func : getTextFunctionList) {result = func.apply(code, locale);if (!StringUtils.isEmpty(result)) {return result;}}return result;}/*** 从指定locale获取值** @param code i18n code* @param locale 语言* @return 查询对应语言的信息*/private String findValueFromLocale(String code, Locale locale) {String resultValue;for (I18nConfigDbLoader i18NConfigDbLoader : i18NConfigDbLoaders) {// 在loadE6I18nDictByLocaleEntity中做过缓存了resultValue = Optional.ofNullable(i18NConfigDbLoader.loadI18nDictByLocaleEntity()).flatMap(localeMap -> Optional.ofNullable(localeMap.get(locale)).map(codeMap -> codeMap.get(code))).orElse(null);if (!org.springframework.util.StringUtils.isEmpty(resultValue)) {return resultValue;}}return null;}// ====================================== 查询字符的五种策略,加入function list ======================================/*** 第一种情况:通过期望的语言类型查找** @param code 国际化代码* @param locale 语言* @return 没找到时返回null*/private String normalFinder(String code, Locale locale) {return findValueFromLocale(code, locale);}/*** 第二种情况,如果期望是 语言-国家 没有找到,那么尝试只找一下语言,比如zh-tw没找到,那就尝试找一下zh** @param code 国际化代码* @param locale 语言* @return 没找到时返回null*/private String languageFinder(String code, Locale locale) {if (locale.getLanguage() != null) {return findValueFromLocale(code, Locale.forLanguageTag(locale.getLanguage()));}return null;}/*** 第三种情况,如果没有找到 且不是默认语言包,则取默认语言包** @param code 国际化代码* @param locale 语言* @return 没找到时返回null*/private String defaultLocaleFinder(String code, Locale locale) {if (!Objects.equals(locale, defaultLocale)) {return findValueFromLocale(code, defaultLocale);}return null;}/*** 第四种情况,通过以上三种方式都没找到,那么尝试从本地配置文件加载期望的语言类型是否有** @param code 国际化代码* @param locale 语言* @return 没找到时返回null*/private String localFinder(String code, Locale locale) {String value = Objects.requireNonNull(getParentMessageSource()).getMessage(code, null, null, locale);if (logger.isDebugEnabled() && !StringUtils.isEmpty(value)) {logger.debug("从配置文件" + locale.toString() + "找到变量" + code + "=" + value);}return value;}/*** 第五种情况,如果没有找到,则从本地配置文件加载默认的语言类型是否有** @param code 国际化代码* @param locale 语言* @return 没找到时返回null*/private String localDefaultFinder(String code, Locale locale) {if (!Objects.equals(locale, defaultLocale)) {return this.localFinder(code, defaultLocale);}return null;}}
pom 文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.bdkjzx.project</groupId><artifactId>i18n-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version><name>i18n-spring-boot-starter</name><description>Spring boot 国际化配置</description><properties><java.version>8</java.version><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!--版本支持到2.7.x--><spring-boot.version>2.0.3.RELEASE</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency></dependencies>
<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>8</source><target>8</target></configuration></plugin></plugins></build></project>
3.源码地址
https://github.com/wangshuai67/i18n-spring-boot-starter/
大家好,我是冰点,今天的原生的SpringBoot国际化配置组件支持本地配置和数据库配置 内容分享就到这儿,写的有点粗糙。如果你有疑问或见解可以在评论区留言。
相关文章:
SpringBoot国际化配置组件支持本地配置和数据库配置
文章目录 0. 前言i18n-spring-boot-starter1. 使用方式0.引入依赖1.配置项2.初始化国际化配置表3.如何使用 2. 核心源码实现一个拦截器I18nInterceptorI18nMessageResource 加载国际化配置 3.源码地址 0. 前言 写个了原生的SpringBoot国际化配置组件支持本地配置和数据库配置 背…...
Shell编程之sort
sort 命令将文件的每一行作为比较对象,通过将不同行进行相互比较,从而得到最终结果。从首字符开始,依次按ASCII码值进行比较,最后将结果按升序输出。 基本语法 sort (选项)(参数) 常用选项 常用选项 -n根据字符串的数字比较-r…...
windows docker 容器启动报错:Ports are not available
docker 启动容器报错: (HTTP code 500) server error - Ports are not available: listen tcp 0.0.0.0:6379: bind: An attempt was made to access a socket in a way forbidden by its access permissions. 问题排查 检查端口是否被其它程序占用:nets…...
300. 最长递增子序列
题目描述 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 示…...
DNS(域名解析系统)
含义 当我们在上网要访问莫个服务器的时候,就需要知道服务器的IP地址,但IP地址是一串数字,虽然这串数字用点分十进制已经清晰不少了,但还是不利于人们记忆和传播,于是人们使用单词来代替IP地址(例如baidu&a…...
解决jsp/html界面跳转servlet出现404错误的方法
解决jsp/html界面跳转servlet出现404错误的方法 最近在学习黑马项目过程中遇到的问题 问题一: 检查页面的跳转路径和名称拼写是否正确 问题二: tomcat发布项目时所使用的路径名称与项目不同 在idea右上角点击如图圈住的按钮 在deployment中更改出现…...
catface,使用Interface定义Controller,实现基于Http协议的RPC调用
catface 前言cat-client 模块EnableCatClientCatClientCatMethodCatNoteCatResponesWrapperCatClientConfigurationCatClientProviderCatClientFactoryCatSendInterceptorCatHttpCatPayloadResolverCatObjectResolverCatLoggerProcessorCatResultProcessorCatSendProcessorAbst…...
Linux:LVS (NAT群集搭建)
模拟环境 外网入口服务器 外网 192.168.8.88 内网ip 192.168.254.4 web1 服务器 ip 192.168.254.1 网关: 192.168.254.4 web2 服务器 ip 192.168.254.2 网关: 192.168.254.4 共享存储服务器 ip 192.168.254.3 介绍 访问 外网192.16…...
音乐格式转换mp3怎么转?跟着步骤操作一遍
音乐格式转换mp3怎么转?mp3,一种音频数据压缩格式,由于其极具优势的文件尺寸小和高质量音效,自诞生之日起就占据了主流音乐格式的头把交椅,并且至今仍然受到用户的青睐,稳居音乐领域的霸主地位。在我们繁忙…...
it监控系统可以电脑吗?有什么效果
IT业务监控已经成为公司不可或缺的一部分,以确保业务的正常运行,提高企业的竞争能力。本文将详细介绍IT业务监控的必要性、实施方法以及如何选择合适的监控工具。 IT业务监控的必要性 确保业务稳定运行 IT业务监控可以实时检测公司的工作流程&#x…...
jvs-智能bi(自助式数据分析)9.1更新内容
jvs-智能bi更新功能 1.报表增加权限功能(服务、模板、数据集、数据源可进行后台权限分配) 每个报表可以独立设置权限,通过自定义分配,给不同的人员分配不同的权限。 2.报表新增执行模式 可选择首次报表加载数据为最新数据和历…...
MyBatis-Plus-扩展操作(3)
3.扩展 代码生成 逻辑删除 枚举处理器 json处理器 配置加密 分页插件 3.1 代码生成 https://blog.csdn.net/weixin_41957626/article/details/132651552 下载下面的插件 红色的是刚刚生成的。 我觉得不如官方的那个好用,唯一的好处就是勾选的选项能够看的懂得。…...
react 中 antd 的 样式和 tailwind 样式冲突
问题原因:在使用 tailwindcss 时,会导入大量的 tailwindcss 默认属性,而默认样式中 button, [typebutton] 包含了 background-color: transparent; 从而导致 antd Button 按钮背景色变成透明。解决办法:禁止 tailwindcss 的默认属…...
获取该虚拟机的所有权失败,主机上的某个应用程序正在使用该虚拟机
点击“openstack-controller”虚机 打开出现如下错误,点击“获取所有权” 点击“取消” 这时候不要删除虚拟机,这种错误一般是由于虚拟机没有正常关闭引起的。 找到openstack-controller的虚拟磁盘文件及配置文件存放的位置,删除openstack-…...
2024届校招-Java开发笔试题-S4卷
有三种题型:单项选择题(10道)、不定项选择题(10道)、编程题(3道) 下面是一些回忆的题目: 1.哪种设计模式将对象的创建与使用分离,通过工厂类创建对象 答:工…...
数据分析面试题(2023.09.08)
数据分析流程 总体分为四层:需求层、数据层、分析层和结论层 一、统计学问题 1、贝叶斯公式复述并解释应用场景 公式:P(A|B) P(B|A)*P(A) / P(B)应用场景:如搜索query纠错,设A为正确的词,B为输入的词,那…...
jenkins 报错fatal:could not read Username for ‘XXX‘:No such device or address
#原因:机器做迁移,或者断电,遇到突发情况 #解决: 一.排查HOME和USER环境变量 可以在项目执行shell脚本的时候echo $HOME和USER 也可以在构建记录位置点击compare environment 对比两次构建的环境变量 二.查看指定节点的git凭证 查…...
LRU算法之我见
文章目录 一、LRU算法是什么?二、使用原理三、代码实现总结 一、LRU算法是什么? LRU算法又称最近最少使用算法,它是是大部分操作系统为最大化页面命中率而广泛采用的一种页面置换算法。是一种缓存淘汰策略,根据使用频率来淘汰无用…...
【第20例】华为 IPD 体系 | IPD 的底层思考逻辑(限制版)
目录 简介 更新情况 IPD体系 CSDN学院 专栏目录 作者简介 简介 最近随着华为 Mate 60 系列的爆火发布。 这家差不多沉寂了 4 年的企业再次映入大众的眼帘。 其实,华为手机业务发展的元年最早可以追溯...
spaCy库的实体链接踩坑,以及spaCy-entity-linker的knowledge_base下载问题
问题1. spacy Can’t find factory for ‘entityLinker’ 1)问题 写了一个实体链接类,代码如下: nlp spacy.load("en_core_web_md")class entieyLink:def __init__(self, doc, nlp):self.nlp nlpself.doc self.nlp(doc)# Che…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
