springboot组件

一、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;
 
}
  1. @ConfigurationProperties注意事项:
  2. 确保添加了@EnableConfigurationProperties注解:为了使@ConfigurationProperties生效,需要在主配置类上添加@EnableConfigurationProperties(value=xxxxProperties.class)注解,开启@ConfigurationProperties注解自动装配功能。
  3. 配置文件中的属性名与类字段名的映射规则:默认情况下,@ConfigurationProperties会将配置文件中的属性名与类字段名进行映射。例如,配置文件中的属性student.name会自动映射到类字段name上。如果配置文件中的属性名与类字段名不一致,可以使用@Value注解或通过setter方法来指定映射关系。
  4. 类必须是Spring管理的Bean:被@ConfigurationProperties注解标记的类必须是由Spring容器管理的Bean,因此需要确保该类被@Component或其他相关注解标记,以便Spring能够扫描并创建该类的实例。
  5. 支持类型转换:@ConfigurationProperties支持自动类型转换,将配置文件中的字符串值转换为目标字段的类型。例如,将字符串转换为整数、布尔值等。如果无法进行类型转换,会抛出异常。
  6. 默认值和可选属性:可以为@ConfigurationProperties注解的字段设置默认值,以防止配置文件中缺少对应的属性。可以使用":“符号指定默认值,例如@Value(”${my.property:default-value}")。另外,可以使用required属性来指定某个属性是否为必需的。
  7. 配置项的验证和校验:可以使用JSR-303/349规范的注解对@ConfigurationProperties注解的字段进行验证和校验。例如,使用@NotBlank、@Min、@Max等注解来限制属性值的有效性。
  8. 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&amp;zeroDateTimeBehavior=convertToNull&amp;useUnicode=true&amp;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&amp;zeroDateTimeBehavior=convertToNull&amp;useUnicode=true&amp;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");
    }

}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值