SpringBoot2 应用使用

692 阅读18分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战

SpringBoot2 应用使用

在SpringBoot中集成MyBatis

在这里插入图片描述

本篇将讲述如何使用SpringBoot集成MyBatis访问 MySQL数据库

Idea 创建一个普通Maven项目 SpringBootYYBJ 应用笔记

导入POM依赖

pom.xml

 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 ​
     <groupId>org.example</groupId>
     <artifactId>SpringBootYYBJ</artifactId>
     <version>1.0-SNAPSHOT</version>
 ​
     <!-- 集成一个父项目 -->
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>2.1.6.RELEASE</version>
     </parent>
 ​
     <dependencies>
         <!-- SpringBoot starter-web依赖-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
 ​
         <!--   mysql数据库-->
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>5.1.48</version>
         </dependency>
         <!--        FastJSON (可选)-->
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
             <version>1.2.31</version>
         </dependency>
         <!--        MyBatis整合所需-->
         <dependency>
             <groupId>org.mybatis.spring.boot</groupId>
             <artifactId>mybatis-spring-boot-starter</artifactId>
             <version>1.1.1</version>
         </dependency>
     </dependencies>
 ​
 </project>

配置yml

SpringBoot 对一些配置都是有默认值的但也可以直接通过 application.propertiesapplication.yml 直接修改 application.yml

 server:
   port: 9090    #SpringBoot 端口
 
 spring:
   datasource:   #数据源配置: 驱动,url,用户,密码  (注意数据库名啥的根据自己需求来更改!)
     driver-class-name: com.mysql.jdbc.Driver
     url: jdbc:mysql://localhost:3306/shipms?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
     username: root
     password: ok
 #引入Mybatis的sql映射: /*.xml 加载包下所有..
 mybatis:
   mapper-locations: classpath:mapper/*.xml
 logging:
   level:
     com.zb.mapper: DEBUG  #sql的执行输出sql语句

创建正常项目所需的包: entity service mapper controller 以及需要的工具组件包 util config... (根据开发习惯 包而已自定义即可~) 至此,基本配置已完成 :

  • 根据需要编写即可, 和以前的SpringMVC 应用程序无太大差异!
  • 而且还不用写前端了, 这里只需要写 Controller控制器, 返回前端JSON即可...
  • 下面本人实现一个查全部操作..

数据库 shipms

在这里插入图片描述

创建实体entity

创建Mapper

UserMapper.Java

 @Mapper
 //表示当前 Mapper(就是以前的Dao)被Spring管理,
 //相对于以前的Spring的: Dao接口 dao = session.getMapper(Dao接口.class);
 public interface UserMapper {
     //查询全部~
     public List<Users> selall();
 }

编写Mybstais sql映射

UserMapper.xml

 <?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 <mapper namespace="com.wsm.mapper.UserMapper">
     <!-- 查询全部 -->
     <select id="selall" resultType="com.wsm.entity.Users"  >
         SELECT * FROM `users`
     </select>
 </mapper>

创建service

UserService.Java

 @Service     //类交给Spring容器管理,且表示业务逻辑
 public class UserService {
 ​
     @Autowired  //或 @Autowired(required = false) 有时候idea工具会检查错误 (Spring上下文没有类),可以使用,不要检查;
     private UserMapper um;
 ​
     //查询全部~
     public List<Users> selall(){
         return um.selall();
     }
 }

创建controller包

UserController.Java

 @CrossOrigin        //表示支持跨域操作: 因为SpringBoot是 前后分离,所以会设计跨服务器的操作(两个项目之间之间访问)..加上即可;
 @RestController     //声明在方法上表示,该类下所有方法返回值,都以JSON返回;
 public class UserController {
     @Autowired
     private UserService us;
 ​
     @GetMapping("/selall")   //相当于以前SpringMVC的 @RequestMapping get方式请求~
     public List<Users> selall(){
         return us.selall();
     }
 }
 ​

别忘了主程序类:

SpringBootRun.Java

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

在这里插入图片描述

跨域:

编写一个前端项目: 通过: http:// 跨域... index.html

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Document</title>
     <script src="../jquery-1.12.4.js"></script>
     <script type="text/javascript" >
         //页面加载事件:
         $(function(){
             page();
         })//页面加载事件结束!
         
         function page(){
         
             //执行异步!
             $.ajax({
                 url:'http://localhost:9090/selall',
                 type:'get',         
                 success:function(result){
                     var html='';    //事先定义一个用于存,等会儿拼接的变量!
                     for(var i=0;i<result.length;i++){       //遍历JSON 要知道JSON是一个Page对象 .其data属性.length 遍历其属性;
                         var r = result[i];                  
                         html+="<tr>"+
                                 "<td>"+r.id+"</td>"+
                                 "<td>"+r.name+"</td>"+
                                 "<td>"+r.status+"</td>"+
                                 "<td>"+r.typeid+"</td>"+
                                 "<td>"+r.lastModifyTime+"</td>"+
                                 
                             "</tr>";
                     }
                     $("tbody").html(html);
                 }
             })
         }
     </script>
 </head>
 <body>
     <!-- table展示数据 -->
     <div>
     
     <table width="100%"  border="1" >
         <thead>
             <tr>
                 <td>编号</td>
                 <td>用户名</td>
                 <td>用户类型</td>
                 <td>用户状态</td>
                 <td>最后修改时间</td>
             </tr>
         </thead>
         <tbody>
             <!-- 内容来源于Ajax~-->
         </tbody>
     </table>
     </div>
 </body>
 </html>

在这里插入图片描述

在SpringBoot中集成Redis

接着上一个项目执行...

导入Redis依赖

pom.xml

 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-redis</artifactId>
      <version>1.4.5.RELEASE</version>
 </dependency>

配置yml 基础上增加

application.yml

 spring:
   datasource:
     driver-class-name: com.mysql.jdbc.Driver
     url: jdbc:mysql://localhost:3306/shipms?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
     username: root
     password: ok
 #redis配置是在Spring目录下的..而且我的redis没有密码,根据你自己的情况来~ (本人无密码)
 #Spring  
   redis:      
     host: 127.0.0.1
     port: 6379

yml 文件以树状结构区分...

创建RestConfig

加一个包com.wsm.config 配置类包 RedisConfig.Java

package com.wsm.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * redis配置类
 * @program: springbootdemo
 * @Description:
 */
@Configuration
@EnableCaching //开启注解
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * retemplate相关配置
     * @param factory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);

        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 值采用json序列化
        template.setValueSerializer(jacksonSeial);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());

        // 设置hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();

        return template;
    }

    /**
     * 对hash类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    /**
     * 对redis字符串类型数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    /**
     * 对链表类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    /**
     * 对无序集合类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    /**
     * 对有序集合类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}

在Service层使用

首先要确保实体类 已经序列化。实现 Serializable接口 使用上面的Config可不用序列化

使用Reids 前要确保 Redis服务启动了....

UserService.Java

 @Service     //类交给Spring容器管理,且表示业务逻辑
 public class UserService {
 ​
     @Autowired  //或 @Autowired(required = false) 有时候idea工具会检查错误 (Spring上下文没有类),可以使用,不要检查;
     private UserMapper um;
 ​
     @Autowired
     private RedisTemplate<String, Object> redisTemplate;
 ​
     //查询全部~
     //redis: 数据库查询一次存入redis 下次访问就不从数据库访问,减少数据库访问连接...
     public List<Users> selall(){
         System.out.println("进入业务查询方法");
         List<Users> usersList = null;
         String key = "Users";               //redis存储的key
         if (redisTemplate.hasKey(key)) {    //是否存在键
             System.out.println("redis查询");
             String val = redisTemplate.boundValueOps(key).get().toString();
             usersList = JSON.parseArray(val, Users.class);
         } else {
             System.out.println("db数据库查询");
             usersList = um.selall();
             redisTemplate.boundValueOps(key).set(JSON.toJSONString(usersList));
         }
         System.out.println("离开业务查询方法");
         return usersList;
     }
 }

主程序执行刷新两次~

在这里插入图片描述 SpringBoot 使用Reids大致如此... 深入Redis还需努力学习...

Redis 工具类:

  • 上面已经介绍了Redis 的基本使用,但对于RedisTemplate内部封装的方法使用有点不舒服的
  • 工具类可以更加方便快捷的提高开发效率!,内部对RedisTemplate 进行了二次封装!可以尝试使用!
  • 一般建议这种工具类,存放在一个指定的 util包方便管理!

RedisUtil.Java

package com.zb.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * redisTemplate封装
 *
 */
@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public RedisUtil(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * @return
     */
    public boolean expire(String key,long time){
        try {
            if(time>0){
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key){
        return redisTemplate.getExpire(key,TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key){
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String ... key){
        if(key!=null&&key.length>0){
            if(key.length==1){
                redisTemplate.delete(key[0]);
            }else{
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    //============================String=============================
    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key){
        return key==null?null:redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key,Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     * @param key 键
     * @param value 值
     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key,Object value,long time){
        try {
            if(time>0){
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            }else{
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     * @param key 键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta){
        if(delta<0){
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     * @param key 键
     * @param delta 要减少几(小于0)
     * @return
     */
    public long decr(String key, long delta){
        if(delta<0){
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }
    
    //================================Map=================================
    /**
     * HashGet
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hget(String key,String item){
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object,Object> hmget(String key){
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map<String,Object> map){
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * HashSet 并设置时间
     * @param key 键
     * @param map 对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String,Object> map, long time){
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if(time>0){
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key,String item,Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @param time 时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key,String item,Object value,long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if(time>0){
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的值
     * @param key 键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item){
        redisTemplate.opsForHash().delete(key,item);
    }

    /**
     * 判断hash表中是否有该项的值
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item){
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     * @param key 键
     * @param item 项
     * @param by 要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item,double by){
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     * @param key 键
     * @param item 项
     * @param by 要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item,double by){
        return redisTemplate.opsForHash().increment(key, item,-by);
    }

    //============================set=============================
    /**
     * 根据key获取Set中的所有值
     * @param key 键
     * @return
     */
    public Set<Object> sGet(String key){
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     * @param key 键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key,Object value){
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     * @param key 键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object...values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     * @param key 键
     * @param time 时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key,long time,Object...values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if(time>0) {
                expire(key, time);
            }
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     * @param key 键
     * @return
     */
    public long sGetSetSize(String key){
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     * @param key 键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(String key, Object ...values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    //===============================list=================================

    /**
     * 获取list缓存的内容
     * @param key 键
     * @param start 开始
     * @param end 结束  0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end){
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     * @param key 键
     * @return
     */
    public long lGetListSize(String key){
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     * @param key 键
     * @param index 索引  index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return
     */
    public Object lGetIndex(String key,long index){
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     * @param key 键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index,Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N个值为value
     * @param key 键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lRemove(String key,long count,Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 模糊查询获取key值
     * @param pattern
     * @return
     */
    public Set keys(String pattern){
        return redisTemplate.keys(pattern);
    }

    /**
     * 使用Redis的消息队列
     * @param channel
     * @param message 消息内容
     */
    public void convertAndSend(String channel, Object message){
        redisTemplate.convertAndSend(channel,message);
    }
    
    //=========BoundListOperations 用法 start============

    /**
     *将数据添加到Redis的list中(从右边添加)
     * @param listKey
     * @param expireEnum 有效期的枚举类
     * @param values 待添加的数据
     */
    public void addToListRight(String listKey, Status.ExpireEnum expireEnum, Object... values) {
        //绑定操作
        BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
        //插入数据
        boundValueOperations.rightPushAll(values);
        //设置过期时间
        boundValueOperations.expire(expireEnum.getTime(),expireEnum.getTimeUnit());
    }
    /**
     * 根据起始结束序号遍历Redis中的list
     * @param listKey
     * @param start  起始序号
     * @param end  结束序号
     * @return
     */
    public List<Object> rangeList(String listKey, long start, long end) {
        //绑定操作
        BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
        //查询数据
        return boundValueOperations.range(start, end);
    }
    /**
     * 弹出右边的值 --- 并且移除这个值
     * @param listKey
     */
    public Object rifhtPop(String listKey){
        //绑定操作
        BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
        return boundValueOperations.rightPop();
    }
    //=========BoundListOperations 用法 End============
}

SpringBoot多线程

在这里插入图片描述

在处理大数据或实时数据时,如果在主线程频繁创建大量对象, 这些对象使用完后成为游离对象,不会立即被GC (垃圾回收~) 当创建速度大于销毁速度时,可能导致内存持续上涨,最后内存溢出。 可以开启多线程来处理,线程内的对象会在执行结束后尽快的销毁, 均分内存累加的负担,保证内存占用的稳定性。

配置@EnableAsync

首先定义Config类,实现AsyncConfigurer接口。 接下来去实现它的两个方法 CustomMultiThreadingConfig.Java

 /**
  * 线程配置类
  */
 @Configuration
 @EnableAsync(proxyTargetClass = true)//利用@EnableAsync注解开启异步任务支持
 @ComponentScan("cn.hello")           //扫描包
 public class CustomMultiThreadingConfig implements AsyncConfigurer {
     @Override
     public Executor getAsyncExecutor() {
         ThreadPoolTaskExecutor taskExecutort = new ThreadPoolTaskExecutor();
         taskExecutort.setCorePoolSize(10);          //线程池初试大小
         taskExecutort.setMaxPoolSize(50);           //线程池最大连接数
         taskExecutort.setQueueCapacity(500);        //消息队列
         taskExecutort.setWaitForTasksToCompleteOnShutdown(true);//等待时休眠
         taskExecutort.setBeanName("my-thrad:");     //线程名
         taskExecutort.initialize();                 //初始化
         return taskExecutort;
     }
 ​
     @Override
     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
         return new SimpleAsyncUncaughtExceptionHandler();
     }
 }

当提交的任务个数大于QueueCapacity, 就需要设置该参数,但spring提供的都不太满足业务场景,可以自定义一个,也可以注意不要超过QueueCapacity即可

配置@Asyn

首先创建一个listener的包方便管理

  • @Asyn 声明在方法/类上 修饰类时表示类里所有方法都是多线程异步执行..
  • 主线程调用方法之后, SpringBoot底层会默认开启一个线程来执行方法.. 就很强!!

这里定义一个场景就是:

  • 查询全部方法之后, 10 秒后 多线程把Redis的key 给删了.. MyAsynclmp.java
 @Component      //表示一个类被Spring管理;
 public class MyAsynclmp {
 ​
     @Autowired //获取一个redis对象
     private RedisTemplate<String, Object> redisTemplate;
 ​
     @Async     //表示该方法多线程执行...
     public void clearData(String key) {
         System.out.println("进入异步方法准备清除数据");
         try {
             Thread.sleep(10000);
             redisTemplate.delete(key);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         System.out.println("异步方法执行完毕!");
     }
 }

Service 实现

UserService.Java

 @Service     //类交给Spring容器管理,且表示业务逻辑
 public class UserService {
 ​
     @Autowired  //或 @Autowired(required = false) 有时候idea工具会检查错误 (Spring上下文没有类),可以使用,不要检查;
     private UserMapper um;
     @Autowired
     private RedisTemplate<String, Object> redisTemplate;
     @Autowired
     private MyAsynclmp myAsynclmp;
 ​
     //查询全部~
     //redis: 数据库查询一次存入redis 下次访问就不从数据库访问,减少数据库访问连接...
     public List<Users> selall(){
         System.out.println("进入业务查询方法");
         List<Users> usersList = null;
         String key = "Users";               //redis存储的key
         if (redisTemplate.hasKey(key)) {    //是否存在键
             System.out.println("redis查询");
             String val = redisTemplate.boundValueOps(key).get().toString();
             usersList = JSON.parseArray(val, Users.class);
         } else {
             System.out.println("db数据库查询");
             usersList = um.selall();
             redisTemplate.boundValueOps(key).set(JSON.toJSONString(usersList));
         }
         
         //执行多线程
         myAsynclmp.clearData("Users");
         
         System.out.println("离开业务查询方法");
         return usersList;
     }
 }

主程序执行:

在这里插入图片描述 注意: 调用方法不能和异步方法在同一类里

SpringBoot使用JVM缓存 @EhCache

通常在获取数据的时候,为了高效率我们常会从数据库中提取常用数据存放到redis缓存中 这样来常用数据存在redis中就可以不需要频繁的访问数据库了。 那么这还不够,我们还可以将数据存放在Java虚拟机中,更加高效。

  • EhCache是一个纯Java的进程内缓存框架,具有快速、精干等特点,
  • Hibernate中的默认Cache就是使用的EhCache。

使用EhCache作为缓存,我们先引入相关依赖。

pom.xml

 <!--ehcache依赖-->
 <dependency>
   <groupId>net.sf.ehcache</groupId>
   <artifactId>ehcache</artifactId>
 </dependency>
 <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-cache</artifactId>
 </dependency>

然后创建EhCache的配置文件ehcache.xml

ehcache.xml

 <?xml version="1.0" encoding="UTF-8"?>
 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false">
      
     <!--
     磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存
     path:指定在硬盘上存储对象的路径
     path可以配置的目录有:
     user.home(用户的家目录)
     user.dir(用户当前的工作目录)
     java.io.tmpdir(默认的临时目录)
     ehcache.disk.store.dir(ehcache的配置目录)
     绝对路径(如:d:\ehcache)
     查看路径方法:String tmpDir = System.getProperty("java.io.tmpdir");
    -->
       
     <diskStore path="java.io.tmpdir"/>
     <!--
     defaultCache:默认的缓存配置信息,如果不加特殊说明,则所有对象按照此配置项处理
     maxElementsInMemory:设置了缓存的上限,最多存储多少个记录对象
     eternal:代表对象是否永不过期 (指定true则下面两项配置需为0无限期)
     timeToIdleSeconds:最大的发呆时间 /秒
     timeToLiveSeconds:最大的存活时间 /秒
     overflowToDisk:是否允许对象被写入到磁盘
     说明:下列配置自缓存建立起600秒(10分钟)有效 。
     在有效的600秒(10分钟)内,如果连续120秒(2分钟)未访问缓存,则缓存失效。
     就算有访问,也只会存活600秒。
    -->
       
     <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="600"
                   timeToLiveSeconds="600" overflowToDisk="true"/>
      
       
     <cache name="mycache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120"
            timeToLiveSeconds="600" overflowToDisk="true"/>
      <!--
         自定义缓存配置:
             以上配置自缓存建立起600秒(10分钟)有效 。
             在有效的600秒(10分钟)内,如果连续120秒(2分钟)未访问缓存,则缓存失效。就算有访问,也只会存活600秒。
     --> 
 </ehcache>

然后SpringBoot配置文件中,指明缓存类型并声明Ehcache配置文件的位置。

application.yml

 #数据源配置: 驱动,url,用户,密码  (注意数据库名啥的根据自己需求来更改!)
 spring:
   datasource:
     driver-class-name: com.mysql.jdbc.Driver
     url: jdbc:mysql://localhost:3306/shipms?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
     username: root
     password: ok
 #redis配置是在Spring目录下的..而且我的redis没有密码,根据你自己的情况来~
 #Spring
   redis:
     host: 127.0.0.1
     port: 6379
 #声明Ehcache配置文件的位置
 #Spring
   cache:
     type: ehcache
     ehcache:
       config: classpath:ehcache.xml

Service上注解

为了方便说明添加一条新功能根据id查询一个用户 UserMapper.Java

 @Mapper
 //表示当前 Mapper(就是以前的Dao)被Spring管理,
 //相对于以前的Spring的: Dao接口 dao = session.getMapper(Dao接口.class);
 public interface UserMapper {
     //查询全部~
     public List<Users> selall();
     //根据id查看用户
     public Users selid(int id);
 }

UserMapper.xml 增加

 <select id="selid"  resultType="com.wsm.entity.Users"  parameterType="int" >
     SELECT * FROM `users` where id=#{id}
 </select>

UserService.Java 增加

 /*
     selid方法的作用是: 根据id查询一个用户;
     @Cacheable的作用是: 声明在方法/类上  类下所有方法/方法的返回值都会添加到 JVM内存中去。
         value: 用于指定缓存存储的集合名。
         key: 非必需,缺省按照函数的所有参数组合作为key值(默认值), 可通过 #参数名 或参数值;
         方法被调用时候会根据key去内存中查找有没有对应的数据,
         如果没有就执行方法,并将方法返回值添加至内存中;
         如果有就直接根据 key 从内存中拿!!
     所以方法 key="'user:'+#id"
         当查询 1号记录时候,key=user1        2号记录时候,key=user2
         底层在此查1号记录,直接根据 key就可以去内存中查找而不会拿错!! 访问2拿到1的数据!!
 * */
 @Cacheable(value = "mycache", key = "'user:'+#id")
 public Users selid(int id){
     String key = "user";
     Users user = null;
     if(redisTemplate.hasKey(key)){
         System.out.println("redis中查找");
         user = (Users) redisTemplate.opsForHash().get(key, key+":"+id);
         if(user==null){
             user = this.seluser(key, id);
         }
     }else {
         System.out.println("db中查找");
         user = this.seluser(key, id);
     }
     return user;
 }
​
 //redis 使用map结构存储——为了方便操作定义一个专门数据库存储key 的方法()
 public Users seluser(String key,int id){
     Users  user = um.selid(id);
     HashMap<String, Users> map = new HashMap<String, Users>();
     map.put(key+":"+id, user);
     redisTemplate.opsForHash().putAll(key, map);
     redisTemplate.expire(key, 1000, TimeUnit.SECONDS);
     return user;
 }
 ​

UserController.Java 增加控制方法();

     @GetMapping("/selid")
     public Users selid(int id){
         return us.selid(id);
     };

主程序上 @EnableCaching开启Spring Cache缓存功能。

SpringBootRun.Java

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

在这里插入图片描述 程序执行第一次回从数据库查并添加到 reids 内存中; 下次如果查看相同数据直接根据 key 从内存中获取即可!! 如果程序关闭! 内存数据消失...下次开机执行会从 redis本地中获取数据!! 在存入内存中 方便下次访问...

Cache注解详解

  • @CacheConfig: 主要用于配置该类中会用到的一些共用的缓存配置。 @CacheConfig(cacheNames = "users"):配置了该数据访问对象中返回的内容将存储于名为users的缓存对象中. 我们也可以不使用该注解,直接通过@Cacheable自己配置缓存集的名字来定义。
  • @Cacheable: 声明在方法/类上 类下所有方法/方法的返回值都会添加到 JVM内存中去。 value、cacheNames:两个等同的参数 cacheNames为Spring 4新增,作为value的别名, 用于指定缓存存储的集合名。 由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了 key: 缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值; 若自己配置需使用SpEL表达式 @Cacheable(key = "#参数名") condition: 缓存对象的条件,非必需,也需使用SpEL表达式 只有满足表达式条件的内容才会被缓存,比如: @Cacheable(key = "#p0", condition = "#p0.length() < 3") 表示只有当第一个参数的长度小于3的时候才会被缓存, 若做此配置上面的AAA用户就不会被缓存. 读者可自行实验尝试。 ..
  • @CachePut: 它的参数与@Cacheable类似 它与@Cacheable不同的是,它每次都会真是调用函数,所以主要用于数据新增和修改操作上。 数据库都调用方法修改了数据, 内存中的数据当然也要更改了!!别忘了更新redsi哦~
  • @CacheEvict 配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。 数据库调用了删除方法,数据库中都不存在数据了, 内存中也没必要留着了吧,, 根据key删除内存中的缓存~

Springboot使用slf4j记录日志

环境准备

在项目开发中,记录日志是必做的一件事情。而当我们使用Springboot框架时,记录日志就变得极其简单了。 springboot内置了slf4j日志框架,我们只需要添加依赖,做一些简单的配置就ok了。

  • 需要注意的是,Lombok这个依赖需要安装插件,我们直接可以在线安装:
  • idea工具: 打开File -- setting -- Plugins
  • 然后直接搜Lombok就可以安装了。
  • 在这里插入图片描述 关于lombok还有很多别的功能 比如使用 @Data注解 可以为一个bean自动生成getter , setter方法及toString 方法

依赖

pom.xml 引入 lombok的依赖

 <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
 </dependency>

yml文件中配置日志级别:info

 #配置日志级别:info
 logging:
   level:
     root: info

或在 application.properties配置日志显示级别 debug

 logging.level.root=debug

测试

修改controller UserController.Java

 @Slf4j              //lombok注解,允许日志!!
 @CrossOrigin        //表示支持跨域操作: 因为SpringBoot是 前后分离,所以会设计跨服务器的操作(两个项目之间之间访问)..加上即可;
 @RestController     //声明在方法上表示,该类下所有方法返回值,都以JSON返回;
 public class UserController {
     @Autowired
     private UserService us;
 ​
     @GetMapping("/selall")   //相当于以前SpringMVC的 @RequestMapping get方式请求~
     public List<Users> selall(){
         return us.selall();
     }
 ​
     @GetMapping("/selid")
     public Users selid(int id){
         //接下来就可以使用日志了...
         log.info("#########  info  #########");
         log.debug("#########  debug  #########");
         log.error("#########  error  #########");
 ​
         return us.selid(id);
     };
 }
 ​

加入@Slf4j的注解。此时,你会发现在该类中你可以直接使用一个log对象。这就证明你的插件和依赖起作用了。 在这里插入图片描述

Lombok 还包含了很多其它的注解..

@NonNull

这个注解可以用在成员方法或者构造方法的参数前面, 会自动产生一个关于此参数的非空检查,如果参数为空,则抛出一个空指针异常

 //成员方法参数加上@NonNull注解
 public String getName(@NonNull Person p){
     return p.getName();
 }

实际效果相当于:

 public String getName(@NonNull Person p){
     if(p==null){
         throw new NullPointerException("person");
     }
     return p.getName();
 }

用在构造方法的参数上效果类似,就不再举例子了。

@Getter/@Setter

这一对注解从名字上就很好理解,用在成员变量前面,相当于为成员变量生成对应的get和set方法, 同时可以,为生成的方法指定访问修饰符,当然默认为public,直接来看下面的简单的例子:

 public class Programmer{
     @Getter
     @Setter
     private String name;
     @Setter(AccessLevel.PROTECTED)
     private int age;
     @Getter(AccessLevel.PUBLIC)
     private String language;
 }

实际效果相当于:

 public class Programmer{
     private String name;
     private int age;
     private String language;
 ​
     public void setName(String name){
         this.name = name;
     }
 ​
     public String getName(){
         return name;
     }
 ​
     protected void setAge(int age){
         this.age = age;
     }
 ​
     public String getLanguage(){
         return language;
     }
 }

这两个注解还可以直接用在类上,可以为此类里的所有非静态成员变量生成对应的get和set方法。

@ToString/@EqualsAndHashCode

这两个注解也比较好理解,就是生成toString,equals和hashcode方法 同时后者还会生成一个canEqual方法,用于判断某个对象是否是当前类的实例 生成方法时只会使用类中的非静态和非transient成员变量

@NoArgsConstructor/@RequiredArgsConstructor /@AllArgsConstructor

这三个注解都是用在类上的

  • 第一个和第三个都很好理解,就是为该类产生无参的构造方法包含所有参数的构造方法
  • 第二个注解则使用类中所有带有@NonNull注解的或者带有final修饰的成员变量生成对应的构造方法
  • 当然,和前面几个注解一样,成员变量都是非静态的, 另外,如果类中含有final修饰的成员变量,是无法使用@NoArgsConstructor注解的。

三个注解都可以指定生成的构造方法的访问权限,同时,第二个注解还可以用

  • @RequiredArgsConstructor(staticName=”methodName”)
  • 的形式生成一个指定名称的静态方法,返回一个调用相应的构造方法产生的对象

@Data/@Value

@Data

  • @Data注解综合了
  • @Getter/@Setter @ToString @EqualsAndHashCode @RequiredArgsConstructor注解
  • 其中@RequiredArgsConstructor使用了类中的带有@NonNull注解的或者final修饰的成员变量
  • 它可以使用@Data(staticConstructor=”methodName”)来生成一个静态方法, 返回一个调用相应的构造方法产生的对象。

@Value

  • @Value注解和@Data类似
  • 区别在于它会把所有成员变量默认定义为private final修饰,并且不会生成set方法。

@.....更多...