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…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
