首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,649 阅读
2
Mac打印机设置黑白打印
5,044 阅读
3
修改elementUI中el-table树形结构图标
4,947 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,705 阅读
5
intelliJ Idea 2022.2.X破解
4,460 阅读
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Vue
Mac
Spring Cloud
MyBatis
WordPress
MacOS
asp.net
Element UI
Nacos
MySQL
.Net
Spring Cloud Alibaba
Mybatis-Plus
Typecho
jQuery
Java Script
IntelliJ IDEA
微信小程序
Laughing
累计撰写
629
篇文章
累计收到
1,421
条评论
首页
栏目
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
629
篇与
的结果
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,152 阅读
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日
974 阅读
0 评论
0 点赞
2021-06-06
java多线程之ThreadLocal类
ThreadLocal是⼀个本地线程副本变量⼯具类。内部是⼀个弱引⽤的Map来维护。这⾥不详细介绍它的原理,⽽是只是介绍它的使⽤,以后有独⽴章节来介绍ThreadLocal类的原理。有些朋友称ThreadLocal为线程本地变量或线程本地存储。严格来说,ThreadLocal类并不属于多线程间的通信,⽽是让每个线程有⾃⼰”独⽴“的变量,线程之间互不影响。它为每个线程都创建⼀个副本,每个线程可以访问⾃⼰内部的副本变量。ThreadLocal类最常⽤的就是set⽅法和get⽅法。package com.company; public class ThreadLocalDemo { static class ThreadA implements Runnable{ private final ThreadLocal<String> threadLocal; public ThreadA(ThreadLocal<String> threadLocal){ this.threadLocal=threadLocal; } @Override public void run() { threadLocal.set("A"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadA输出:" + threadLocal.get()); } } static class ThreadB implements Runnable{ private final ThreadLocal<String> threadLocal; public ThreadB(ThreadLocal<String> threadLocal){ this.threadLocal=threadLocal; } @Override public void run() { threadLocal.set("B"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadB输出:" + threadLocal.get()); } } public static void main(String[] args){ ThreadLocal<String> threadLocal = new ThreadLocal<>(); new Thread(new ThreadA(threadLocal)).start(); new Thread(new ThreadB(threadLocal)).start(); } }
2021年06月06日
829 阅读
0 评论
0 点赞
2021-06-06
Java线程间的通信之join()方法
join()⽅法是Thread类的⼀个实例⽅法。它的作⽤是让当前线程陷⼊“等待”状态,等join的这个线程执⾏完成后,再继续执⾏当前线程。有时候,主线程创建并启动了⼦线程,如果⼦线程中需要进⾏⼤量的耗时运算,主线程往往将早于⼦线程结束之前结束。如果主线程想等待⼦线程执⾏完毕后,获得⼦线程中的处理完的某个数据,就要⽤到join⽅法了。示例代码package com.company; public class Join { static class ThreadJoin implements Runnable{ @Override public void run() { try { System.out.println("我是⼦线程,我先睡⼀秒"); Thread.sleep(1000); System.out.println("我是⼦线程,我睡完了⼀秒"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new ThreadJoin()); thread.start(); thread.join(); System.out.println("如果没有join,我会提前输出"); } }
2021年06月06日
781 阅读
0 评论
0 点赞
2021-06-06
Java线程间的通信之等待/通知机制
上⾯⼀种基于“锁”的⽅式,线程需要不断地去尝试获得锁,如果失败了,再继续尝试。这可能会耗费服务器资源。⽽等待/通知机制是另⼀种⽅式。Java多线程的等待/通知机制是基于Object类的wait()⽅法和notify(),notifyAll()⽅法来实现的。{message type="success" content="notify()⽅法会随机叫醒⼀个正在等待的线程,⽽notifyAll()会叫醒所有正在等待的线程。"/}前⾯我们讲到,⼀个锁同⼀时刻只能被⼀个线程持有。⽽假如线程A现在持有了⼀个锁lock并开始执⾏,它可以使⽤lock.wait() 让⾃⼰进⼊等待状态。这个时候,lock这个锁是被释放了的。这时,线程B获得了lock这个锁并开始执⾏,它可以在某⼀时刻,使⽤lock.notify(),通知之前持有lock锁并进⼊等待状态的线程A,说“线程A你不⽤等了,可以往下执⾏了”。{message type="warning" content="需要注意的是,这个时候线程B并没有释放锁lock,除⾮线程B这个时候使⽤lock.wait()释放锁,或者线程B执⾏结束⾃⾏释放锁,线程A才能得到lock锁。"/}package com.company; import java.util.stream.IntStream; public class WaitAndNotify { private static Object lock = new Object(); static class ThreadA implements Runnable{ @Override public void run() { synchronized (lock){ IntStream.range(1,5).forEach(i->{ System.out.println("ThreadA:"+i); lock.notify(); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }); } } } static class ThreadB implements Runnable{ @Override public void run() { synchronized (lock){ IntStream.range(1,5).forEach(i->{ System.out.println("ThreadB:"+i); lock.notify(); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }); } } } public static void main(String[] args) throws InterruptedException { new Thread(new ThreadA()).start(); Thread.sleep(1000); new Thread(new ThreadB()).start(); } } {message type="info" content="需要注意的是等待/通知机制使⽤的是使⽤同⼀个对象锁,如果你两个线程使⽤的是不同的对象锁,那它们之间是不能⽤等待/通知机制通信的。"/}
2021年06月06日
879 阅读
0 评论
1 点赞
2021-06-06
Java线程间的通信之锁与同步
在Java中,锁的概念都是基于对象的,所以我们⼜经常称它为对象锁。线程和锁的关系,我们可以⽤婚姻关系来理解。⼀个锁同⼀时间只能被⼀个线程持有。也就是说,⼀个锁如果和⼀个线程“结婚”(持有),那其他线程如果需要得到这个锁,就得等这个线程和这个锁“离婚”(释放)。在我们的线程之间,有⼀个同步的概念。什么是同步呢,假如我们现在有2位正在抄暑假作业答案的同学:线程A和线程B。当他们正在抄的时候,⽼师突然来修改了⼀些答案,可能A和B最后写出的暑假作业就不⼀样。我们为了A,B能写出2本相同的暑假作业,我们就需要让⽼师先修改答案,然后A,B同学再抄。或者A,B同学先抄完,⽼师再修改答案。这就是线程A,线程B的线程同步。可以以解释为:线程同步是线程之间按照⼀定的顺序执⾏。为了达到线程同步,我们可以使⽤锁来实现它。我们先来看看⼀个⽆锁的程序:package com.company; import java.util.stream.IntStream; public class NoneLock { static class ThreadA implements Runnable { @Override public void run() { IntStream.range(1, 100).forEach(i -> { System.out.println("threadA:" + i); }); } } static class ThreadB implements Runnable { @Override public void run() { IntStream.range(1, 100).forEach(i -> { System.out.println("threadB:" + i); }); } } public static void main(String[] args) { new Thread(new ThreadA()).start(); new Thread(new ThreadB()).start(); } } 某次的运行结果那我现在有⼀个需求,我想等A先执⾏完之后,再由B去执⾏,怎么办呢?最简单的⽅式就是使⽤⼀个“对象锁”:package com.company; import java.util.stream.IntStream; public class ObjectLock { private static final Object lock = new Object(); static class ThreadA implements Runnable { @Override public void run() { synchronized (lock) { IntStream.range(1, 100).forEach(i -> { System.out.println("threadA:" + i); }); } } } static class ThreadB implements Runnable { @Override public void run() { synchronized (lock) { IntStream.range(1, 100).forEach(i -> { System.out.println("threadB:" + i); }); } } } public static void main(String[] args) throws InterruptedException { new Thread(new ThreadA()).start(); Thread.sleep(10); new Thread(new ThreadB()).start(); } }
2021年06月06日
922 阅读
0 评论
0 点赞
2021-05-29
spring boot整合log4j2使用日志切面
在spring boot 整合log4j2中,我们在spring boot中整合了log4j,这篇文章,我们通过增加切面,实现自动记录日志。增加切面依赖<!-- 增加切面依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>增加切面@Aspect @Component @Slf4j public class LogAspect { /** * 定义切点 * 所有controller包下的public方法 */ @Pointcut("execution(public * cc.lisen.log4j.controller..*.*(..))") public void autoLog(){}; /** * 方法执行前 * @param joinPoint 切点 */ @Before("autoLog()") public void doBefore(JoinPoint joinPoint){ ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); assert servletRequestAttributes != null; HttpServletRequest request = servletRequestAttributes.getRequest(); log.info("method---"+request.getMethod()); } }
2021年05月29日
967 阅读
2 评论
1 点赞
2021-05-29
spring boot 整合log4j2
log4j2的优点相比与其他的日志系统,log4j2丢数据的情况少;在多线程环境下,性能高于logback等10倍以上;利用jdk1.5并发的特性,减少了死锁的发生。使用添加依赖引入依赖时,需要首先排除spring boot自带的logback,然后在添加log4j2的依赖。<!--排除 Spring-boot-starter 默认的日志配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <!--增加日志依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>{message type="success" content="推荐如上方式排除默认的logback依赖,而不是放到某一个starter依赖中"/}添加log4j2的配置文件修改默认配置文件名log4j2默认配置文件名为log4j2-spring.xml,如果想自定义配置文件,可以在`application.yamllogging: config: classpath:log4j2.xml这样我们在项目resources文件夹下创建文件log4j2.xml配置文件模板log4j2.xml配置模板内容如下:<?xml version="1.0" encoding="UTF-8"?> <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--> <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--> <configuration monitorInterval="5"> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--变量配置--> <Properties> <!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符--> <!-- %logger{36} 表示 Logger 名字最长36个字符 --> <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" /> <!-- 定义日志存储的路径 --> <property name="FILE_PATH" value="log" /> <property name="FILE_NAME" value="log4j2" /> </Properties> <appenders> <console name="Console" target="SYSTEM_OUT"> <!--输出日志的格式--> <PatternLayout pattern="${LOG_PATTERN}"/> <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> </console> <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用--> <File name="Filelog" fileName="${FILE_PATH}/test.log" append="false"> <PatternLayout pattern="${LOG_PATTERN}"/> </File> <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="${LOG_PATTERN}"/> <Policies> <!--interval属性用来指定多久滚动一次,默认是1 hour--> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--> <DefaultRolloverStrategy max="15"/> </RollingFile> <!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="${LOG_PATTERN}"/> <Policies> <!--interval属性用来指定多久滚动一次,默认是1 hour--> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--> <DefaultRolloverStrategy max="15"/> </RollingFile> <!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="${LOG_PATTERN}"/> <Policies> <!--interval属性用来指定多久滚动一次,默认是1 hour--> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--> <DefaultRolloverStrategy max="15"/> </RollingFile> </appenders> <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。--> <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效--> <loggers> <!--过滤掉spring和mybatis的一些无用的DEBUG信息--> <logger name="org.mybatis" level="info" additivity="false"> <AppenderRef ref="Console"/> </logger> <!--监控系统信息--> <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。--> <Logger name="org.springframework" level="info" additivity="false"> <AppenderRef ref="Console"/> </Logger> <root level="info"> <appender-ref ref="Console"/> <appender-ref ref="Filelog"/> <appender-ref ref="RollingFileInfo"/> <appender-ref ref="RollingFileWarn"/> <appender-ref ref="RollingFileError"/> </root> </loggers> </configuration>配置参数介绍日志级别机制:如果一条日志信息的级别大于等于配置文件的级别,就记录。trace:追踪,就是程序推进一下,可以写个trace输出debug:调试,一般作为最低级别,trace基本不用。info:输出重要的信息,使用较多warn:警告,有些信息不是错误信息,但也要给程序员一些提示。error:错误信息。用的也很多。fatal:致命错误。输出源CONSOLE(输出到控制台)FILE(输出到文件)格式SimpleLayout:以简单的形式显示HTMLLayout:以HTML表格显示PatternLayout:自定义形式显示PatternLayout自定义日志布局:%d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间,输出到毫秒的时间%-5level : 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0%c : logger的名称(%logger)%t : 输出当前线程名称%p : 日志输出格式%m : 日志内容,即 logger.info("message")%n : 换行符%C : Java类名(%F)%L : 行号%M : 方法名%l : 输出语句所在的行数, 包括类名、方法名、文件名、行数hostName : 本地机器名hostAddress : 本地ip地址简单测试lombok就是一个注解工具jar包,能帮助我们省略一繁杂的代码。为了方便使用,我们可以在需要打印日志的类上添加@Slf4j注解,然后通过log.输出日志,如下/** * 测试log4j2 * * @author laughing * @date 2021.5.29 */ @RequestMapping("") @RestController @Slf4j public class IndexController { @GetMapping public String index() { log.info("测试日志"); return "测试成功"; } }
2021年05月29日
952 阅读
0 评论
1 点赞
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,615 阅读
0 评论
0 点赞
2021-05-20
Vue使用NProgress
NProgressNProgress就是在页面顶端显示一个进度条。安装npm install --save nprogress使用引入NProgress我们新建一个global.js文件,通过钩子,在路由开始前,启动NProgress,路由结束后,停止NProgressimport NProgress from 'nprogress' import 'nprogress/nprogress.css' import router from './router' router.beforeEach((to, from, next) => { NProgress.start() setTimeout(() => { next() }, 1000) } ) router.afterEach((to, from) => { NProgress.done() })在main.js中引入global.jsimport './global'NProgress方法说明//方法 NProgress.start() - 显示进度条 NProgress.set(0.4) - 设置百分比 NProgress.inc() - 稍微增加 NProgress.done() - 完成进度(进度条消失)修改NProgress颜色颜色配置是在App.vue中的加入样式#nprogress .bar { background: red !important; }NProgress配置可以通过调用 .set(n)来设置进度,n是0-1的数字。 NProgress.set(0.0); // Sorta same as .start() NProgress.set(0.4); NProgress.set(1.0); // Sorta same as .done() 可以使用inc()随机增长进度条,注意,这个方法永远不会让进度条达到100%。 NProgress.inc(); NProgress.configure({ showSpinner: false }) //禁用进度环 NProgress.configure({ trickle : false }) //关闭进度条步进 NProgress.configure({ trickleRate : 10}) //每次步进增长多少 NProgress.configure({ trickleSpeed : 200}) //每次步进间隔,单位毫秒ms NProgress.configure({ easing: 'linear'}) //动画方向 NProgress.configure({ speed : 10}) //动画速度,单位毫秒ms NProgress.configure({ minimum : 0.08}) //最小百分比spinner即右侧的一个东西
2021年05月20日
1,300 阅读
0 评论
1 点赞
2021-05-19
修改elementUI中el-table树形结构图标
elementUI中的表格,可以显示成树形的结构,默认的样式,跟el-tree的样式一样,el-table如果是树形结构的话,如果树形有下级的话,显示的是一个小三角号,如果没有下级的话,是没有图标的。感觉这种图标不是特别好看,为了美化,我们可以替换成自己的图标。如果我们想改变树形的图标,可以在vue的style中加入以下样式<style scoped> .el-tree /deep/ .el-tree-node__expand-icon.expanded { -webkit-transform: rotate(0deg); transform: rotate(0deg); } /*有子节点 且未展开*/ .el-table /deep/ .el-table__expand-icon .el-icon-arrow-right:before { background: url('../../../../assets/images/文件夹未展开.png') no-repeat; content: ''; display: block; width: 16px; height: 16px; font-size: 16px; background-size: 16px; } /*有子节点 且已展开*/ .el-table /deep/ .el-table__expand-icon--expanded{ -webkit-transform: rotate(0); transform: rotate(0); } .el-table /deep/ .el-table__expand-icon--expanded .el-icon-arrow-right:before { background: url('../../../../assets/images/文件夹展开.png') no-repeat; content: ''; display: block; width: 16px; height: 16px; font-size: 16px; background-size: 16px; } /*没有子节点*/ .el-table /deep/ .el-table__placeholder::before { background: url('../../../../assets/images/文件.png') no-repeat; content: ''; display: block; width: 16px; height: 16px; font-size: 16px; background-size: 16px; } </style>
2021年05月19日
4,947 阅读
2 评论
5 点赞
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,394 阅读
2 评论
0 点赞
1
...
25
26
27
...
53