bt365体育投注.主頁欢迎您!!

    <acronym id="zvmrr"></acronym>
    <td id="zvmrr"></td>
  • <tr id="zvmrr"><label id="zvmrr"></label></tr>
  • <acronym id="zvmrr"></acronym>
  • Java旅途

    Java旅途 查看完整档案

    兰州编辑兰州理工大学  |  计算机科学与技术 编辑Java旅途  |  Java工程师 编辑 github.com/binzh303/spring-boot-route 编辑
    编辑

    公众号:Java旅途
    github:https://github.com/binzh303/s...

    个人动态

    Java旅途 回答了问题 · 12月1日

    IDEA有什么功能可以分析Java中一个方法的最上层的调用方法

    图片.png

    关注 2 回答 2

    Java旅途 回答了问题 · 11月27日

    Maven dependency Analyzer分析中,同一个依赖使用两个不同版本 怎么回事?

    依赖冲突,不用哪个版本,在该依赖中使用exclude进行排除

    关注 3 回答 3

    Java旅途 发布了文章 · 11月23日

    为什么你写的拦截器注入不了 Java bean?

    一、如何实现拦截器

    在Spring Boot项目中,拦截器经常被用来做登陆验证,日志记录等操作。拦截器是Spring提供的,所以可以将拦截器注成bean,由IOC容器来管理。实现拦截器的方式很简单,主要由以下两个步骤:

    1. 自定义拦截器类实现HandlerInterceptor接口
    2. 自定义WebMvc配置类实现WebMvcConfigurer接口,添加自定义拦截器类

    简要实现代码如下:

    自定义拦截器 LoginInterceptor

    public class LoginInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            String token = request.getHeader("token");
            if(StringUtils.isEmpty(token)){
                 ...
                return false;
            }
            return true;
        }
    }

    配置自定义拦截器:

    @Configuration
    public class WebConfiguration implements WebMvcConfigurer {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoginInterceptor())
                    // 拦截的请求
                    .addPathPatterns("/**")
                    // 不用拦截的请求
                    .excludePathPatterns("/login");
        }
    }

    这个拦截器的主要作用就是拦截所有访问请求,验证所带token是否有效,当token验证成功后,才能访问我们的业务接口。这时候就需要提供一个验证token有效性的接口,在拦截器中验证token,由于拦截器是Spring提供的,因此很容易想到使用@Component注解将拦截器注成一个 bean。然后使用@Autowired注解将验证token的类注入到拦截器进行验证。

    改造完的代码如下:

    验证token接口类:

    @Component
    public class TokenUtil {
        /**
         * 验证token 是否有效
         */
        public boolean checkToken(String token){
            ...
        }
    }

    改造完的拦截器代码如下:

    @Component
    public class LoginInterceptor implements HandlerInterceptor {
    
        @Autowired
        private TokenUtil tokenUtil;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            if(!tokenUtil.checkToken(token)){
                ...
                return false;
            }
            return true;
        }
    }

    调用接口时发现,TokenUtil并没有被注入进来!明明代码写的没问题,为什么不能正常注入TokenUtil呢?

    仔细观察我们自定义的配置类WebConfiguration,在添加拦截器的时候用的是new LoginInterceptor()如果想要拦截器生效,必须将拦截器配置到WebMvc的配置类中,就是我们自定义的WebConfiguration类。现在添加拦截器的时候是 new 了一个拦截器,也就是说并没有将拦截器托管给IOC容器,所以就无法引入Spring的bean对象

    二、如何将拦截器托管给IOC容器

    解决问题的思路也很简单,就是将拦截器也托管给IOC容器,这样容器内的对象就可以相互注入了。总共有以下三种方式进行处理上述问题。

    2.1 在WebConfiguration注入拦截器

    拦截器代码不变,在拦截器上使用@Component,同时在WebConfiguration中使用@Autowired注解将拦截器注入。

    拦截器代码:

    @Component
    public class LoginInterceptor implements HandlerInterceptor {
    }

    配置类代码:

    @Configuration
    public class WebConfiguration implements WebMvcConfigurer {
    
        @Autowired
        private LoginInterceptor loginInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(loginInterceptor);
        }
    }

    2.2 在WebConfiguration将拦截器注成bean

    拦截器无需增加@Component注解,在WebConfiguration类中使用@Bean注解将拦截器注成bean。

    拦截器代码:

    public class LoginInterceptor implements HandlerInterceptor {
    }

    配置类代码:

    @Configuration
    public class WebConfiguration implements WebMvcConfigurer {
    
        @Bean
        public LoginInterceptor loginInterceptor(){
            return new LoginInterceptor();
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor( loginInterceptor());
        }
    }

    2.3 通过构造器处理

    思路是在WebConfiguration类中注入需要的验证token的业务类,然后在初始化拦截器的时候将业务类通过构造器带入拦截器中,这样就不用把拦截器注成Spring Bean对象了。

    拦截器代码:

    public class LoginInterceptor implements HandlerInterceptor {
    
        private TokenUtil tokenUtil;
    
        public LoginInterceptor(TokenUtil tokenUtil) {
            this.tokenUtil = tokenUtil;
        }
    }

    配置类代码:

    @Configuration
    public class WebConfiguration implements WebMvcConfigurer {
    
        @Autowired
        private TokenUtil tokenUtil;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoginInterceptor(tokenUtil));
        }
    }

    三、总结

    网上关于拦截器的代码基本都是通过new一个拦截器进行配置的,这时候就会出现无法注入其他bean的情况。很多人想当然地直接在拦截器加@Component注解使其成为一个bean对象。这是一种错误的做法。我们需要保证的是在WebMvc配置类中添加的拦截器是Spring 的一个bean对象,也就是说我们需要将拦截器注成一个bean,同时将这个bean添加的WebMvc配置类中。

    四、点关注、不迷路

    如果觉得文章不错,欢迎关注点赞收藏,你们的支持是我创作的动力,感谢大家。

    如果文章写的有问题,请不要吝惜文笔,欢迎留言指出,我会及时核查修改。

    如果你还想看到更多别的东西,可以微信搜索「Java旅途」进行关注。「Java旅途」目前已经整理各种中间件的使用教程及各类Java相关的面试题。

    查看原文

    赞 8 收藏 6 评论 0

    Java旅途 发布了文章 · 11月3日

    Redis还可以做哪些事?

    上一篇文章中,讲到了redis五大基本数据类型的使用场景,除了string,hash,list,set,zset之外,redis还提供了一些其他的数据结构(当然,严格意义上也不算数据结构),一起来看看redis还可以做哪些事?

    一 Bitmaps

    在计算机中,使用二进制做为信息的基础单元,也就是输入的任何信息,最终在计算机底层都会转会为一串二进制的数字。在redis中,提供了Bitmaps来进行位操作。我们可以把Bitmaps想象成一个以位为单位的数组,数组的下标叫做偏移量。使用Bitmaps的优势就是占用空间更少。

    假如我们想记录员工今天是否登录过公司官网,我们可以日期做为key,员工id做为偏移量(这里员工id在数据库中是自增的),如果id是从1000开始,为了节省空间,一般会将员工id减去这个初始值来做为偏移量,偏移量一般从0开始。是否访问官网用01来表示。

    这样的话,id为3的员工访问了官网,就将他的值写成1

    # id为3的员工访问了官网
    setbit user:2020-11-04 3 1
    # id为18的员工访问了官网
    setbit user:2020-11-04 18 1

    查看某个员工是否访问过官网

    getbit user:2020-11-04 1

    查询指定范围(字节)内值为1的个数,比如我想查看id从1-30之间有多少员工访问了官网

    bitcount user:2020-11-04 0 3

    二 HyperLogLog

    HyperLogLog可以利用极小的内存空间完成数据统计,无法获取单条数据,只能做为统计使用,会有一定的误差率。

    假如我想统计访问官网的IP地址

    添加官网今天访问的ip列表

    # 2020-11-04访问的ip
    pfadd 2020-11-04:ip "ip1" "ip2" "ip3" 
    # 2020-11-05访问的ip
    pfadd 2020-11-05:ip "ip3" "ip4" "ip5" 

    计算今天官网访问的ip数

    pfcount 2020-11-04:ip

    返回结果为3

    查看2020-11-04和2020-11-05这两天总共有多少个独立ip访问过网站

    先将两天的数据做并集,并复制给某个值

    pfmerge 2020-11:ip 2020-11-04:ip 2020-11-05:ip

    然后使用pfcount命令查询,获得的值为5

    pfcount 2020-11:ip

    三 GEO

    Redis3.2版本中增加了GEO(地理位置定位)功能,可以使用此功能来获取附近的人。

    添加命令如下,可批量添加

    geoadd city longitud latitude member

    我们添加几个城市的位置信息,来获取某个城市附近的城市

    geoadd city 116.28 39.55 beijing 117.12 39.08 tianjin

    获取北京的经纬度命令如下

    geopos city beijing

    查看beijing和tianjin两座城市的距离

    geodist city beijing tianjin km

    最后面的km表示距离单位是公里,支持的单位有以下几个:

    • m,米
    • km,千米
    • mi,英里
    • ft,尺

    获取附近的位置有两个命令,georadius根据经纬度获取,georadiusbymember根据成员获取

    georadius key longitude laitude [单位]
    georadiusbymember key member [单位]

    后面还可以跟非必须参数,参数分别如下

    • withcoord:返回结果中包含经纬度
    • withdist:返回结果中包含距离中心位置的距离
    • withhash:返回结果中包含geohash(就是将经纬度转换为hash值)
    • COUNT count:指定返回结果的数量
    • asc|desc:返回结果按距离中心位置的距离排序
    • store key:将返回结果的地理位置信息保存到指定key中
    • storedist key:将返回结果距离中心位置的距离保存到指定key中

    四 发布订阅模式消息

    上一篇文章中讲到了可以使用list和zset来实现消息队列,但是上面实现的消息队列是点对点模式,也就是一条消息只能由一个消费者来消费。除此之外,redis还支持发布订阅模式,即一个消息由所有订阅者消费,比如广播、公告等等,发布一条公告后,所有关注了我的用户都可以收到这条公告。

    1. 发布消息

    发布到信道channel:message一条消息,消息内容为hi

    pulish channel:message hi
    1. 订阅信道

    订阅者可以订阅一个或多个信道,比如订阅channel:message

    subscribe channel:message
    1. 取消订阅
    unsubscribe channel:message
    1. 查看活跃信道
    pubsub channels
    1. 查看订阅数

    查看信道channel:message订阅个数

    pubsub numsub channel:message

    redis的发布订阅模式和专业的消息中间件相比,略显粗糙,但是实现起来非常简单,学习成本较低。

    五 Bloom Filter

    布隆过滤器是redis4版本中新增的一个功能。其实现原理和Bitmaps差不多,也是利用一个位数组,将你的值经过多个hash函数,得到对应的位数组的位置,将这些值设置为1。布隆过滤器经常别用来防止缓存穿透。

    存在的问题,如果说某个元素不存在,则一定不存在,如果说某个元素存在,则可能不存在。这是因为如果有三个元素abc要放入同一个数组中去,假设a经过三次hash,得到1,5,7三个位置,那么就会将这三个位置修改成1b经过三次hash,得到2,4,6三个位置,将这三个位置修改成1c经过三次hash得到2,5,7三个位置,但是经过前两个元素hash后,这三个位置已经修改成1了,那么我们能说c一定存在吗?显然不能!


    点关注、不迷路

    如果觉得文章不错,欢迎关注点赞收藏,你们的支持是我创作的动力,感谢大家。

    如果文章写的有问题,请不要吝惜文笔,欢迎留言指出,我会及时核查修改。

    如果你还想更加深入的了解我,可以微信搜索「Java旅途」进行关注。回复「1024」即可获得学习视频及精美电子书。每天7:30准时推送技术文章,让你的上班路不在孤独,而且每月还有送书活动,助你提升硬实力!

    查看原文

    赞 21 收藏 14 评论 5

    Java旅途 发布了文章 · 10月30日

    Redis可以做哪些事?

    Redis是一种基于键值对的NoSQL数据库,它的值主要由string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合)五种基本数据结构构成,除此之外还支持一些其他的数据结构和算法。key都是由字符串构成的,那么这五种数据结构的使用场景有哪些?一起来看看!

    一 字符串

    字符串类型是Redis最基础的数据结构,字符串类型可以是JSONXML甚至是二进制的图片等数据,但是最大值不能超过512MB。

    1.1 内部编码

    Redis会根据当前值的类型和长度决定使用哪种内部编码来实现。

    字符串类型的内部编码有3种:

    1. int:8个字节的长整型。
    2. embstr:小于等于39个字节的字符串。
    3. raw:大于39个字节的字符串。

    1.2 使用场景

    1.2.1 缓存

    在web服务中,使用MySQL作为数据库,Redis作为缓存。由于Redis具有支撑高并发的特性,通常能起到加速读写和降低后端压力的作用。web端的大多数请求都是从Redis中获取的数据,如果Redis中没有需要的数据,则会从MySQL中去获取,并将获取到的数据写入redis。

    1.2.2 计数

    Redis中有一个字符串相关的命令incr keyincr命令对值做自增操作,返回结果分为以下三种情况:

    • 值不是整数,返回错误
    • 值是整数,返回自增后的结果
    • key不存在,默认键为0,返回1

    比如文章的阅读量,视频的播放量等等都会使用redis来计数,每播放一次,对应的播放量就会加1,同时将这些数据异步存储到数据库中达到持久化的目的。

    1.2.3 共享Session

    在分布式系统中,用户的每次请求会访问到不同的服务器,这就会导致session不同步的问题,假如一个用来获取用户信息的请求落在A服务器上,获取到用户信息后存入session。下一个请求落在B服务器上,想要从session中获取用户信息就不能正常获取了,因为用户信息的session在服务器A上,为了解决这个问题,使用redis集中管理这些session,将session存入redis,使用的时候直接从redis中获取就可以了。

    1.2.4 限速

    为了安全考虑,有些网站会对IP进行限制,限制同一IP在一定时间内访问次数不能超过n次。

    二 哈希

    Redis中,哈希类型是指一个键值对的存储结构。

    2.1 内部编码

    哈希类型的内部编码有两种:

    • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)同时所有值都小于hash-max-ziplist-value配置(默认64字节)时使用。ziplist使用更加紧凑的结构实现多个元素的连续存储,所以比hashtable更加节省内存。
    • hashtable(哈希表):当ziplist不能满足要求时,会使用hashtable。

    2.2 使用场景

    由于hash类型存储的是一个键值对,比如数据库有以下一个用户表结构

    idnameage
    1Java旅途18

    将以上信息存入redis,用表明:id作为key,用户属性作为值:

    hset user:1 name Java旅途 age 18

    使用哈希存储会比字符串更加方便直观

    三 列表

    列表类型用来存储多个有序的字符串,一个列表最多可以存储2^32-1个元素,列表的两端都可以插入和弹出元素。

    3.1 内部编码

    列表的内部编码有两种:

    • ziplist(压缩列表):当哈希类型元素个数小于list-max-ziplist-entries配置(默认512个)同时所有值都小于list-max-ziplist-value配置(默认64字节)时使用。ziplist使用更加紧凑的结构实现多个元素的连续存储,所以比hashtable更加节省内存。
    • linkedlist(链表):当ziplist不能满足要求时,会使用linkedlist。

    3.2 使用场景

    3.2.1 消息队列

    列表用来存储多个有序的字符串,既然是有序的,那么就满足消息队列的特点。使用lpush+rpop或者rpush+lpop实现消息队列。除此之外,redis支持阻塞操作,在弹出元素的时候使用阻塞命令来实现阻塞队列。

    3.2.2 栈

    由于列表存储的是有序字符串,满足队列的特点,也就能满足栈先进后出的特点,使用lpush+lpop或者rpush+rpop实现栈。

    3.2.3 文章列表

    因为列表的元素不但是有序的,而且还支持按照索引范围获取元素。因此我们可以使用命令lrange key 0 9分页获取文章列表

    四 集合

    集合类型也可以保存多个字符串元素,与列表不同的是,集合中不允许有重复元素并且集合中的元素是无序的。一个集合最多可以存储2^32-1个元素。

    4.1 内部编码

    集合类型的内部编码有两种:

    • intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,redis会选用intset来作为集合的内部实现,从而减少内存的使用。
    • hashtable(哈希表):当intset不能满足要求时,会使用hashtable。

    4.2 使用场景

    4.2.1 用户标签

    例如一个用户对篮球、足球感兴趣,另一个用户对橄榄球、乒乓球感兴趣,这些兴趣点就是一个标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同感兴趣的标签。给用户打标签的时候需要①给用户打标签,②给标签加用户,需要给这两个操作增加事务。

    • 给用户打标签
    sadd user:1:tags tag1 tag2
    • 给标签添加用户
    sadd tag1:users user:1
    
    sadd tag2:users user:1

    使用交集(sinter)求两个user的共同标签

    sinter user:1:tags user:2:tags

    4.2.2 抽奖功能

    集合有两个命令支持获取随机数,分别是:

    • 随机获取count个元素,集合元素个数不变

    srandmember key [count]

    • 随机弹出count个元素,元素从集合弹出,集合元素个数改变

    spop key [count]

    用户点击抽奖按钮,参数抽奖,将用户编号放入集合,然后抽奖,分别抽一等奖、二等奖,如果已经抽中一等奖的用户不能参数抽二等奖则使用spop,反之使用srandmember

    五 有序集合

    有序集合和集合一样,不能有重复元素。但是可以排序,它给每个元素设置一个score作为排序的依据。最多可以存储2^32-1个元素。

    5.1 内部编码

    有序集合类型的内部编码有两种:

    • ziplist(压缩列表):当有序集合的元素个数小于list-max-ziplist-entries配置(默认128个)同时所有值都小于list-max-ziplist-value配置(默认64字节)时使用。ziplist使用更加紧凑的结构实现多个元素的连续存储,更加节省内存。
    • skiplist(跳跃表):当不满足ziplist的要求时,会使用skiplist。

    5.2 使用场景

    5.2.1 排行榜

    用户发布了n篇文章,其他人看到文章后给喜欢的文章点赞,使用score来记录点赞数,有序集合会根据score排行。流程如下

    用户发布一篇文章,初始点赞数为0,即score为0

    zadd user:article 0 a

    有人给文章a点赞,递增1

    zincrby user:article 1 a

    查询点赞前三篇文章

    zrevrange user:article 0 2

    查询点赞后三篇文章

    zrange user:article 0 2

    5.2.2 延迟消息队列

    下单系统,下单后需要在15分钟内进行支付,如果15分钟未支付则自动取消订单。将下单后的十五分钟后时间作为score,订单作为value存入redis,消费者轮询去消费,如果消费的大于等于这笔记录的score,则将这笔记录移除队列,取消订单。

    总结

    在开发中,字符串类型是用的最多的数据类型,导致我们忽视了redis的其他四种数据类型,在具体场景下选择具体的数据类型对提升redis性能有非常大的帮助。redis虽然支持消息队列的实现,但是并不支持ack。所以redis实现的消息队列不能保证消息的可靠性,除非自己实现消息确认机制,不过这非常麻烦,所以如果是重要的消息还是推荐使用专门的消息队列去做。


    点关注、不迷路

    如果觉得文章不错,欢迎关注点赞收藏,你们的支持是我创作的动力,感谢大家。

    如果文章写的有问题,请不要吝惜文笔,欢迎留言指出,我会及时核查修改。

    如果你还想更加深入的了解我,可以微信搜索「Java旅途」进行关注。回复「1024」即可获得学习视频及精美电子书。每天7:30准时推送技术文章,让你的上班路不在孤独,而且每月还有送书活动,助你提升硬实力!

    查看原文

    赞 3 收藏 3 评论 1

    Java旅途 发布了文章 · 10月24日

    1024|推荐一个开源免费的Spring Boot教程

    2020-1024=996!

    今天,星期六,你们是否加班了?我反正加了!早上去公司开了一早上会,中午回家写下了这篇文章。

    今天,我要推荐一个开源免费的Spring Boot项目,就是我最近日更的Spring Boot系列文章——spring-boot-route。

    github地址:https://github.com/binzh303/s...

    spring-boot-route包含基础知识、Web开发、数据访问、缓存服务、消息队列、日志管理、服务监控、定时任务及其他知识等九大部分,二十三篇文章,内容几乎涉猎了开发中比较常用的技术点。具体内容目录如下:

    代码如下:

    代码及文章已经上传至github,点击文末的阅读原文可直接跳转到github地址。如果觉得文章不错,点个star支持一下。

    这个系列的内容难易程度参差不齐,基本上都是我工作这几年用到的或者是想要了解的一些内容。最开始列提纲的时候差不多有三十篇文章,后面经过反复推敲去掉了安全框架模块及几个不常用的技术点,最终保留了23篇,组成了这个系列。

    冥冥之中自有天意

    九月份开始列提纲,零零碎碎的写了几篇文章。国庆的时候基本窝在家里写这个系列,同时开始在公众号上推送。没想到更新完正好是10月23,这真不是有意赶1024。直到昨晚休息,我都以为今天是10月23日,今天文章的题目我都想好了“1024前夕,我带着他向你们走来了”。但是今早发现朋友圈有太多的关于1024的话题,打开日历才发现这个日子已经到了。

    为什么要写这个系列

    写这个系列主要有三个原因,其一:在更新公众号的这些日子里,有很多读者加我,他们跟我说的第一句话基本上都是我工作不久或者我还在上学,问我应该学点什么。其二:我坚持每天都更新一篇文章,最难的不是写文章,而是考虑写什么内容,正好更新这个系列后,这段时间不会为写什么而发愁了,而且有些内容我也不是很熟,可以巩固一下基础。其三:在我工作的这几年里,我觉得一个合格程序员应该做的三件事,分别是搭建自己的博客,写一个开源项目,出版一本图书。搭建博客我在去年已经完成了(当然,后来关停了),出书的话我目前还没有这个打算,所以就把第二件事情做完。天天逛github,也是时候为开源出一份力了。

    这段时间的得与失

    说实话,这个系列的文章我觉得有点拉跨了。最明显的就是在写这个系列前公众号文章的阅读量基本200多,好点的文章可以达到400多,而这个系列的文章阅读量基本维持在100以内。虽然说写公众号最主要的还是沉淀自己,但是没有人读你写的文章还是有点难受的,就像演员没有观众。当然,也有一小部分人坚持跟着文章实践,他们留言、点赞、分享,你们是我坚持更新完的动力。

    虽然公众号上看的人很少,但是在其他网站上也得到了一些业内人士的认可,其中有几个出版社的编辑也加到了我,联系我将这个系列出版图书。

    目前我还没有这个打算,接下来一段时间内,我还是会持久关注这个系列。各位小伙伴如果有想补充的内容直接微信反馈给我或者github上提issue。如果我觉得有必要,我会加到这个系列中来。


    Spring Boot系列教程目录

    spring-boot-route(一)Controller接收参数的几种方式

    spring-boot-route(二)读取配置文件的几种方式

    spring-boot-route(三)实现多文件上传

    spring-boot-route(四)全局异常处理

    spring-boot-route(五)整合swagger生成接口文档

    spring-boot-route(六)整合JApiDocs生成接口文档

    spring-boot-route(七)整合jdbcTemplate操作数据库

    spring-boot-route(八)整合mybatis操作数据库

    spring-boot-route(九)整合JPA操作数据库

    spring-boot-route(十)多数据源切换

    spring-boot-route(十一)数据库配置信息加密

    spring-boot-route(十二)整合redis做为缓存

    spring-boot-route(十三)整合RabbitMQ

    spring-boot-route(十四)整合Kafka

    spring-boot-route(十五)整合RocketMQ

    spring-boot-route(十六)使用logback生产日志文件

    spring-boot-route(十七)使用aop记录操作日志

    spring-boot-route(十八)spring-boot-adtuator监控应用

    spring-boot-route(十九)spring-boot-admin监控服务

    spring-boot-route(二十)Spring Task实现简单定时任务

    spring-boot-route(二十一)quartz实现动态定时任务

    spring-boot-route(二十二)实现邮件发送功能

    spring-boot-route(二十三)开发微信公众号

    这个系列的文章都是工作中频繁用到的知识,学完这个系列,应付日常开发绰绰有余。如果还想了解其他内容,扫面下方二维码告诉我,我会进一步完善这个系列的文章!

    查看原文

    赞 34 收藏 27 评论 2

    Java旅途 发布了文章 · 10月23日

    spring-boot-route(二十三)开发微信公众号

    在讲微信公众号开发之前,先来大概了解一下微信公众号。微信公众号大体上可以分为服务号和订阅号,订阅号和服务号的区别如下:

    1. 服务号可以申请微信支付功能。
    2. 服务号只能由企业申请,订阅号可以有企业或个人申请。
    3. 订阅号和服务号每月推送消息次数不同,订阅号每天可以推送一次,服务号每月可以推送四次。
    4. 服务号推送的消息会出现在用户的聊天列表中,而订阅号推送的消息显示在订阅号文件夹中。
    5. 还有一些其他接口功能的区别和限制,总的来说服务号支持更高级的功能开发。

    订阅号更加偏向于向用户传递咨询,一般各种技术类公众号都属于订阅号,订阅号的消息推送并不会有太显眼的提醒,如果你想让某个公众号的推送内容更加显眼,可以选择置为星标。置为星标后公众号会显示在所有订阅号的最顶部,同时收到消息后会有黄色五角星星标提醒。

    一 公众号配置服务器

    微信官方提供了非常完善的接入文档,如果想了解文档的具体内容,直接浏览器搜索微信开发文档就可以了。但是为了方便开发,一般不会直接去根据微信开发文档进行开发,gitee上有许多开源项目对微信开发文档进行了封装,这里我使用mica-weixin开发包进行演示,mica-weixinjfinal-weixin的boot版本。

    配置服务器信息很简单,具体流程就是微信服务发送请求一个请求给业务服务器,业务服务器验证请求后给微信服务一个响应

    1.1 搭建业务服务

    本地搭建一个spring-boot-weixin的项目,使用内网穿透工具进行穿透,使其可以与外网进行通信。

    1.1.1 引入mica-weixin依赖

    <dependency>
        <groupId>net.dreamlu</groupId>
        <artifactId>mica-weixin</artifactId>
        <version>2.0.1</version>
    </dependency>

    1.1.2 配置公众号信息

    mica-weixin通过配置文件进行公众号信息的配置,如果你想通过数据库配置公众号信息,可以参考我以前写过的一篇文章jfinal-weixin自定义配置支持多公众号

    dream:
      weixin:
        wx-configs:
        - appId: xxxxxx
          appSecret: xxxxxx
          token: javatrip
          encodingAesKey: xxxxxx

    appIdappSecret可在公众号后台进行查看,具体位置在菜单开发—>基本配置中,其中appSecret要妥善保管,现在公众号已经不支持查看appSecret了,如果你忘了appSecret,只能进行重置。

    1.1.3 开发消息校验接口

    mica-weixin已经为我们提供好了消息校验接口,只需要继承DreamMsgControllerAdapter就可以了。

    @WxMsgController("/weixin/wx")
    public class WeiXinMsgController extends DreamMsgControllerAdapter {
        @Override
        protected void processInFollowEvent(InFollowEvent inFollowEvent) {
        }
    
        @Override
        protected void processInTextMsg(InTextMsg inTextMsg) {
        }
    
        @Override
        protected void processInMenuEvent(InMenuEvent inMenuEvent) {
        }
    }

    同时,需要开启缓存,由于mica-weixin的将access_token等信息放在了缓存中。在启动类上加@EnableCaching就开启了。

    @SpringBootApplication
    @EnableCaching
    public class WeixinApplication {
        public static void main(String[] args) {
            SpringApplication.run(WeixinApplication.class, args);
        }
    }

    1.1.4 公众号后台配置服务器信息

    使用内网穿透工具穿透内网地址,然后在公众号后台菜单开发—>基本配置中填写服务器配置信息。

    填写完成后点击启用,这样就完成了微信服务器和业务服务器的关系配置。开启开发者配置后,自动回复、自定义菜单等功能都不能正常使用了。这时候就需要去调用对应的接口实现这些功能。

    二 实现各种消息接口

    2.1 关注消息

    在一步中,自定义类WeiXinMsgController中需要重写三个父类中的方法,其中processInFollowEvent()就是关注和取消关注的方法,取消关注后用户虽然不能收到消息,但是后台可以接收到用户取消关注的事件。

    @Override
    protected void processInFollowEvent(InFollowEvent inFollowEvent) {
    
        OutTextMsg defaultMsg = new OutTextMsg(inFollowEvent);
        // 关注
        if(InFollowEvent.EVENT_INFOLLOW_SUBSCRIBE.equals(inFollowEvent.getEvent())){
            // 可将关注用户录入db,此处可以获取到用户openid
            String openId = inFollowEvent.getFromUserName();
            // 查询db,根据响应消息类型封装消息体
            if("文本消息"){
                OutTextMsg otm = new OutTextMsg(inFollowEvent);
                otm.setContent("消息内容");
                render(otm);
                return;
            }else if("图片消息"){
                OutImageMsg oim = new OutImageMsg(inFollowEvent);
                // 这里需要调用微信提供的素材接口,将图片上传至素材库。
                oim.setMediaId("图片素材id");
                render(oim);
                return;
            }else if("图文消息"){
                OutNewsMsg onm = new OutNewsMsg(inFollowEvent);
                onm.addNews("标题","简介","图片地址","图文链接");
                render(onm);
                return;
            }else if("视频消息"){
                OutVideoMsg ovm = new OutVideoMsg(inFollowEvent);
                ovm.setTitle("标题");
                ovm.setDescription("简介");
                ovm.setMediaId("视频素材id");
                render(ovm);
                return;
            }else{
                defaultMsg.setContent("感谢关注");
            }
        }
        // 取消关注
        if(InFollowEvent.EVENT_INFOLLOW_UNSUBSCRIBE.equals(inFollowEvent.getEvent())){
            log.info("用户取消关注了");
            // 此处可以将取消关注的用户更新db
        }
    }

    2.2 关键词消息

    响应内容跟关注消息一样,查询db去匹配关键词,然会根据消息内容封装对应的消息体进行返回,如果没匹配到关键词则回复统一的消息内容。processInTextMsg()方法就是用来回复关键词消息的。

    @Override
    protected void processInTextMsg(InTextMsg inTextMsg) {
    
        String content = inTextMsg.getContent();
        // 根据用户发送的content去查询db中的响应内容
        if("文本消息"){
            OutTextMsg otm = new OutTextMsg(inTextMsg);
            otm.setContent("消息内容");
            render(otm);
            return;
        }else if("图片消息"){
            OutImageMsg oim = new OutImageMsg(inTextMsg);
            // 这里需要调用微信提供的素材接口,将图片上传至素材库。
            oim.setMediaId("图片素材id");
            render(oim);
            return;
        }else if("图文消息"){
            OutNewsMsg onm = new OutNewsMsg(inTextMsg);
            onm.addNews("标题","简介","图片地址","图文链接");
            render(onm);
            return;
        }else if("视频消息"){
            OutVideoMsg ovm = new OutVideoMsg(inTextMsg);
            ovm.setTitle("标题");
            ovm.setDescription("简介");
            ovm.setMediaId("视频素材id");
            render(ovm);
            return;
        }else{
            OutTextMsg otm = new OutTextMsg(inTextMsg);
            otm.setContent("暂未查到关键词...");
        }
    }

    2.3 菜单消息

    点击菜单后也是一样,通过processInMenuEvent()方法进行响应内容的回复。

    @Override
    protected void processInMenuEvent(InMenuEvent inMenuEvent) {
        String eventKey = inMenuEvent.getEventKey();
        // 根据用户发送的content去查询db中的响应内容
        if("文本消息"){
            OutTextMsg otm = new OutTextMsg(inMenuEvent);
            otm.setContent("消息内容");
            render(otm);
            return;
        }else if("图片消息"){
            OutImageMsg oim = new OutImageMsg(inMenuEvent);
            // 这里需要调用微信提供的素材接口,将图片上传至素材库。
            oim.setMediaId("图片素材id");
            render(oim);
            return;
        }else if("图文消息"){
            OutNewsMsg onm = new OutNewsMsg(inMenuEvent);
            onm.addNews("标题","简介","图片地址","图文链接");
            render(onm);
            return;
        }else if("视频消息"){
            OutVideoMsg ovm = new OutVideoMsg(inMenuEvent);
            ovm.setTitle("标题");
            ovm.setDescription("简介");
            ovm.setMediaId("视频素材id");
            render(ovm);
            return;
        }else{
            OutTextMsg otm = new OutTextMsg(inMenuEvent);
            otm.setContent("无效链接,请重试...");
        }
    }

    三 接口API调用

    目前,微信提供的接口对订阅号的限制比较大,未认证的订阅号基本上只有接收消息的几个功能接口。

    调用接口的时候需要传递token,获取token需要在微信后台中配置业务服务器的白名单。如下:

    如果需要配置多个白名单ip,使用回车键将多个ip分隔开。

    mica-weixin提供了所有的接口封装,具体可参考它的官方文档,如果要获取微信菜单,可以这样写:

    @WxApi("weixin/api")
    public class WeiXinApiController {
        @GetMapping("menu")
        @ResponseBody
        public String getMenu(){
            ApiResult menu = MenuApi.getMenu();
            return menu.getJson();
        }
    }

    @WxApi这个是它的自定义注解,其实就是包含了@RequestMapping@Controller

    四 其他事项

    4.1 多公众号配置

    mica-weixin提供了多公众号配置的功能,使用ThreadLocalappid进行绑定。只需要简单配置即可实现多公众号配置。

    dream:
      weixin:
        wx-configs:
          - appId: xxxxxx
            appSecret: xxxxxx
            token: javatrip
            encodingAesKey: xxxxxx
          - appId: xxxxxx
            appSecret: xxxxxx
            token: javatrip
            encodingAesKey: xxxxxx

    4.2 redis配置

    access_token的有效期是2小时,并且该接口有调用次数限制,mica-weixinaccess_token存储在redis中,避免每次调用接口都去获取access-token,因此项目需要配置redis。

    spring:
      redis:
        host: localhost
        port: 6379

    4.3 手动选择ThreadLocal

    如果想要开发微信公众号的后台管理功能,多公众号的时候就需要手动去指定当前线程使用哪个公众号信息。如下:

    ApiConfigKit.setThreadLocalAppId(appid);

    至此,SpringBoot开发微信公众号就算完成了,由于订阅号开放的接口太少了,好多功能不能正常演示。还有mica-weixin也许不是最好的选择,如果想试着开发微信公众号,可以在gitee上找一下开发包。至于我为什么会使用mica-weixin,是因为我曾用过一段时间的jfinal框架,与之配套的微信开发包就是jfinal-weixin,也就是jfinal版的mica-weixin


    本文示例代码已上传至github,点个star支持一下!

    Spring Boot系列教程目录

    spring-boot-route(一)Controller接收参数的几种方式

    spring-boot-route(二)读取配置文件的几种方式

    spring-boot-route(三)实现多文件上传

    spring-boot-route(四)全局异常处理

    spring-boot-route(五)整合swagger生成接口文档

    spring-boot-route(六)整合JApiDocs生成接口文档

    spring-boot-route(七)整合jdbcTemplate操作数据库

    spring-boot-route(八)整合mybatis操作数据库

    spring-boot-route(九)整合JPA操作数据库

    spring-boot-route(十)多数据源切换

    spring-boot-route(十一)数据库配置信息加密

    spring-boot-route(十二)整合redis做为缓存

    spring-boot-route(十三)整合RabbitMQ

    spring-boot-route(十四)整合Kafka

    spring-boot-route(十五)整合RocketMQ

    spring-boot-route(十六)使用logback生产日志文件

    spring-boot-route(十七)使用aop记录操作日志

    spring-boot-route(十八)spring-boot-adtuator监控应用

    spring-boot-route(十九)spring-boot-admin监控服务

    spring-boot-route(二十)Spring Task实现简单定时任务

    spring-boot-route(二十一)quartz实现动态定时任务

    spring-boot-route(二十二)实现邮件发送功能

    spring-boot-route(二十三)开发微信公众号

    这个系列的文章都是工作中频繁用到的知识,学完这个系列,应付日常开发绰绰有余。如果还想了解其他内容,扫面下方二维码告诉我,我会进一步完善这个系列的文章!

    查看原文

    赞 3 收藏 1 评论 0

    Java旅途 发布了文章 · 10月22日

    spring-boot-route(二十二)实现邮件发送功能

    在项目开发中,除了需要短信验证外,有时候为了节省 短信费也会使用邮件发送。在Spring项目中发送邮件需要封装复杂的消息体,不太方便。而在Spring Boot项目中发送邮件就太简单了,下面一起来看看Spring Boot如何发送邮件。

    本文以126邮箱为例进行邮件发送功能,其他邮箱的配置也都大同小异。

    1. 获取授权码

    常用的电子协议有POP3SMTPIMAP,协议的具体区别就不进行详细介绍了。这里选择smtp协议进行演示。登录邮箱,在设置中找到协议地址,点击开启。授权码只会显示一次,需要保存好。

    下面是126邮箱对应的三种协议主机地址:

    2. 添加依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>

    3. 配置邮箱信息

    需要注意的是password不是邮箱登录密码,而是第一步中获取的授权码。

    spring:
      mail:
        default-encoding: utf-8
        # 主机地址
        host: smtp.126.com
        # 邮箱名
        username: xxx@126.com
        # 授权码(不是密码)
        password: xxxxxxxxxx

    4. 发送邮件

    封装SimpleMailMessage消息内容,注入JavaMailSender调用其send()方法,完成邮件发送。其中收件人和抄送人支持多个发送,多个地址用,拼接起来完成批量发送。

    @RestController
    public class Email {
    
        @Autowired
        private JavaMailSender mailSender;
        @GetMapping("send")
        private void send(){
    
            SimpleMailMessage message = new SimpleMailMessage();
            // 发件人
            message.setFrom("xxx@126.com");
            // 收件人
            message.setTo("xxx@163.com");
            // 邮件标题
            message.setSubject("Java发送邮件第二弹");
            // 邮件内容
            message.setText("你好,这是一条用于测试Spring Boot邮件发送功能的邮件!哈哈哈~~~");
            // 抄送人
            message.setCc("xxx@qq.com");
            mailSender.send(message);
        }
    }

    5. 发送效果

    最后一起来看看上面内容中涉及到的三个邮箱是否接收到数据了。

    发件人:

    收件人:

    抄送人:


    本文示例代码已上传至github,点个star支持一下!

    Spring Boot系列教程目录

    spring-boot-route(一)Controller接收参数的几种方式

    spring-boot-route(二)读取配置文件的几种方式

    spring-boot-route(三)实现多文件上传

    spring-boot-route(四)全局异常处理

    spring-boot-route(五)整合swagger生成接口文档

    spring-boot-route(六)整合JApiDocs生成接口文档

    spring-boot-route(七)整合jdbcTemplate操作数据库

    spring-boot-route(八)整合mybatis操作数据库

    spring-boot-route(九)整合JPA操作数据库

    spring-boot-route(十)多数据源切换

    spring-boot-route(十一)数据库配置信息加密

    spring-boot-route(十二)整合redis做为缓存

    spring-boot-route(十三)整合RabbitMQ

    spring-boot-route(十四)整合Kafka

    spring-boot-route(十五)整合RocketMQ

    spring-boot-route(十六)使用logback生产日志文件

    spring-boot-route(十七)使用aop记录操作日志

    spring-boot-route(十八)spring-boot-adtuator监控应用

    spring-boot-route(十九)spring-boot-admin监控服务

    spring-boot-route(二十)Spring Task实现简单定时任务

    spring-boot-route(二十一)quartz实现动态定时任务

    spring-boot-route(二十二)实现邮件发送功能

    spring-boot-route(二十三)开发微信公众号

    这个系列的文章都是工作中频繁用到的知识,学完这个系列,应付日常开发绰绰有余。如果还想了解其他内容,扫面下方二维码告诉我,我会进一步完善这个系列的文章!

    查看原文

    赞 4 收藏 3 评论 2

    Java旅途 发布了文章 · 10月21日

    spring-boot-route(二十一)quartz实现动态定时任务

    Quartz是一个定时任务的调度框架,涉及到的主要概念有以下几个:

    Scheduler:调度器,所有的调度都由它控制,所有的任务都由它管理。

    Job:任务,定义业务逻辑。

    JobDetail:基于Job,进一步封装。其中关联一个Job,并为Job指定更详细的信息。

    Trigger:触发器,可以指定给某个任务,指定任务的触发机制。

    一 创建简单任务

    1.1 Quartz依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>

    1.2 创建任务

    任务创建需要实现Job接口,重写execute(JobExecutionContext jobExecutionContext)方法,增加定时任务的业务逻辑,这里我只是简单的打印一下定时任务执行。

    @Slf4j
    public class SimpleJob implements Job {
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            log.info("job execute---"+new Date());
        }
    }

    1.3 JobDetail增加属性

    这里增加的属性可以在Job实现类中获取,来处理业务。

    JobDetail jobDetail = JobBuilder.newJob(SimpleJob.class)
                                    // 任务标识,及任务分组
                                    .withIdentity("job1", "group1")
                                    // 链接调用,增加需要的参数
                                    .usingJobData("name","Java旅途")
                                    .usingJobData("age",18)
                                    .build();

    1.4 Trigger实现

    Trigger分为两种,SimpleTriggerCronTriggerSimpleTrigger是根据Quartz的一些api实现的简单触发行为。CronTrigger用的比较多,使用cron表达式进行触发。这里先用SimpleTrigger来实现。

    SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
                                                .withIdentity("trigger1", "group1")
                                                // 立即执行
                                                .startNow()
                                                // 10s后停止
                                                .endAt(new Date(System.currentTimeMillis()+10*1000))
                                                .withSchedule(
                                                SimpleScheduleBuilder.simpleSchedule()
                                                // 每秒执行一次
                                                .withIntervalInSeconds(1)
                                                // 一直执行
                                                .repeatForever()
                                                ).build();

    1.5 启动任务

    @Autowired
    private Scheduler scheduler;
    
    scheduler.scheduleJob(jobDetail,simpleTrigger);

    1.6 执行效果

    启动项目后,任务立即执行,每秒执行一次,10s后停止,执行效果图如下:

    二 动态操作定时任务

    有时候除了已经开发好的定时任务外,还需要我们手动去创建任务并且控制任务的执行。

    2.1 创建任务

    @GetMapping("create")
    public void createJob(String jobName,String jobGroup,String cron,String triggerName,String triggerGroup) throws SchedulerException {
    
        JobKey jobKey = new JobKey(jobName,jobGroup);
        // 如果存在这个任务,则删除
        if(scheduler.checkExists(jobKey)) {
            scheduler.deleteJob(jobKey);
        }
    
        JobDetail jobDetail = JobBuilder.newJob(SimpleJob.class)
            .withIdentity(jobKey)
            .build();
    
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
    
        Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity(triggerName,triggerGroup)
            .withSchedule(cronScheduleBuilder).build();
        scheduler.scheduleJob(jobDetail,trigger);
    }

    2.2 暂停任务

    @GetMapping("pause")
    public void pauseJob(String jobName,String jobGroup) throws SchedulerException {
    
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if (jobDetail == null) {
            return;
        }
        scheduler.pauseJob(jobKey);
    }

    2.3 恢复暂停的任务

    @GetMapping("remuse")
    public void remuseJob(String jobName, String jobGroup) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if (jobDetail == null) {
            return;
        }
        scheduler.resumeJob(jobKey);
    }

    2.4 删除定时任务

    @GetMapping("remove")
    public void removeJob(String jobName, String jobGroup,String triggerName,String triggerGroup) throws SchedulerException {
    
        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        Trigger trigger =  scheduler.getTrigger(triggerKey);
        if (trigger == null) {
            return;
        }
        // 停止触发器
        scheduler.pauseTrigger(triggerKey);
        // 移除触发器
        scheduler.unscheduleJob(triggerKey);
        // 删除任务
        scheduler.deleteJob(jobKey);
    }

    三 任务持久化

    Quartz默认使用RAMJobStore存储方式将任务存储在内存中,除了这种方式还支持使用JDBC将任务存储在数据库,为了防止任务丢失,我们一般会将任务存储在数据库中。

    这里使用mysql进行存储,在quartz的源码包中找到文件tables_mysql_innodb.sql,然后在客户端进行运行sql文件。如果嫌源码包不好下载的话,我已经将sql文件上传至GitHub了,可以直接访问github拉去表结构,数据表如下:

    3.1 增加mysql和jdbc依赖

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.11</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    3.2 指定使用jdbc存储

    quartz默认使用memory存储,这里修改成jdbc进行存储,并配置jdbc的相关信息

    spring:
      quartz:
        job-store-type: jdbc
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/simple_fast
        username: root
        password: root

    3.3 创建任务

    启动项目,调用create接口创建任务,然后数据表中就会新增任务相关的数据了。


    本文示例代码已上传至github,点个star支持一下!

    Spring Boot系列教程目录

    spring-boot-route(一)Controller接收参数的几种方式

    spring-boot-route(二)读取配置文件的几种方式

    spring-boot-route(三)实现多文件上传

    spring-boot-route(四)全局异常处理

    spring-boot-route(五)整合swagger生成接口文档

    spring-boot-route(六)整合JApiDocs生成接口文档

    spring-boot-route(七)整合jdbcTemplate操作数据库

    spring-boot-route(八)整合mybatis操作数据库

    spring-boot-route(九)整合JPA操作数据库

    spring-boot-route(十)多数据源切换

    spring-boot-route(十一)数据库配置信息加密

    spring-boot-route(十二)整合redis做为缓存

    spring-boot-route(十三)整合RabbitMQ

    spring-boot-route(十四)整合Kafka

    spring-boot-route(十五)整合RocketMQ

    spring-boot-route(十六)使用logback生产日志文件

    spring-boot-route(十七)使用aop记录操作日志

    spring-boot-route(十八)spring-boot-adtuator监控应用

    spring-boot-route(十九)spring-boot-admin监控服务

    spring-boot-route(二十)Spring Task实现简单定时任务

    spring-boot-route(二十一)quartz实现动态定时任务

    spring-boot-route(二十二)实现邮件发送功能

    spring-boot-route(二十三)开发微信公众号

    这个系列的文章都是工作中频繁用到的知识,学完这个系列,应付日常开发绰绰有余。如果还想了解其他内容,扫面下方二维码告诉我,我会进一步完善这个系列的文章!

    查看原文

    赞 8 收藏 7 评论 0

    Java旅途 发布了文章 · 10月20日

    spring-boot-route(二十)Spring Task实现简单定时任务

    Spring Task是Spring 3.0自带的定时任务,可以将它看作成一个轻量级的Quartz,功能虽然没有Quartz那样强大,但是使用起来非常简单,无需增加额外的依赖,可直接上手使用。

    一 如何实现定时任务

    1. 开启定时任务

    在启动类上增加@EnableScheduling注解,开启定时任务

    2. 定时任务方法

    使用@Scheduled注解的方法就会被声明成一个定时任务

    @Slf4j
    @Component
    public class ScheduledTest {
    
        @Scheduled(cron = "*/1 * * * * ?")
        public void test1(){
            log.info("这个定时任务1----");
        }
        @Scheduled(cron = "*/1 * * * * ?")
        public void test2(){
            log.info("这个定时任务2----");
        }
        @Scheduled(cron = "*/1 * * * * ?")
        public void test3(){
            log.info("这个定时任务3----");
        }
    }

    3. 注册bean

    使用@Component将这个定时任务类注册成一个bean组件,交给Spring容器管理。

    二 cron表达式详解

    在spring 4.x中已经不支持7个参数的cronin表达式了,要求必须是6个参数。cron表达式格式如下:

    {秒} {分} {时} {日期(具体哪天)} {月} {星期}
    • 秒:必填项,允许的值范围是0-59,支持的特殊符号包括,-*/,表示特定的某一秒才会触发任务,-表示一段时间内会触发任务,*表示每一秒都会触发,/表示从哪一个时刻开始,每隔多长时间触发一次任务。
    • 分:必填项,允许的值范围是0-59,支持的特殊符号和秒一样,含义类推
    • 时:必填项,允许的值范围是0-23,支持的特殊符号和秒一样,含义类推
    • 日期:必填项,允许的值范围是1-31,支持的特殊符号相比秒多了?,表示与{星期}互斥,即意味着若明确指定{星期}触发,则表示{日期}无意义,以免引起冲突和混乱。
    • 月:必填项,允许的值范围是1-12(JAN-DEC),支持的特殊符号与秒一样,含义类推
    • 星期:必填项,允许值范围是1~7 (SUN-SAT),1代表星期天(一星期的第一天),以此类推,7代表星期六,支持的符号相比秒多了?,表达的含义是与{日期}互斥,即意味着若明确指定{日期}触发,则表示{星期}无意义。

    如果对cron表达式不是很熟悉,可以网上找一下,有许多生产cron表达式的网站。Spring Task就简单介绍到这了,使用起来非常方便,下一章将介绍Quartz的使用及将任务持久化存储。


    本文示例代码已上传至github,点个star支持一下!

    Spring Boot系列教程目录

    spring-boot-route(一)Controller接收参数的几种方式

    spring-boot-route(二)读取配置文件的几种方式

    spring-boot-route(三)实现多文件上传

    spring-boot-route(四)全局异常处理

    spring-boot-route(五)整合swagger生成接口文档

    spring-boot-route(六)整合JApiDocs生成接口文档

    spring-boot-route(七)整合jdbcTemplate操作数据库

    spring-boot-route(八)整合mybatis操作数据库

    spring-boot-route(九)整合JPA操作数据库

    spring-boot-route(十)多数据源切换

    spring-boot-route(十一)数据库配置信息加密

    spring-boot-route(十二)整合redis做为缓存

    spring-boot-route(十三)整合RabbitMQ

    spring-boot-route(十四)整合Kafka

    spring-boot-route(十五)整合RocketMQ

    spring-boot-route(十六)使用logback生产日志文件

    spring-boot-route(十七)使用aop记录操作日志

    spring-boot-route(十八)spring-boot-adtuator监控应用

    spring-boot-route(十九)spring-boot-admin监控服务

    spring-boot-route(二十)Spring Task实现简单定时任务

    spring-boot-route(二十一)quartz实现动态定时任务

    spring-boot-route(二十二)实现邮件发送功能

    spring-boot-route(二十三)开发微信公众号

    这个系列的文章都是工作中频繁用到的知识,学完这个系列,应付日常开发绰绰有余。如果还想了解其他内容,扫面下方二维码告诉我,我会进一步完善这个系列的文章!

    查看原文

    赞 2 收藏 2 评论 0

    认证与成就

    • 获得 290 次点赞
    • 获得 6 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 6 枚铜徽章

    擅长技能
    编辑

    开源项目 & 著作
    编辑

    注册于 2019-09-29
    个人主页被 8k 人浏览

    bt365体育投注