一、springboot 事件监听
1.1 事件发布与监听
1. 事件定义
public class MyMessageEvent extends ApplicationEvent {
private String type;
private Object mqMessage;
public MyMessageEvent(Object source, String type, Object mqMessage) {
super(source);
this.type = type;
this.mqMessage = mqMessage;
}
public String getType() {
return type;
}
public Object getMqMessage() {
return mqMessage;
}
}
2. 事件发布
@Slf4j
@Component
public class EventAlarmMessageListener extends EvoEventMessageHandler<EvoAlarmMessage> {
@Autowired
private ApplicationEventPublisher publisher;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
protected EvoAlarmMessage parseData(EvoEventMessage messageBody) {
System.out.println(JSON.toJSONString(messageBody));
logger.info("【Alarm message】 {}", JSON.toJSONString(messageBody));
publisher.publishEvent(new MyMessageEvent(this, messageBody.getMethod(), messageBody));
return null;
}
@Override
protected void process(String method, EvoAlarmMessage data) {
}
}
3. 事件监听
@EventListener(condition = "'alarm.msg'.equals(#messageEvent.type)")
public void actTimeAttendanceListener(MyMessageEvent messageEvent) {
EvoEventMessage eventMessage = (EvoEventMessage) messageEvent.getMqMessage();
String str = JSONObject.toJSONString(eventMessage.getInfo());
JSONObject jsonObject = JSONObject.parseObject(str);
AlarmMessage.Info info = jsonObject.toJavaObject(AlarmMessage.Info.class);
}
二、配置文件读取
参考:SpringBoot读取配置文件的6种方式_springboot 读取配置文件-优快云博客
2.1 使用@Value注解读取单个配置
(1)编写application.yml文件配置:
student:
name: 张三
age: 20
(2)使用@Value读取配置:
@SpringBootTest
@Slf4j
public class ValueTest {
@Value("${student.name}")
private String name;
@Value("${student.age}")
private Integer age;
@Test
public void test() {
log.info("@Value 配置获取 name:{},age:{}",name,age);
}
}
@Value注意事项:
@Value 注解只能读取单个配置进行赋值,无法读取整个配置文件批量赋值。当使用@Value注解读取配置时,确保配置在yml中存在,否则启动程序时就会报错。注解中属性名引用方式如下:
@Value("${一级属性名.二级属性名...}")
当使用 @Value 注解引用属性时,可以在属性名称后面使用冒号(: default-value )的形式添加默认值。这样,如果在配置文件中找不到对应的属性,就会使用默认值。如果在配置文件中找到了属性,其值将会覆盖默认值。
@Value 注解只能用于被Spring管理的Bean中使用,,如使用 @Component 、 @Service 、 @Controller 等注解修饰的类,或者使用Java配置编写的 @Configuration 类中。
@Value 注解可以用于字段、构造函数参数、方法参数和方法上。当将它放在方法上时,Spring容器初始化时会调用该方法,并将配置属性的值作为方法的参数传递进去。
//可以使用各种类型的默认值,包括字符串、数字、布尔值等
@Value("${student.name:aopmin}")
private String name;
@Value("${student.age:18}")
private Integer age;
//表示一个空字符串作为默认值
@Value("${student.name:}")
private String name;
Spring支持多种数据类型的属性注入,对于每种类型,都可以设置默认值。以下是一些常见的数据类型及其默认值设置示例:
1. 字符串类型
@Value("${app.name:MyApp}")
private String appName;
如果配置文件中未定义app.name,那么appName将会被赋值为"MyApp"。
2. 整数类型
@Value("${app.port:8080}")
private int port;
如果配置文件中未定义app.port,那么ports将会被赋值为8080。
3. 布尔类型
@Value("${eature.enabled:false}")
private Boolean featureEnabled;
如果配置文件中未定义feature.enabled,那么featureEnabled将会被赋值为false。
4. 浮点类型
@Value("${threshold.value:0.5}")
private Double thresholdValue;
如果配置文件中未定义threshold.value,那么thresholdValue将会被赋值为0.5。
5. 列表类型
对于列表类型的值,你可以使用逗号分隔的形式来定义默认值。
@Value("${app.tags:tag1,tag2,tag3}")
private List<String> appTags;
如果配置文件中未定义appTags,那么servers列表将包含"tag1"、"tag2"、"tag3"。
6. 数组类型
对于列表类型的值,你可以使用逗号分隔的形式来定义默认值。
@Value("${app.tags:tag1,tag2,tag3}")
private String[] appTags;
如果配置文件中未定义appTags,那么servers列表将包含"tag1"、"tag2"、"tag3"。
7. Map类型:
@Value("#{${app.properties:{key1:'value1', key2:'value2'}}}")
private Map<String, String> appProperties;
2.2 使用@ConfigurationProperties注解批量绑定
package cn.hk.pojo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 参数配置类 (需要提供setter方法)
*/
//将这个类与配置文件前缀为student的配置绑定,然后把yml、properties中关于student的配置信息注入到当前类的成员变量中
@Component
@Data
@ConfigurationProperties(prefix = "student")
public class StudentProperties {
private String name;
}
- @ConfigurationProperties注意事项:
- 确保添加了@EnableConfigurationProperties注解:为了使@ConfigurationProperties生效,需要在主配置类上添加@EnableConfigurationProperties(value=xxxxProperties.class)注解,开启@ConfigurationProperties注解自动装配功能。
- 配置文件中的属性名与类字段名的映射规则:默认情况下,@ConfigurationProperties会将配置文件中的属性名与类字段名进行映射。例如,配置文件中的属性student.name会自动映射到类字段name上。如果配置文件中的属性名与类字段名不一致,可以使用@Value注解或通过setter方法来指定映射关系。
- 类必须是Spring管理的Bean:被@ConfigurationProperties注解标记的类必须是由Spring容器管理的Bean,因此需要确保该类被@Component或其他相关注解标记,以便Spring能够扫描并创建该类的实例。
- 支持类型转换:@ConfigurationProperties支持自动类型转换,将配置文件中的字符串值转换为目标字段的类型。例如,将字符串转换为整数、布尔值等。如果无法进行类型转换,会抛出异常。
- 默认值和可选属性:可以为@ConfigurationProperties注解的字段设置默认值,以防止配置文件中缺少对应的属性。可以使用":“符号指定默认值,例如@Value(”${my.property:default-value}")。另外,可以使用required属性来指定某个属性是否为必需的。
- 配置项的验证和校验:可以使用JSR-303/349规范的注解对@ConfigurationProperties注解的字段进行验证和校验。例如,使用@NotBlank、@Min、@Max等注解来限制属性值的有效性。
- 2.2.1
2.2.1 ConfigurationProperties读取成多个实体类
/**
* 上报emap,注册点位图层
*
*/
@Configuration
@ConfigurationProperties(prefix = "emapregisterbeans")
@PropertySource("classpath:emap/evo_emap.properties")
@Data
public class EmapConfiguration {
private EmapRegisterBean iot;
private EmapRegisterBean alarmIn;
private EmapRegisterBean alarmOut;
private EmapRegisterBean gps;
}
@Data
public class EmapRegisterBean {
private String layerCode;
private String layerName;
private String layerIcon;
private String searchType;
private String iconUrls;
private Integer mapType;
private String componentName;
private Integer useCluster;
private String subSystem;
private String layerClass;
}
evo_emap.properties
emapRegisterBeans.encoder.componentName=commonDevice
emapRegisterBeans.encoder.iconUrls={"offline": "/emap/static/img/markericon/video-marker-offline.png","online": "/emap/static/img/markericon/video-marker.png","warning": "/emap/static/img/markericon/video-marker-warning.png","selected": "/emap/static/img/markericon/video-marker-s.png","layerIconSmall": "/emap/static/img/markericon/video-icon-s.png"}
emapRegisterBeans.encoder.layerCode=videoLayer
emapRegisterBeans.encoder.layerIcon=/emap/static/img/markericon/video-icon.png
emapRegisterBeans.encoder.layerName=emap.config.video
emapRegisterBeans.encoder.mapType=2
emapRegisterBeans.encoder.searchType={"type": "001;00_1,00_3,00_5,00_7,00_21,00_35,00_36,00_45;1"}
emapRegisterBeans.encoder.subSystem=admin
emapRegisterBeans.encoder.useCluster=1
emapRegisterBeans.alarmIn.componentName=commonDevice
emapRegisterBeans.alarmIn.iconUrls={"offline": "/emap/static/img/markericon/alarm-in-marker-offline.png","online": "/emap/static/img/markericon/alarm-in-marker.png","warning": "/emap/static/img/markericon/alarm-in-marker-warning.png","selected": "/emap/static/img/markericon/alarm-in-marker-s.png","layerIconSmall": "/emap/static/img/markericon/alarm-in-icon-s.png"}
emapRegisterBeans.alarmIn.layerCode=alarmInLayer
emapRegisterBeans.alarmIn.layerIcon=/emap/static/img/markericon/alarm-in-icon.png
emapRegisterBeans.alarmIn.layerName=emap.config.alarminput
emapRegisterBeans.alarmIn.mapType=2
emapRegisterBeans.alarmIn.searchType={"type": "001;00;3"}
emapRegisterBeans.alarmIn.subSystem=admin
emapRegisterBeans.alarmIn.useCluster=1
三、springboot-kafka
参考地址:spring boot 集成kafka生产者消费者_springboot集成kafka生产者工具类-优快云博客
四、springboot 多数据源
参考地址:SpringBoot整合mysql、postgres、sqlserver实现多数据源配置案例_spring boot 项目集成mysql和sqlserver-优快云博客
4.1Mybatis插件使用
4.1.1 pom引入驱动以及配置文件配置数据源
#1-1pom
<dependency>
<groupId>com.oracle.ojdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>19.3.0.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
# 1-2 配置文件
# mysql
config.service.db.ip=10.81.119.225
config.service.db.port=3306
config.service.db.account.name=mysql
config.service.db.account.pwd=P7UuFj35MPFF.zR
config.service.db.database=dev_channel
config.service.db.check-live=true
config.service.dboracle.ip=10.2.0.20
config.service.dboracle.port=1521
config.service.dboracle.account.name=syt_xgxt_zj
config.service.dboracle.account.pwd=Syt2025_ytzyxy
config.service.dboracle.database=ORCLCDB
config.service.dboracle.check-live=true
# datasource master
spring.datasource.dynamic.primary=master
spring.datasource.dynamic.datasource.master.url=jdbc:mariadb://${config.service.db.ip}:${config.service.db.port}/${config.service.db.database}?socketTimeout=1200000&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
spring.datasource.dynamic.datasource.master.username=${config.service.db.account.name}
spring.datasource.dynamic.datasource.master.password=${config.service.db.account.pwd}
spring.datasource.dynamic.datasource.master.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.dynamic.datasource.master.druid.initial-size=5
spring.datasource.dynamic.datasource.master.druid.max-active=50
spring.datasource.dynamic.datasource.master.druid.min-idle=5
spring.datasource.dynamic.datasource.master.druid.max-wait=60000
spring.datasource.dynamic.datasource.master.druid.min-evictable-idle-time-millis=300000
spring.datasource.dynamic.datasource.master.druid.max-evictable-idle-time-millis=300000
spring.datasource.dynamic.datasource.master.druid.time-between-eviction-runs-millis=60000
spring.datasource.dynamic.datasource.master.druid.validation-query=SELECT 1
spring.datasource.dynamic.datasource.master.druid.validation-query-timeout=-1
spring.datasource.dynamic.datasource.master.druid.test-on-borrow=false
spring.datasource.dynamic.datasource.master.druid.test-on-return=false
spring.datasource.dynamic.datasource.master.druid.test-while-idle=true
spring.datasource.dynamic.datasource.master.druid.pool-prepared-statements=true
spring.datasource.dynamic.datasource.master.druid.max-pool-prepared-statement-per-connectionSize=20
spring.datasource.dynamic.datasource.master.druid.filters=stat,log4j
spring.datasource.dynamic.datasource.master.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=3000
spring.datasource.dynamic.datasource.master.druid.use-global-data-source-stat=true
spring.datasource.dynamic.datasource.master.druid.share-prepared-statements=true
spring.datasource.dynamic.datasource.master.druid.initialize=false
# datasource oracle
spring.datasource.dynamic.datasource.data-source1.url=jdbc:oracle:thin:@${config.service.dboracle.ip}:${config.service.dboracle.port}/${config.service.dboracle.database}?socketTimeout=1200000&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
spring.datasource.dynamic.datasource.data-source1.username=${config.service.dboracle.account.name}
spring.datasource.dynamic.datasource.data-source1.password=${config.service.dboracle.account.pwd}
spring.datasource.dynamic.datasource.data-source1.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.dynamic.datasource.data-source1.druid.initial-size=5
spring.datasource.dynamic.datasource.data-source1.druid.max-active=50
spring.datasource.dynamic.datasource.data-source1.druid.min-idle=5
spring.datasource.dynamic.datasource.data-source1.druid.max-wait=60000
spring.datasource.dynamic.datasource.data-source1.druid.min-evictable-idle-time-millis=300000
spring.datasource.dynamic.datasource.data-source1.druid.max-evictable-idle-time-millis=300000
spring.datasource.dynamic.datasource.data-source1.druid.time-between-eviction-runs-millis=60000
spring.datasource.dynamic.datasource.data-source1.druid.validation-query=SELECT 1
spring.datasource.dynamic.datasource.data-source1.druid.validation-query-timeout=-1
spring.datasource.dynamic.datasource.data-source1.druid.test-on-borrow=false
spring.datasource.dynamic.datasource.data-source1.druid.test-on-return=false
spring.datasource.dynamic.datasource.data-source1.druid.test-while-idle=true
spring.datasource.dynamic.datasource.data-source1.druid.pool-prepared-statements=true
spring.datasource.dynamic.datasource.data-source1.druid.max-pool-prepared-statement-per-connectionSize=20
spring.datasource.dynamic.datasource.data-source1.druid.filters=stat,log4j
spring.datasource.dynamic.datasource.data-source1.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=3000
spring.datasource.dynamic.datasource.data-source1.druid.use-global-data-source-stat=true
spring.datasource.dynamic.datasource.data-source1.druid.share-prepared-statements=true
spring.datasource.dynamic.datasource.data-source1.druid.initialize=false
4.1.2 数据源 动态切换
# 1-1 数据源类型
/**
* @describe:
* @date 2025/10/13
*/
public class DataSourceType {
public static final String DATA_SOURCE1 = "data-source1";
public static final String DATA_SOURCE2 = "data-source2";
public static final String MASTER_DATA_SOURCE = "master";
public static final String SHARDING_DATASOURCE = "sharding";
}
# 1-2 注解动态切换
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
/**
* @describe:
* @date 2025/10/20
*/
@Repository
@DS(DataSourceType.MASTER_DATA_SOURCE)
public interface LeaveAccessControlChnMapper extends BaseMapper<LeaveAccessControlChn> {
}
4.2 自定义多数据源切换
4.2.1 pom引入和多数据源配置
# 1-1 数据源
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
<!--mysql数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<dependency>
<groupId>oracle.jdbc</groupId>
<artifactId>ojdbc</artifactId>
<version>11.2.0.3</version>
</dependency>
# 1-2 BaseConfig
@Component
@PropertySource(
value = {"classpath:application.properties", "classpath:config/application.properties", "classpath:component.properties", "classpath:config/component.properties"},
ignoreResourceNotFound = true
)
public class BaseConfig implements EnvironmentAware {
private static final Logger log = LoggerFactory.getLogger(BaseConfig.class);
@Value("${evo.dahua.log.print.length:1000}")
private String logMaxLen;
private Binder binder;
private Environment environment;
public BaseConfig() {
}
public void setEnvironment(Environment env) {
Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(env);
this.binder = new Binder(sources);
this.environment = env;
String loggingConfig = this.getPropertyResolver().getProperty("logging.config");
if (loggingConfig != null) {
String filePath = null;
try {
filePath = ResourceUtils.getURL("classpath:").getPath();
filePath = filePath + loggingConfig.replace("classpath:", "");
} catch (Exception e) {
log.info("can not find logging.config file {}", loggingConfig, e);
}
if (filePath != null) {
this.startModify(filePath, "%m%", "%maxLen{%m}{" + this.logMaxLen + "}%");
}
}
}
private void startModify(String path, String target, String replacement) {
File file = new File(path);
if (file.exists()) {
this.modifyFileContent(file, target, replacement);
} else {
log.info("file not exist {}", path);
}
}
private void modifyFileContent(File file, String target, String replacement) {
log.info("modify file:{}, replace {} to {}", new Object[]{file.getName(), target, replacement});
StringBuilder sb = new StringBuilder();
int cnt = 0;
int rowLine = 0;
String enter = System.getProperty("line.separator");
PrintWriter pw = null;
try {
BufferedReader br = new BufferedReader(new FileReader(file));
Throwable var10 = null;
try {
for(String line = br.readLine(); line != null; line = br.readLine()) {
++rowLine;
if (line.contains(target)) {
line = line.replace(target, replacement);
log.info("replace line:{}", rowLine);
++cnt;
}
if (rowLine == 1) {
sb.append(line);
} else {
sb.append(enter).append(line);
}
}
pw = new PrintWriter(new FileWriter(file));
pw.print(sb);
} catch (Throwable var33) {
var10 = var33;
throw var33;
} finally {
if (br != null) {
if (var10 != null) {
try {
br.close();
} catch (Throwable var32) {
var10.addSuppressed(var32);
}
} else {
br.close();
}
}
}
} catch (FileNotFoundException e) {
log.info("file not exist! ignore it", e);
} catch (IOException ex) {
log.info("file io exception!ignore it", ex);
} finally {
if (pw != null) {
pw.close();
}
}
try {
log.info("modify success:{} ,total line {}", file.getCanonicalFile(), cnt);
} catch (IOException var31) {
log.info("modify success:{} ,total line {}", file.getAbsoluteFile(), cnt);
}
}
public String getConfigPrefix() {
return "";
}
public Properties getPropertyResolver() {
String prefix = this.getConfigPrefix();
if (prefix == null) {
return new Properties();
} else {
prefix = StringUtils.strip(prefix, ".");
BindResult<Properties> bindResult = this.binder.bind(prefix, Properties.class);
Properties properties = (Properties)bindResult.get();
properties.forEach((key, value) -> {
if (value instanceof String) {
properties.put(key, this.resolvePlaceholders((String)value));
}
});
return properties;
}
}
private String resolvePlaceholders(String text) {
return this.environment.resolvePlaceholders(text);
}
}
# 1-3 BaseConfig
/**
* 数据源类型
*
* @author 232422
* @date 2023年3月9日19:09:17
*/
public enum EduDataSourceTypeEnum {
/**
* 主数据源
*/
MASTER,
BUSINESSDB,
THIRDDB,
BUGUDB
}
@Slf4j
@Configuration
public class DataSourceConfig extends BaseConfig {
@Value("${spring.datasource.default.url}")
private String masterUrl;
@Value("${spring.datasource.default.username}")
private String masterUserName;
@Value("${spring.datasource.default.password}")
private String masterPassword;
@Value("${spring.datasource.default.driverClassName}")
private String masterDriverClassName;
@Value("${spring.datasource.third.url}")
private String thirdUrl;
@Value("${spring.datasource.third.username}")
private String thirdUserName;
@Value("${spring.datasource.third.password}")
private String thirdPassword;
@Value("${spring.datasource.third.driverClassName}")
private String thirdDriverClassName;
@Value("${config.third.service.db.open:false}")
private String openThirdDbBean;
@Value("${spring.datasource.bugu.url}")
private String db1Url;
@Value("${spring.datasource.bugu.username}")
private String db1UserName;
@Value("${spring.datasource.bugu.password}")
private String db1Password;
@Value("${spring.datasource.bugu.driver-class-name}")
private String db1DriverClassName;
@Value("${config.bugu.service.db.open:false}")
private String openBuguDbBean;
@Override
public String getConfigPrefix() {
return "spring.datasource.";
}
@Bean(name = "masterDb")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource masterDataSource() {
String password = this.getMasterPassword();
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setUrl(masterUrl);
dataSource.setUsername(masterUserName);
dataSource.setPassword(password);
dataSource.setDriverClassName(masterDriverClassName);
return dataSource;
}
@Bean(name = "thirdDb")
@ConfigurationProperties(prefix = "spring.datasource.third")
@ConditionalOnExpression("'${config.third.service.db.open:false}'=='true'")
public DataSource thirdDataSource() {
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setUrl(thirdUrl);
dataSource.setUsername(thirdUserName);
dataSource.setPassword(thirdPassword);
dataSource.setDriverClassName(thirdDriverClassName);
return dataSource;
}
@Bean(name = "buguDb")
@ConfigurationProperties(prefix = "spring.datasource.bugu")
@ConditionalOnExpression("'${config.bugu.service.db.open:false}'=='true'")
public DataSource dataSource1() {
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setUrl(db1Url);
dataSource.setUsername(db1UserName);
dataSource.setPassword(db1Password);
dataSource.setDriverClassName(db1DriverClassName);
return dataSource;
}
@Primary
@Bean(name = "dataSource")
public DataSource dynamicDataSource(@Qualifier("masterDb") DataSource masterDataSource) {
// 配置多数据源
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put(EduDataSourceTypeEnum.MASTER.name(), masterDataSource);
if (openThirdDbBean.equals("true")) {
DataSource thirdDataSource = AppContextHelper.getBean("thirdDb");
dataSourceMap.put(EduDataSourceTypeEnum.THIRDDB.name(), thirdDataSource);
}
if (openBuguDbBean.equals("true")) {
DataSource DataSource1 = AppContextHelper.getBean("buguDb");
dataSourceMap.put(EduDataSourceTypeEnum.BUGUDB.name(), DataSource1);
}
return new DynamicDataSource(masterDataSource, dataSourceMap);
}
/**
* 获取中心数据库密码
*
* @return
*/
public String getMasterPassword() {
return DynamicPropertiesAESUtils.aesDecrypt(masterPassword);
}
/**
* 配置@Transactional注解事务
*/
@Bean
@Qualifier("dataSource")
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
#1-4 DynamicDataSource
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
public DynamicDataSource(DataSource dataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(dataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
String dataSource = DynamicDataSourceContextHolder.getDataSource();
log.info("==========当前正在使用数据源:{}=========", dataSource);
return dataSource;
}
}
#1-5 DynamicDataSource
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Aspect
@Slf4j
public class DynamicDataSourceAspect {
@Pointcut("execution(* com.dahua.evo.edu.tpcs.dal.mapper..*.*(..))")
public void dataSourcePoint() {
}
@Before("dataSourcePoint()")
public void before(JoinPoint point) {
try {
String methodPackageName = Arrays.stream(point.getTarget().getClass().getAnnotatedInterfaces()).findFirst().get().getType().getTypeName();
// String methodPackageName = point.getSignature().getDeclaringType().getName();
log.info("=========dal method:{}", methodPackageName);
if (methodPackageName.contains(EduDataSourceTypeEnum.MASTER.name().toLowerCase()) || methodPackageName.contains(EduDataSourceTypeEnum.BUSINESSDB.name().toLowerCase())) {
DynamicDataSourceContextHolder.setDataSource(EduDataSourceTypeEnum.MASTER.name());
} else if (methodPackageName.contains(EduDataSourceTypeEnum.BUGUDB.name().toLowerCase())) {
DynamicDataSourceContextHolder.setDataSource(EduDataSourceTypeEnum.BUGUDB.name());
}else {
DynamicDataSourceContextHolder.setDataSource(EduDataSourceTypeEnum.THIRDDB.name());
}
@After("dataSourcePoint()")
public void after(JoinPoint point) {
DynamicDataSourceContextHolder.clearDataSource();
}
}
@Slf4j
public class DynamicDataSourceContextHolder {
/**
* 数据源标识,保存在线程变量中,避免多线程操作数据源时互相干扰
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源
*
* @param dataSource
*/
public static void setDataSource(String dataSource) {
dataSource = StringUtils.isEmpty(dataSource) ? EduDataSourceTypeEnum.MASTER.name() : dataSource;
log.info("==========切换数据源:{}=========", dataSource);
CONTEXT_HOLDER.set(dataSource);
}
/**
* 获取数据源
*
* @return
*/
public static String getDataSource() {
return CONTEXT_HOLDER.get() == null ? EduDataSourceTypeEnum.MASTER.name() : CONTEXT_HOLDER.get();
}
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
4.2.2 包名区分多数据源与多数据源切换逻辑处理
五、接口参数校验
@PostMapping("/orgMove")
public RestResult orgMove(@Validated(AddGroup.class) @RequestBody OrgMoveParam param, HttpServletRequest request) {
@Data
@JsonIgnoreProperties(ignoreUnknown=true)
public class OrgMoveParam {
/**
* 旧组织编码
*/
@NotEmpty(message = "旧组织编码为空",groups = {AddGroup.class})
private String oldOrgCode;
public interface AddGroup {
}
六、springboot 多线程
第一钟
import com.dahua.evo.event.common.constant.BusinessConstant;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.*;
/**
* The type Spring executor.
*
* @author 234271
* @version 1.0 全局线程池配置类
* @since 2023 /2/6 16:46
*/
@Configuration
public class SpringExecutor extends BaseConfig{
@Override
public String getConfigPrefix(){
return "spring.execute.";
}
/**
* Alarm msg executor executor.
*
* @return the executor
*/
@Bean(name = "alarmMsgExecutor")
public Executor alarmMsgExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Integer.parseInt(getPropertyResolver().getProperty("alarm.msg.corePoolSize")));
executor.setMaxPoolSize(Integer.parseInt(getPropertyResolver().getProperty("alarm.msg.maxPoolSize")));
executor.setQueueCapacity(Integer.parseInt(getPropertyResolver().getProperty("alarm.msg.queueCapacity")));
executor.setKeepAliveSeconds(Integer.parseInt(getPropertyResolver().getProperty("alarm.msg.keepAliveSeconds")));
executor.setThreadNamePrefix("alarmMsgExecutor-");
executor.setRejectedExecutionHandler(new AlarmPolicy());
executor.initialize();
return executor;
}
/**
* Alarm log executor executor.
*
* @return the executor
*/
@Bean(name = "alarmDbExecutor")
public Executor alarmLogExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Integer.parseInt(getPropertyResolver().getProperty("alarm.db.corePoolSize")));
executor.setMaxPoolSize(Integer.parseInt(getPropertyResolver().getProperty("alarm.db.maxPoolSize")));
executor.setQueueCapacity(Integer.parseInt(getPropertyResolver().getProperty("alarm.db.queueCapacity")));
executor.setKeepAliveSeconds(Integer.parseInt(getPropertyResolver().getProperty("alarm.db.keepAliveSeconds")));
executor.setThreadNamePrefix("alarmDbExecutor-");
executor.setRejectedExecutionHandler(new AlarmPolicy());
executor.initialize();
return executor;
}
/**
* 针对事件总线 rrb 的 Executor
*
* @return java.util.concurrent.Executor 返回对象
* @author 234271
* @since V5.0.6 2023/7/5 15:14
*/
@Bean(name = "rrbExecutor")
public Executor rrbExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Integer.parseInt(getPropertyResolver().getProperty("rrb.corePoolSize")));
executor.setMaxPoolSize(Integer.parseInt(getPropertyResolver().getProperty("rrb.maxPoolSize")));
executor.setQueueCapacity(Integer.parseInt(getPropertyResolver().getProperty("rrb.queueCapacity")));
executor.setKeepAliveSeconds(Integer.parseInt(getPropertyResolver().getProperty("rrb.keepAliveSeconds")));
executor.setThreadNamePrefix("EventExecutor-RRB-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
/**
* 针对事件推送总线 rrb 的 Executor
*
* @return java.util.concurrent.Executor 返回对象
* @author 234271
* @since V5.0.6 2023/7/5 15:14
*/
@Bean(name = "rrbSendExecutor")
public Executor rrbSendExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Integer.parseInt(getPropertyResolver().getProperty("send.rrb.corePoolSize")));
executor.setMaxPoolSize(Integer.parseInt(getPropertyResolver().getProperty("send.rrb.maxPoolSize")));
executor.setQueueCapacity(Integer.parseInt(getPropertyResolver().getProperty("send.rrb.queueCapacity")));
executor.setKeepAliveSeconds(Integer.parseInt(getPropertyResolver().getProperty("send.rrb.keepAliveSeconds")));
executor.setThreadNamePrefix("EventExecutor-RRB-Send-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
/**
* Container redis message listener container.
*
* @param connectionFactory the connection factory
* @param redisSubscribeListener the redis subscribe listener
* @return the redis message listener container
*/
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
RedisSubscribeListener redisSubscribeListener){
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setSubscriptionExecutor(Executors.newSingleThreadExecutor());
container.setTaskExecutor(Executors.newSingleThreadExecutor());
container.addMessageListener(redisSubscribeListener, new PatternTopic(BusinessConstant.EVENT_REDIS_SUBSCRIBE));
return container;
}
/**
* Get application event multicaster application event multicaster.
*
* @return the application event multicaster
*/
@Bean("applicationEventMulticaster")
public ApplicationEventMulticaster getApplicationEventMulticaster(){
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("applicationEventMulticaster-pool-%d").build();
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, corePoolSize, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000),namedThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
simpleApplicationEventMulticaster.setTaskExecutor(executor);
return simpleApplicationEventMulticaster;
}
/**
*
* 增加公共线程池配置
* @since V5.0.14 2023/8/8 17:10
* @return java.util.concurrent.Executor 返回对象
* @author 234271
*/
@Bean(name = "eventCommonExecutor")
public Executor eventCommonExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Integer.parseInt(getPropertyResolver().getProperty("common.corePoolSize")));
executor.setMaxPoolSize(Integer.parseInt(getPropertyResolver().getProperty("common.maxPoolSize")));
executor.setQueueCapacity(Integer.parseInt(getPropertyResolver().getProperty("common.queueCapacity")));
executor.setKeepAliveSeconds(Integer.parseInt(getPropertyResolver().getProperty("common.keepAliveSeconds")));
executor.setThreadNamePrefix("AlarmExecutor-eventCommonExecutor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
/**
*
* 增加公共线程池配置
* @since V5.0.14 2023/8/8 17:10
* @return java.util.concurrent.Executor 返回对象
* @author 234271
*/
@Bean(name = "batchInsertThirdAlarmExecutor")
public Executor batchInsertThirdAlarmExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Integer.parseInt(getPropertyResolver().getProperty("common.corePoolSize")));
executor.setMaxPoolSize(Integer.parseInt(getPropertyResolver().getProperty("common.maxPoolSize")));
executor.setQueueCapacity(Integer.parseInt(getPropertyResolver().getProperty("common.queueCapacity")));
executor.setKeepAliveSeconds(Integer.parseInt(getPropertyResolver().getProperty("common.keepAliveSeconds")));
executor.setThreadNamePrefix("BatchInsertThirdAlarmExecutor-eventCommonExecutor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
@Resource(name = "batchInsertThirdAlarmExecutor")
private Executor executor;
if (CollUtil.isNotEmpty(insertList)) {
log.info("thirdAlarmInfoServiceImpl.batchAdd.insertList:{}", JSONObject.toJSONString(insertList));
executor.execute(()->{
thirdAlarmInfoMapper.insertBatch(insertList);
});
@Configuration
public class ThreadPoolsConfiguration {
//线程池的核心线程数量
private static final int HANDLER_CORE_POOL_SIZE = 2;
//线程池的最大线程数
private static final int HANDLER_MAX_POOL_SIZE = 10;
//阻塞队列的容量
private static final int HANDLER_QUEUE_CAPACITY = 5000;
//当线程数大于核心线程数时,多余的空闲线程存活的最长时间()
private static final int HANDLER_KEEP_ALIVE_TIME = 1;
@Bean(name = {"cardRecordSyncExecutor"})
public Executor cardRecordSyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(HANDLER_CORE_POOL_SIZE);
executor.setMaxPoolSize(HANDLER_MAX_POOL_SIZE);
executor.setQueueCapacity(HANDLER_QUEUE_CAPACITY);
executor.setKeepAliveSeconds(HANDLER_KEEP_ALIVE_TIME);
executor.setThreadNamePrefix("cardRecordSyncExecutor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
executor.initialize();
return executor;
}
}
七、springboot 定时任务
@EnableScheduling
import org.springframework.scheduling.annotation.Scheduled;
@RestController
@Slf4j
@RequestMapping("/platformEquipmentJob")
public class PlatformEquipmentJob {
@Resource
PlatformEquipmentService platformEquipmentService;
@GetMapping(value = "/equipmentResources")
public RestResult equipmentResources() {
log.info("platformEquipmentJob.equipmentResources.start");
platformEquipmentService.upgradeSyncPlatformEquipmentResources();
log.info("platformEquipmentJob.equipmentResources.end");
return RestResult.createSuccessfulResult(0);
}
/**
* 升级识别杆资源
*/
@Async
@Scheduled(cron = "${corn.cameraInfoCache}")// 每分钟执行一次
public void upgradeEquipmentResources(){
log.info("platformEquipmentJob.upgradeEquipmentResources.start");
platformEquipmentService.upgradeSyncPlatformEquipmentResources();
log.info("platformEquipmentJob.upgradeEquipmentResources.end");
}
}
459

被折叠的 条评论
为什么被折叠?



