一、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 读取配置文件-CSDN博客
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等注解来限制属性值的有效性。
三、springboot-kafka
参考地址:spring boot 集成kafka生产者消费者_springboot集成kafka生产者工具类-CSDN博客
四、springboot 多数据源
参考地址:SpringBoot整合mysql、postgres、sqlserver实现多数据源配置案例_spring boot 项目集成mysql和sqlserver-CSDN博客
五、接口参数校验
@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;
}
}