Java SpringBoot使用Redis缓存和Ehcache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?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">
    <!--
       该文件存放在resources目录下  application.properties文件添加spring.cache.ehcache.config=classpath:/ehcache.xml
       diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
       user.home – 用户主目录
       user.dir  – 用户当前工作目录
       java.io.tmpdir – 默认临时文件路径
     -->
    <diskStore path="java.io.tmpdir/Tmp_EhCache"/>
    <!--
       defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
    <!--
      name:缓存名称。
      maxElementsInMemory:缓存最大数目
      maxElementsOnDisk:硬盘最大缓存个数。
      eternal:对象是否永久有效,一但设置了,timeout将不起作用。
      overflowToDisk:是否保存到磁盘,当系统当机时
      timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
      timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
      diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
      diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
      diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
      memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
      clearOnFlush:内存数量最大时是否清除。
      memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
      FIFO,first in first out,这个是大家最熟的,先进先出。
      LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
      LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
   -->
    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>
 
    <cache
            name="product"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
 
</ehcache>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package ehcache.demo.controller;
 
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import ehcache.demo.entity.Product;
import ehcache.demo.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
 
import javax.sound.midi.Soundbank;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@Controller
public class ProductController {
    @Autowired
    private ProductService productService;
 
    /**
     * 获取所有商品 使用redis缓存
     *
     * @return
     */
    @SuppressWarnings("unchecked")
    @RequestMapping("getall")
    @ResponseBody
    public Object GetAllProduct() {
 
        /**************** 高并发环境下的缓存穿透测试 **********************/
        /*
        // 线程,该线程调用底层的查询所有商品方法
        Runnable runnable = new Runnable() {
            public void run() {
                productService.getAll();
            }
        };
 
        // 模拟多线程测试高并发穿透问题
        ExecutorService executorService = Executors.newFixedThreadPool(6);
        for (int i = 0; i < 1000; i++) {
            executorService.submit(runnable);
        }
        */
 
        return productService.getAll();
    }
 
    /**
     * 获取分页商品
     *
     * @return
     */
    @RequestMapping("getpage")
    @ResponseBody
    public Object GetPage() {
        // 分页查询
        PageHelper.startPage(1, 5);
        List<Product> listProducts = productService.getAll();
        PageInfo<Product> productPageInfo = new PageInfo(listProducts);
        return productPageInfo;
    }
 
    /**
     * 获取单个商品 使用ehcache
     *
     * @param id
     * @return
     */
    @RequestMapping("getone/{id}")
    @ResponseBody
    public Object GetProduct(@PathVariable int id) {
 
        Product product = productService.selectByPrimaryKey(id);
        System.out.println("第一次查询" + product.toString());
        product = productService.selectByPrimaryKey(id);
        System.out.println("第二次查询" + product.toString());
        product = productService.selectByPrimaryKey(id);
        System.out.println("第三次查询" + product.toString());
        product = productService.selectByPrimaryKey(id);
        System.out.println("第四次查询" + product.toString());
        return product;
    }
 
 
    /**
     * 返回json测试
     *
     * @return
     */
    @RequestMapping("json")
    @ResponseBody
    private Object json() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("name", "测试姓名");
        map.put("address", "深圳市南山区");
        return map;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package ehcache.demo.service;
 
import ehcache.demo.entity.Product;
 
import java.util.List;
 
public interface ProductService {
 
    public Product selectByPrimaryKey(int id);
 
    public List<Product> getAll() ;
 
    public Object deleteProduct(int id);
 
    public Object addProduct(Product product);
 
    public Object updateProduct(Product product);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package ehcache.demo.service.Impl;
 
import com.alibaba.fastjson.JSON;
import ehcache.demo.entity.Product;
import ehcache.demo.mapper.ProductMapper;
import ehcache.demo.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
@Service
public class ProductServiceImpl implements ProductService {
 
    @Autowired
    private ProductMapper productMapper;
 
    /* 注入springboot自动配置的redis */
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
 
    @Cacheable(value = "product", key = "#id")
    public Product selectByPrimaryKey(int id) {
        return productMapper.selectByPrimaryKey(id);
    }
 
    public List<Product> getAll() {
        // 创建redis序列化构造器,方便存储可视化
        RedisSerializer<?> redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);  //序列化key
 
        // 查询redis缓存
        @SuppressWarnings("unchecked")
        List<Product> productList = (List<Product>) redisTemplate.opsForValue().get("allproducts");
 
        /* 使用双重验证锁解决高并发环境下的缓存穿透问题 */
        if (productList == null) { // 第一重验证
            synchronized (this) {
                productList = (List<Product>) redisTemplate.opsForValue().get("allproducts");
                if (productList == null) { // 第二重验证
                    System.out.println("查询数据库............");
                    // 缓存为空,则查询数据 entity需支持序列化 implements Serializable
                    productList = productMapper.getAll();
                    // 数据库查询的数据保存至缓存
                    redisTemplate.opsForValue().set("allproducts", productList);
                } else {
                    System.out.println("查询缓存............");
                }
            }
        } else {
            System.out.println("查询缓存............");
        }
        return productList;
    }
 
    public Object deleteProduct(int id) {
        return productMapper.deleteByPrimaryKey(id);
    }
 
    @Override
    public Object addProduct(Product product) {
        return productMapper.insert(product);
    }
 
    public Object updateProduct(Product product) {
        return productMapper.updateByPrimaryKey(product);
    }
}

pom.xml 文件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>Ehcache</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
 
    <properties>
        <java.version>1.8</java.version>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
 
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.61</version>
        </dependency>
 
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.6.7</version>
        </dependency>
 
        <!--添加热部署依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
            <scope>true</scope>
        </dependency>
 
        <!--mybatis spring起步依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
 
 
        <!-- tomcat对jsp支持开启 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <!--jstl支持开启 -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- servlet支持开启 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>
        <!--jstl支持开启 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
 
        <!-- 分页插件开始 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.7</version>
        </dependency>
        <!-- 分页插件结束 -->
 
        <!-- mysql支持开始 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
        <!-- mysql支持结束 -->
 
        <!-- 加载redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
 
        <!-- Spring Boot 缓存支持启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!-- Ehcache 坐标 -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>
 
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
 
        <!--添加防止生成后找不到XML、YML、properties文件-->
        <resources>
            <resource>
                <directory>src/main/webapp</directory>
                <targetPath>META-INF/resources</targetPath>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
 
</project>