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…...
【数据结构】红黑树的插入与验证
文章目录 一、基本概念1.时代背景2. 基本概念3.基本性质 二、实现原理1. 插入1.1变色1.2旋转变色①左旋②右旋③右左双旋④左右双旋 2.验证 源码总结 一、基本概念 1.时代背景 1972年鲁道夫拜尔(Rudolf Bayer)发明了一种数据结构,这是一种特殊的B树4阶情况。这些树…...
Pycharm----将Anaconda建立的环境导入
首先打开项目设置,点击添加 随后点击现有环境,点击三个。。。号进行添加 最后找到你Anaconda安装文件夹,envs找到你建立的环境名称,找到python.exe将它导入即可让现在的python环境为你建立的环境,同时还需要更改终端方…...
数字花园的指南针:微信小程序排名的提升之道
微信小程序,是一片数字花园,其中各种各样的小程序竞相绽放,散发出各自独特的芬芳。在这个花园中,排名优化就像是精心照料花朵的园丁,让我们一同走进这个数字花园,探寻如何提升微信小程序的排名优化…...
LRU与LFU的c++实现
LRU 是时间维度上最少使用 维持一个链表,最近使用的放在表头 淘汰表尾 LFU 是实际使用频率的最少使用 每一个对应的频率维持一个链表, 淘汰最低频率的最后一个 1. LRU LRU(Least Recently Used,最近最少使用)是一种常…...
什么是Docker和Docker-Compose?
Docker的构成 Docker仓库:https://hub.docker.com Docker自身组件 Docker Client:Docker的客户端 Docker Server:Docker daemon的主要组成部分,接受用户通过Docker Client发出的请求,并按照相应的路由规则实现路由分发…...
三.listview或tableviw显示
一.使用qt creator 转变类型 变形为listview或tableviw 二.导出ui文件为py文件 # from123.py 为导出 py文件 form.ui 为 qt creator创造的 ui 文件 pyuic5 -o x:\xxx\from123.py form.uifrom123.py listview # -*- coding: utf-8 -*-# Form implementation generated fro…...
【算法】一文带你从浅至深入门dp动态规划
文章目录 一、前言二、动态规划理论基础1、基本概念2、动态规划五部曲【✔】3、出错了如何排查? 三、实战演练🗡0x00 斐波那契数0x01 第N个泰波那契数0x02 爬楼梯0x03 三步问题0x04 使用最小花费爬楼梯⭐解法一解法二 0x05 解码方法* 四、总结与提炼 一、…...
超简单免费转换ape到flac
1. 安装最新版的ffmpeg 2. 安装cywin环境 3. 设置path到ffmpeg export PATH$PATH:"PATH/TO/FFMPEG/BIN" 4.到ape所在的目录,执行以下命令 find . -iname "*.ape" | while read line; do fb${line::-4}; fn"$fb.flac";echo ffm…...
JavaScript混淆加密
什么是JS混淆加密? JavaScript混淆加密是一种通过对源代码进行变换,使其变得难以理解和分析的技术。它的目标是增加攻击者破解代码的难度,同时保持代码的功能不受影响。混淆加密的目的是使代码难以逆向工程,从而防止攻击者窃取知…...
Java8特性-Lambda表达式
📕概述 在Java 8中引入了Lambda表达式作为一项重要的语言特性,可以堪称是一种语法糖。Lambda表达式使得以函数式编程的方式解决问题变得更加简洁和便捷。 Lambda表达式的语法如下: (parameters) -> expression (参数) -> {代码}其中&…...
设计师网站推荐/我要推广网
使用环境(蓝色粗体字为特别注意内容) 1、软件环境:Keil MDK 5.15 2、硬件环境:STM32F103C8T6最小系统 在项目中打算用基本定时器6作为延时定时器,编译发现报错:main.c(77): error: #20: identifier "RCC_APB1ENR_TIM6EN&qu…...
江苏专业做网站的公司/临沂百度联系方式
1.前言 最近无意中看到了阮一峰大佬的关于数字签名的一个翻译(数字签名是什么?,该篇文章的精髓在于评论内容),觉得对数字签名突然没有那么迷茫了,所以决定做一个学习笔记,以便以后回来查阅。 …...
网站建设活动策划/seo 培训教程
休假了,有时间测试下12C的新功能了,今天测试下分页查询。在12C之前,要想分页查询,我们通常会用下面的SQL。SQL> select *2 from (select a.*, rownum rn3 from (select id,4 username5 from top_test) a6 where rn < 10)7 w…...
备份wordpress配置/佛山企业用seo策略
程序1: void myMalloc(char *s) //我想在函数中分配内存,再返回 { s(char *) malloc(100); } void main() { char *pNULL; myMalloc(p); //这里的p实际还是NULL,p的值没有改变,为什么? if(p) free(p); } 程序2: void myMalloc(cha…...
天津市建设 中标公示网站/seo费用
1.输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 输入描述: 输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。 思路:这个题麻烦在&…...
佛山网站设计怎么做/短信营销
拒绝重复劳动.保存如下代码为codeGen.py,放到模块统计目录下 import sysname sys.argv[1] verbose sys.argv[2]def underscore(str):return "".join(map(lambda x: "_" x if x.isupper() else x, str))[1:].lower()model ""&quo…...