首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,610 阅读
2
Mac打印机设置黑白打印
4,952 阅读
3
修改elementUI中el-table树形结构图标
4,896 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,675 阅读
5
intelliJ Idea 2022.2.X破解
4,358 阅读
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Vue
Spring Cloud
Mac
MyBatis
WordPress
MacOS
asp.net
Element UI
Nacos
.Net
Spring Cloud Alibaba
MySQL
Mybatis-Plus
Typecho
jQuery
Java Script
IntelliJ IDEA
微信小程序
Laughing
累计撰写
627
篇文章
累计收到
1,421
条评论
首页
栏目
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
103
篇与
的结果
2021-06-08
Spring Boot通过ApplicationRunner实现系统启动任务
在Spring Boot通过CommandLineRunner实现系统启动任务中我们介绍了通过CommandLineRunner实现启动任务。但是CommandLineRunner实现的启动任务,我们在传递入口参数时,只能传递基本类型,入法通过键值对的形式传递参数。ApplicationRunner的实现原理跟CommandLineRunner基本类似,我们终点说一下ApplicationRunner的用法。ApplicationRunner实现类中run()方法参数为ApplicationArguments,ApplicationArguments说明如下:通过getNonOptionArgs()获取基本参数,这个与CommandLineRunner的参数是一致的。通过getOptionNames()获取键。通过getOptionValues获取值。演示代码@Component @Order(1) @Slf4j public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) { List<String> nonOptionArgs = args.getNonOptionArgs(); Set<String> optionNames = args.getOptionNames(); List<String> optionValues = args.getOptionValues("name"); log.info("MyApplicationRunner>>>nonOptionArgs>>>" + nonOptionArgs.toString()); log.info("MyApplicationRunner>>>optionNames>>>" + optionNames.toString()); log.info("MyApplicationRunner>>>optionValues>>>" + optionValues.toString()); } }修改入口参数--name代表键,=后面是值,注意是--不是-
2021年06月08日
1,057 阅读
0 评论
0 点赞
2021-06-08
Spring Boot通过CommandLineRunner实现系统启动任务
所谓系统启动任务,是在系统启动时执行的一些操作,一般而言只在系统启动之时执行,并且一般只执行一次。比如加载配置文件、数据库初始化操作等。Spring Boot针对系统启动任务,提供了两种解决方案:一是实现CommandLineRunner接口另一种方式是实现ApplicationRunner接口。本文针对CommandLineRunner方式进行说明。Spring Boot项目在启动时,会遍历所有CommandLineRunner实现类并调用其中的run()方法。如果系统中有多个CommandLineRunner实现类,可以通过@Order()注解指定实现类的调用顺序,数值越小越先执行。{mtitle title="演示代码"/}MyCommandLineRunner1@Component @Order(1) @Slf4j public class MyCommandLineRunner1 implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("Runner1>>>" + Arrays.toString(args)); } }MyCommandLineRunner2@Component @Order(1) @Slf4j public class MyCommandLineRunner2 implements CommandLineRunner { @Override public void run(String... args) { log.info("Runner2>>>" + Arrays.toString(args)); } }idea配置入口参数@Order(1)用来描述CommandLineRunner的执行顺序,值越小越先执行。run()方法的参数是系统启动时传入的参数。
2021年06月08日
1,042 阅读
0 评论
0 点赞
2021-06-07
Spring Boot注册拦截器Interceptor
Spring MVC提供了AOP风格的拦截器,拥有更加精细的拦截器处理能力。Spring Boot中拦截器的注册更加方便,步骤如下:spring-boot-starter-web创建拦截器,实现HandlerInterceptor配置拦截器,定义配置类进行拦截器的配置增加依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>实现HandlerInterceptor代码如下:public class MyInterceptor implements HandlerInterceptor { private final Logger logger = LoggerFactory.getLogger(MyInterceptor.class); /** * 方法执行前运行 * 必须返回true,后面的方法才能执行 * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { logger.info("我先执行"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.info("我在controller方法之后执行"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.info("我最后执行"); } }配置拦截器@Configuration public class MyWebMvcConfig implements WebMvcConfigurer { /** * 拦截器 * * @param registry registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()) //配置拦截的路由 .addPathPatterns("/interceptor/**") //配置不拦截的路由 .excludePathPatterns("/demo/**") //配置顺序 .order(1); } }测试定义controller测试我们的拦截器@RestController @RequestMapping("interceptor") public class InterceptorController { private final Logger logger = LoggerFactory.getLogger(InterceptorController.class); @GetMapping("/test") public String testInterceptor(){ logger.info("我是controller方法"); return "success"; } }通过调用http://localhost:8080/interceptor/test查看输出日志。温馨提示拦截器按照preHandle→Controller→postHandle→afterHandle的顺序执行。只有preHandle方法返回true时,后面的方法才会执行。当拦截器链内存在多个拦截器时,postHandle在拦截器链内所有拦截器返回成功时才会调用。当拦截器链内存在多个拦截器时,afterHandle在拦截器链内所有拦截器返回true时才会调用。当拦截器链内存在多个拦截器时,如果第一个拦截器的preHandle方法返回false,则后面的方法都不会执行。调用controller方法时,只要配置了拦截的路由,哪怕前端请求404,仍然会调用preHandle、postHandle及afterHandle的方法。如果我们设置了order,代码如下:/** * 拦截器 * * @param registry registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor2()) //配置拦截的路由 .addPathPatterns("/interceptor/**") //配置不拦截的路由 .excludePathPatterns("/demo/**") //配置顺序 .order(200); registry.addInterceptor(new MyInterceptor1()) //配置拦截的路由 .addPathPatterns("/interceptor/**") //配置不拦截的路由 .excludePathPatterns("/demo/**") //配置顺序 .order(300); }
2021年06月07日
1,178 阅读
1 评论
1 点赞
2021-06-07
springboot 通过 DefaultErrorAttributes自定义错误信息
自定义error数据就是对返回的数据进行自定义。Spring Boot返回的Error信息一共有5条,分别是timestamp、status、error、path。在BasicErrorController的errorHtml()方法和error()方法,都是通过getErrorAttributes()方法获取Error信息的,该方法最终会调用DefaultErrorAttributes类的getErrorAttributes()方法,而DefaultErrorAttributes类是在ErrorMvcAutoConfiguration中默认提供的。当系统没有提供 errorAttributes 时才会采 DefaultErrorAttributes,因此自定义错误提示时,只需要自己提供一个ErrorAttributes即可,而DefaultErrorAttributes是ErrorAttributes的子类,因此只需要继承 DefaultErrorAttributes 即可。@Component public class MyErrorAttribute extends DefaultErrorAttributes { @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { Map<String, Object> map = super.getErrorAttributes(webRequest, options); map.put("errorMsg", "出错了"); return map; } }
2021年06月07日
2,094 阅读
0 评论
1 点赞
2021-06-06
SpringBoot 使用Gson
添加依赖需要排除web自带的jackson<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency>增加配置文件@Configuration public class GsonConfig { @Bean @ConditionalOnMissingBean public GsonHttpMessageConverter gsonHttpMessageConverter() { GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter(); gsonHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8); GsonBuilder gsonBuilder = new GsonBuilder(); //设置日期格式 gsonBuilder.setDateFormat("yyyy-MM-dd"); //设置忽略的字段 gsonBuilder.excludeFieldsWithModifiers(Modifier.PROTECTED); Gson gson = gsonBuilder.create(); gsonHttpMessageConverter.setGson(gson); return gsonHttpMessageConverter; } }增加测试类@Data public class Person { protected Integer age; private String name; private Date birthday; }测试 @GetMapping("test") public Person test() { Person person = new Person(); person.setAge(30); person.setBirthday(new Date()); person.setName("张三"); return person; }
2021年06月06日
1,148 阅读
0 评论
0 点赞
2021-06-06
Spring Boot使用profile
开发者在项目发布之前,一般需要频繁的在开发环境、测试环境及生产环境之间进行切换,这个时候大量的配置需要频繁更改,例如数据库配置、 redis配置、mongodb 配置等。频繁修改带来巨大工作量,Spring对此提供了解决方案(@Profile注解), Spring Boot则更进一步提供了更加简洁的解决方案, Spring Boot中约定的不同环境下配置文件名称规则为application-{profile} .properties, profile占位符表示当前环境的名称,具体配置步骤如下。创建配置文件在resources中创建两个配置文件,分别是application-dev.properties和application-prod.properties,分别代表开发环境及正式环境的配置。application-dev.propertiesdev指定端口为8080server.port=8080 server.servlet.context-path=/study server.tomcat.basedir=logapplication-prod.propertiesprod指定端口为8081server.port=8081 server.servlet.context-path=/study server.tomcat.basedir=log在application.properties中配置spring.profiles.active=prod在代码中配置在启动类中设置 public static void main(String[] args) { // SpringApplication.run(DemoApplication.class, args); SpringApplicationBuilder builder = new SpringApplicationBuilder(DemoApplication.class); builder.bannerMode(Banner.Mode.OFF); builder.application().setAdditionalProfiles("prod"); builder.run(args); }在项目启动时配置java -jar demo-0.0.1-SNAPSHOT.jar --spring.profile.active=prod
2021年06月06日
972 阅读
0 评论
0 点赞
2021-05-28
spring boot mybatis配置主从数据库(多数据源)
MyBatis配置多数据源基本步骤:预制两个测试的数据库master和cluster添加mybatis及druid依赖配置文件配置两个数据源(配置数据源时,必须要有一个主数据源)测试代码预制数据库表预制两个数据库,主库为master,并创建表test,从库为cluster,并创建表user。主库从库创建表语句:CREATE TABLE `test` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4预制数据INSERT INTO cluster.user (id, username) VALUES (1, '李四');从库从库创建表语句CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4预制语句INSERT INTO cluster.user (id, username) VALUES (1, '李四');添加项目依赖创建spring boot项目,并添加如下依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </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.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.4</version> </dependency>添加配置数据库连接在application.properties中添加两个数据库的配置文件#主库配置 master.datasource.url=jdbc:mysql://IP:3306/master?useSSL=false&characterEncoding=utf8 master.datasource.username=master master.datasource.password=master master.datasource.driver=com.mysql.jdbc.Driver #从库配置 cluster.datasource.url=jdbc:mysql://IP:3306/cluster?useSSL=false&characterEncoding=utf8 cluster.datasource.username=cluster cluster.datasource.password=cluster cluster.datasource.driver=com.mysql.jdbc.Driver添加数据源主库添加主库数据源MasterDataSourceConfig.javapackage cc.lisen.mybatsmultidatasource.config.ds; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; /** * 主库配置 * * @author laughing @2021.5.27 */ @Configuration @MapperScan(basePackages = MasterDataSourceConfig.PACKAGE,sqlSessionFactoryRef = "masterSqlSessionFactory") public class MasterDataSourceConfig { final static String PACKAGE = "cc.lisen.mybatsmultidatasource.dao.master"; private final static String mapperLocation = "classpath*:mapper/**/*.xml"; @Value("${master.datasource.url}") private String url; @Value("${master.datasource.username}") private String username; @Value("${master.datasource.password}") private String password; @Value("${master.datasource.driver}") private String driver; @Bean(name = "masterDataSource") @Primary public DataSource masterDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driver); return dataSource; } @Bean(name = "masterDataSourceTransactionManager") @Primary public DataSourceTransactionManager masterDataSourceTransactionManager() { return new DataSourceTransactionManager(masterDataSource()); } @Bean(name = "masterSqlSessionFactory") @Primary public SqlSessionFactory masterSqlSessionFactory(@Qualifier(value = "masterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.mapperLocation)); return sqlSessionFactoryBean.getObject(); } } 从库添加从库数据源ClusterDataSourceConfig.javapackage cc.lisen.mybatsmultidatasource.config.ds; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; /** * 从库配置 * * @author laughing @2021.5.27 */ @Configuration @MapperScan(basePackages = ClusterDataSourceConfig.PACKAGE,sqlSessionFactoryRef = "clusterSqlSessionFactory") public class ClusterDataSourceConfig { final static String PACKAGE = "cc.lisen.mybatsmultidatasource.dao.cluster"; private final static String mapperLocation = "classpath*:mapper/**/*.xml"; @Value("${cluster.datasource.url}") private String url; @Value("${cluster.datasource.username}") private String username; @Value("${cluster.datasource.password}") private String password; @Value("${cluster.datasource.driver}") private String driver; @Bean(name = "clusterDataSource") @Primary public DataSource clusterDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driver); return dataSource; } @Bean(name = "clusterDataSourceTransactionManager") @Primary public DataSourceTransactionManager clusterDataSourceTransactionManager() { return new DataSourceTransactionManager(clusterDataSource()); } @Bean(name = "clusterSqlSessionFactory") @Primary public SqlSessionFactory masterSqlSessionFactory(@Qualifier(value = "clusterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(ClusterDataSourceConfig.mapperLocation)); return sqlSessionFactoryBean.getObject(); } } 主库、从库的数据源通过@MapperScan注解注入不同数据库mapper所在的包。创建DAO主库主库的包位于cc.lisen.mybatsmultidatasource.dao.master与@MapperScan配置的位置要保持一致。主库的包里面创建一个selectAll()方法,用于查询所有数据,及查询主库Test表的所有数据。从库从库的包位于cc.lisen.mybatsmultidatasource.dao.cluster与@MapperScan配置的位置要保持一致。从库的包里面创建一个selectAll()方法,用于查询所有数据,及查询主库User表的所有数据。创建Service代码就不罗列了,也是有一个selectAll()方法。测试创建controller分别查询主库及从库的数据,如下@RestController public class UserController { @Resource private ITestService testService; @Resource private IUserService userService; @GetMapping("/master") public List<Test> getMasterAll(){ return testService.selectAll(); } @GetMapping("/cluster") public List<User> getClusterAll(){ return userService.selectAll(); } }
2021年05月28日
1,614 阅读
0 评论
0 点赞
2021-05-17
MQTT使用三之Spring Boot集成MQTT简单使用
Spring Boot集成MQTT支持动态创建topic。添加依赖 <!-- MQTT --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-integration</artifactId> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-mqtt</artifactId> </dependency>添加配置文件server: port: 9999 #mqtt的配置 mqtt: server: url: tcp://ip:1883 port: 1883 username: 用户名 password: 密码 client: consumerId: consumerCo publishId: publishCo default: topic: topic completionTimeout: 3000MQTT配置文件package cc.lisen.mqtt.config; import cc.lisen.mqtt.utils.MqttReceiveHandle; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.core.MessageProducer; import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; import org.springframework.integration.mqtt.core.MqttPahoClientFactory; import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.util.Arrays; import java.util.List; /** * Description:消息订阅配置 * * @author : laughing * DateTime: 2021-05-18 13:31 */ @Configuration public class MqttConfig { public final Logger logger = LoggerFactory.getLogger(this.getClass()); private static final byte[] WILL_DATA; static { WILL_DATA = "offline".getBytes(); } @Resource private MqttReceiveHandle mqttReceiveHandle; @Value("${mqtt.server.url}") private final String url = "tcp://139.198.172.114:1883"; @Value("${mqtt.server.port}") private final String port = "1883"; @Value("${mqtt.server.username}") private final String username = "admin"; @Value("${mqtt.server.password}") private final String password = "public"; @Value("${mqtt.client.consumerId}") private final String consumerId = "consumerClient"; @Value("${mqtt.client.publishId}") private final String publishId = "publishClient"; @Value("${mqtt.default.topic}") private final String topic = "topic"; @Value("${mqtt.default.completionTimeout}") private final Integer completionTimeout = 3000; //消息驱动 private MqttPahoMessageDrivenChannelAdapter adapter; //订阅的主题列表 private String listenTopics = ""; // //mqtt消息接收接口 // private MqttReceiveService mqttReceiveService; // // public void setMqttReceiveService(MqttReceiveService mqttReceiveService){ // this.mqttReceiveService = mqttReceiveService; // } /** * MQTT连接器选项 * **/ @Bean(value = "getMqttConnectOptions") public MqttConnectOptions getMqttConnectOptions(){ MqttConnectOptions mqttConnectOptions=new MqttConnectOptions(); // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接 mqttConnectOptions.setCleanSession(true); // 设置超时时间 单位为秒 mqttConnectOptions.setConnectionTimeout(10); mqttConnectOptions.setAutomaticReconnect(true); mqttConnectOptions.setUserName(username); mqttConnectOptions.setPassword(password.toCharArray()); mqttConnectOptions.setServerURIs(new String[]{url}); // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线,但这个方法并没有重连的机制 mqttConnectOptions.setKeepAliveInterval(10); // 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。 mqttConnectOptions.setWill("willTopic", WILL_DATA, 2, false); return mqttConnectOptions; } /** * MQTT工厂 * **/ @Bean public MqttPahoClientFactory mqttClientFactory() { DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); factory.setConnectionOptions(getMqttConnectOptions()); return factory; } /** * MQTT信息通道(生产者) * **/ @Bean public MessageChannel mqttOutboundChannel() { return new DirectChannel(); } /** * MQTT消息处理器(生产者) * **/ @Bean @ServiceActivator(inputChannel = "mqttOutboundChannel") public MessageHandler mqttOutbound() { MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(publishId, mqttClientFactory()); messageHandler.setAsync(true); messageHandler.setDefaultTopic(topic); return messageHandler; } /** * 配置client,监听的topic * MQTT消息订阅绑定(消费者) * **/ @Bean public MessageProducer inbound() { if(adapter == null){ adapter = new MqttPahoMessageDrivenChannelAdapter(consumerId, mqttClientFactory(), topic); } String [] topics = listenTopics.split(","); for(String topic: topics){ if(!StringUtils.isEmpty(topic)){ adapter.addTopic(topic,1); } } adapter.setCompletionTimeout(completionTimeout); adapter.setConverter(new DefaultPahoMessageConverter()); adapter.setQos(2); adapter.setOutputChannel(mqttInputChannel()); return adapter; } /** * 增加监听的topic * @param topicArr 消息列表 * @return 结果 */ public List<String> addListenTopic(String [] topicArr){ if(adapter == null){ adapter = new MqttPahoMessageDrivenChannelAdapter(consumerId, mqttClientFactory(), topic); } List<String> listTopic = Arrays.asList(adapter.getTopic()); for(String topic: topicArr){ if(!StringUtils.isEmpty(topic)){ if(!listTopic.contains(topic)){ adapter.addTopic(topic,1); } } } return Arrays.asList(adapter.getTopic()); } /** * 移除一个监听的topic * @param topic * @return */ public List<String> removeListenTopic(String topic){ if(adapter == null){ adapter = new MqttPahoMessageDrivenChannelAdapter(consumerId, mqttClientFactory(), topic); } List<String> listTopic = Arrays.asList(adapter.getTopic()); if(listTopic.contains(topic)){ adapter.removeTopic(topic); } return Arrays.asList(adapter.getTopic()); } /** * MQTT信息通道(消费者) * **/ @Bean public MessageChannel mqttInputChannel() { return new DirectChannel(); } /** * MQTT消息处理器(消费者) * **/ @Bean @ServiceActivator(inputChannel = "mqttInputChannel") public MessageHandler handler() { return new MessageHandler() { @Override public void handleMessage(Message<?> message) throws MessagingException { //处理接收消息 mqttReceiveHandle.handle(message); //String topic = message.getHeaders().get("mqtt_receivedTopic").toString(); //String msg = ((String) message.getPayload()).toString(); //mqttReceiveService.handlerMqttMessage(topic,msg); } }; } } 消息处理package cc.lisen.mqtt.utils; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.MqttCallback; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.Message; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; @Component public class MqttReceiveHandle implements MqttCallback { private final Logger logger = LoggerFactory.getLogger(MqttReceiveHandle.class); public void handle(Message<?> message) { try { logger.info("{},客户端号:{},主题:{},QOS:{},消息接收到的数据:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), message.getHeaders().get(MqttHeaders.ID), message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC), message.getHeaders().get(MqttHeaders.RECEIVED_QOS), message.getPayload()); //处理mqtt数据 this.handle(message.getPayload().toString()); } catch (Exception e) { e.printStackTrace(); logger.error("处理错误" + e.getMessage()); } } private void handle(String str) throws Exception { logger.info(str); } @Override public void connectionLost(Throwable throwable) { logger.warn("连接丢失"); } @Override public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { logger.info("消息到达:" + topic + "\n" + "消息内容:" + new String(mqttMessage.getPayload()) + "\nclientId:" + mqttMessage.getId()); } @Override public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { logger.info("clientId:" + iMqttDeliveryToken.getClient().getClientId()); } }消息发送package cc.lisen.mqtt.utils; import org.springframework.integration.annotation.MessagingGateway; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.handler.annotation.Header; /** * Description: * * @author : laughing * DateTime: 2021-05-18 13:44 */ @MessagingGateway(defaultRequestChannel = "mqttOutboundChannel") public interface MqttGateway { /** * 发送信息到MQTT服务器 * * @param data 发送的文本 */ void sendToMqtt(String data); /** * 发送信息到MQTT服务器 * * @param topic 主题 * @param payload 消息主体 */ void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload); /** * 发送信息到MQTT服务器 * * @param topic 主题 * @param qos 对消息处理的几种机制。 * 0 表示的是订阅者没收到消息不会再次发送,消息会丢失。 * 1 表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息。 * 2 多了一次去重的动作,确保订阅者收到的消息有一次。 * @param payload 消息主体 */ void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload); }测试package cc.lisen.mqtt.controller; import cc.lisen.mqtt.config.MqttConfig; import cc.lisen.mqtt.utils.MqttGateway; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; @RestController public class MqttController { @Resource private MqttGateway mqttGateway; @Resource private MqttConfig mqttConfig; @GetMapping("/add/{topic}") public String addTopic(@PathVariable("topic") String topic) { String[] topics = {topic}; List<String> list = mqttConfig.addListenTopic(topics); return list.toString(); } @GetMapping("/pub") public String pubTopic() { String topic = "temperature1"; String msg = "client msg at: " + String.valueOf(System.currentTimeMillis()); mqttGateway.sendToMqtt(topic, 2, msg); return "OK"; } @GetMapping("/del/{topic}") public String delTopic(@PathVariable("topic") String topic) { List<String> list = mqttConfig.removeListenTopic(topic); return list.toString(); } }
2021年05月17日
1,377 阅读
2 评论
0 点赞
2021-05-07
mybatis-plus自动注入sql语句
我们在日常开发中,一般会在表里面增加创建人(create_by)、创建时间(create_time)、修改人(update_by)、修改时间(update_time)四个字段。创建人:新增时,通过上下文获取用户信息。创建时间:新增时,获取系统当前时间。修改人:修改时,通过上下文获取用户信息。修改时间:修改时,获取系统当前时间。对于这种公共的字段,如果每次都在Sql中进行处理,那是相当繁琐也是没必要的。如果你用的是MyBatis-Plus那么一切就变的简单了。继承mybatis的Interceptor拦截器/** * Description: mybatis拦截器,自动注入创建人、创建时间、修改人、修改时间 * * @author : laughing * DateTime: 2021-05-06 17:13 */ @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) @Slf4j @SuppressWarnings("all") public class MybatisSqlInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; String sqlId = mappedStatement.getId(); SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); Object parameter = invocation.getArgs()[1]; if (parameter == null) { return invocation.proceed(); } if(SysOperateLog.class.isAssignableFrom(parameter.getClass()) || SysLoginInfo.class.isAssignableFrom(parameter.getClass())){ return invocation.proceed(); } if (SqlCommandType.INSERT == sqlCommandType) { Field[] fields = ConvertUtils.getAllFields(parameter); Long userId = SecurityUtils.getUserId(); for (Field field : fields) { try { if ("createBy".equals(field.getName())) { field.setAccessible(true); Object local_createBy = field.get(parameter); field.setAccessible(false); if (local_createBy == null || local_createBy.equals("")) { // 登录人账号 field.setAccessible(true); field.set(parameter, userId); field.setAccessible(false); } } // 注入创建时间 if ("createTime".equals(field.getName())) { field.setAccessible(true); Object local_createDate = field.get(parameter); field.setAccessible(false); if (local_createDate == null || local_createDate.equals("")) { field.setAccessible(true); field.set(parameter, new Date()); field.setAccessible(false); } } } catch (Exception e) { } } } if (SqlCommandType.UPDATE == sqlCommandType) { Long userId = SecurityUtils.getUserId(); Field[] fields = null; if (parameter instanceof MapperMethod.ParamMap) { MapperMethod.ParamMap<?> p = (MapperMethod.ParamMap<?>) parameter; if (p.containsKey("et")) { parameter = p.get("et"); } else { parameter = p.get("param1"); } if (parameter == null) { return invocation.proceed(); } fields = ConvertUtils.getAllFields(parameter); } else { fields = ConvertUtils.getAllFields(parameter); } for (Field field : fields) { try { if ("updateBy".equals(field.getName())) { //获取登录用户信息 // 登录账号 field.setAccessible(true); field.set(parameter, userId); field.setAccessible(false); } if ("updateTime".equals(field.getName())) { field.setAccessible(true); field.set(parameter, new Date()); field.setAccessible(false); } } catch (Exception e) { e.printStackTrace(); } } } return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // TODO Auto-generated method stub } }通过SqlSessionFactory加载插件/** * Mybatis-Plus配置 * * @author leeframe */ @Configuration @EnableTransactionManagement public class MyBatisPlusConfig { @javax.annotation.Resource private Environment env; static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; private final Logger logger = LoggerFactory.getLogger(MyBatisPlusConfig.class); @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor( new TenantLineHandler() { // manager_id = 1088248166370832385 // 获取租户 ID 值表达式,只支持单个 ID 值 @Override public Expression getTenantId() { return new StringValue(SecurityUtils.getOrgCode()); } // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件, // 这里设置 role表不需要该条件 @Override public boolean ignoreTable(String tableName) { return StringUtils.isEmpty(SecurityUtils.getOrgCode()) || LeeFrameConfig.getFilterTableList().stream().anyMatch(e -> e.equalsIgnoreCase(tableName)); } @Override public String getTenantIdColumn() { return "org_code"; } })); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setUseDeprecatedExecutor(false); } public static String setTypeAliasesPackage(String typeAliasesPackage) { ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); List<String> allResult = new ArrayList<String>(); try { for (String aliasesPackage : typeAliasesPackage.split(",")) { List<String> result = new ArrayList<String>(); aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; Resource[] resources = resolver.getResources(aliasesPackage); if (resources != null && resources.length > 0) { MetadataReader metadataReader = null; for (Resource resource : resources) { if (resource.isReadable()) { metadataReader = metadataReaderFactory.getMetadataReader(resource); try { result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } if (result.size() > 0) { HashSet<String> hashResult = new HashSet<String>(result); allResult.addAll(hashResult); } } if (allResult.size() > 0) { typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); } else { throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); } } catch (IOException e) { e.printStackTrace(); } return typeAliasesPackage; } public Resource[] resolveMapperLocations(String[] mapperLocations) { ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); List<Resource> resources = new ArrayList<Resource>(); if (mapperLocations != null) { for (String mapperLocation : mapperLocations) { try { Resource[] mappers = resourceResolver.getResources(mapperLocation); resources.addAll(Arrays.asList(mappers)); } catch (IOException e) { // ignore } } } return resources.toArray(new Resource[resources.size()]); } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatisPlus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatisPlus.mapperLocations"); String configLocation = env.getProperty("mybatisPlus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor()); return sessionFactory.getObject(); } @Bean public MybatisSqlInterceptor mybatisSqlInterceptor() { MybatisSqlInterceptor mybatisSqlInterceptor = new MybatisSqlInterceptor(); Properties properties = new Properties(); mybatisSqlInterceptor.setProperties(properties); return mybatisSqlInterceptor; } }
2021年05月07日
1,468 阅读
0 评论
0 点赞
2021-05-07
Spring Boot yaml文件配置列表
我们在Spring Boot的yaml配置文件中,一般配置的都是一些文本(字符串)。那么我们在yaml文件中如何配置列表或者数组呢。场景试想一下我们的场景:我们系统涉及到租户,数据库采用行级别的隔离,也就是说表里面有一个org_code列,作为租户之间数据隔离的条件,类似于where org_code = 'xx'。但是,我们不是所有的表都要进行,那么我们在过滤某些表不进行过滤时,首先肯定想到的就是在yaml文件中配置需要过滤的表名。敲代码yaml设置# 项目相关配置 leeframe: # 过滤表名,不进行租户的过滤 filterTableList: - sys_dict_type - sys_dict_data - sys_config - sys_organization - sys_job - sys_user_role配置映射@Component @ConfigurationProperties(prefix = "leeframe") public class LeeFrameConfig { /** * 过滤表名,不进行租户的过滤 */ private static List<String> filterTableList; public static List<String> getFilterTableList() { return filterTableList; } public void setFilterTableList(List<String> filterTableList) { LeeFrameConfig.filterTableList = filterTableList; } }使用LeeFrameConfig.getFilterTableList()
2021年05月07日
1,305 阅读
0 评论
1 点赞
2021-05-07
Spring Boot alibaba druid配置输出sql
用的slf4j打印的sql# 数据源配置 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver druid: # 主库数据源 master: url: jdbc:mysql://localhost:3306/leeframe?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: root # 从库数据源 slave: # 从数据源开关/默认关闭 enabled: false url: username: password: # 初始连接数 initialSize: 5 # 最小连接池数量 minIdle: 10 # 最大连接池数量 maxActive: 20 # 配置获取连接等待超时的时间 maxWait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最大生存的时间,单位是毫秒 maxEvictableIdleTimeMillis: 900000 # 配置检测连接是否有效 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false webStatFilter: enabled: true statViewServlet: enabled: true # 设置白名单,不填则允许所有访问 allow: url-pattern: /druid/* # 控制台管理用户名和密码 login-username: login-password: filter: stat: enabled: true # 慢SQL记录 log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true slf4j: enabled: true statement-create-after-log-enabled: false statement-log-enabled: true statement-executable-sql-log-enable: true statement-log-error-enabled: true result-set-log-enabled: false logging: level: druid: sql: Statement: DEBUG
2021年05月07日
1,348 阅读
0 评论
1 点赞
2021-03-16
Spring Boot 封装微信小程序工具类
/** * Description: 微信小程序工具类 * * @author : laughing * DateTime: 2021-03-16 11:10 */ @Slf4j public class WeChatUtils { public static String httpRequest(String requestUrl, String requestMethod, String output) { try { URL url = new URL(requestUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod(requestMethod); if (null != output) { OutputStream outputStream = connection.getOutputStream(); outputStream.write(output.getBytes(StandardCharsets.UTF_8)); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str; StringBuilder buffer = new StringBuilder(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); connection.disconnect(); return buffer.toString(); } catch (Exception e) { e.printStackTrace(); } return ""; } public static String decryptData(String encryptDataB64, String sessionKeyB64, String ivB64) { log.info("encryptDataB64:" + encryptDataB64); log.info("sessionKeyB64:" + sessionKeyB64); log.info("ivB64:" + ivB64); return new String( decryptOfDiyIv( Base64.decode(encryptDataB64), Base64.decode(sessionKeyB64), Base64.decode(ivB64) ) ); } private static final String KEY_ALGORITHM = "AES"; private static final String ALGORITHM_STR = "AES/CBC/PKCS7Padding"; private static Key key; private static Cipher cipher; private static void init(byte[] keyBytes) { // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要 int base = 16; if (keyBytes.length % base != 0) { int groups = keyBytes.length / base + 1; byte[] temp = new byte[groups * base]; Arrays.fill(temp, (byte) 0); System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length); keyBytes = temp; } // 初始化 Security.addProvider(new BouncyCastleProvider()); // 转化成JAVA的密钥格式 key = new SecretKeySpec(keyBytes, KEY_ALGORITHM); try { // 初始化cipher cipher = Cipher.getInstance(ALGORITHM_STR, "BC"); } catch (Exception e) { e.printStackTrace(); } } /** * 解密方法 * * @param encryptedData 要解密的字符串 * @param keyBytes 解密密钥 * @param ivs 自定义对称解密算法初始向量 iv * @return 解密后的字节数组 */ private static byte[] decryptOfDiyIv(byte[] encryptedData, byte[] keyBytes, byte[] ivs) { byte[] encryptedText = null; init(keyBytes); try { cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivs)); encryptedText = cipher.doFinal(encryptedData); } catch (Exception e) { e.printStackTrace(); } return encryptedText; } /** * 向指定 URL 发送POST方法的请求 * * @param url 发送请求的 URL * @param json 请求参数,请求参数应该是 json 的形式。 * @return 所代表远程资源的响应结果 */ public static String httpPost(String url, JSONObject json) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(json); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null) { result=result.concat(line); } } catch (Exception e) { System.out.println("发送 POST 请求出现异常!" + e); e.printStackTrace(); } //使用finally块来关闭输出流、输入流 finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; }
2021年03月16日
933 阅读
0 评论
0 点赞
1
...
5
6
7
...
9