batch = personService.batch(personList);
81 | Assert.assertEquals(batch.size(), 3);
82 | }
83 |
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/git-flow-plus.config:
--------------------------------------------------------------------------------
1 | {"dingtalkToken":"","featurePrefix":"feature/","helloToken":"","hotfixPrefix":"hotfix/","masterBranch":"master","pattern":"(?:fix|chore|docs|feat|refactor|style|test)(?:\\(.*\\))?\\s+(?:XM\\d{7}-\\d+)(?:\\s*)(?:\\S+)(?:[\\n\\r]{2,})(?:[\\s\\S]*)","patternExplain":"规范说明:https://blog.csdn.net/xiaolyuh123/article/details/129042182\n正确示例:\n\nfeat(web)XM2102301-30用户目标地图\n\n背景:\nhttp://jira.xxx.com/browse/XM2607301-30\n修改:\n1.使用idea自带http工具来替换postman\n2.新增查询我的目标接口\n影响:\n1.影响PC端个人目标地图显示","releaseBranch":"release","tagPrefix":"","testBranch":"test"}
2 |
--------------------------------------------------------------------------------
/images/offset原理.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/offset原理.png
--------------------------------------------------------------------------------
/images/pay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/pay.jpg
--------------------------------------------------------------------------------
/images/pubsub重连.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/pubsub重连.png
--------------------------------------------------------------------------------
/images/web-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/web-1.png
--------------------------------------------------------------------------------
/images/web-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/web-2.png
--------------------------------------------------------------------------------
/images/web-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/web-3.png
--------------------------------------------------------------------------------
/images/wechat.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/wechat.jpeg
--------------------------------------------------------------------------------
/images/wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/wechat.png
--------------------------------------------------------------------------------
/images/wx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/wx.jpg
--------------------------------------------------------------------------------
/images/总体架构.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/总体架构.png
--------------------------------------------------------------------------------
/images/拉模式数据同步流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/拉模式数据同步流程.png
--------------------------------------------------------------------------------
/images/推模式数据同步流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/推模式数据同步流程.png
--------------------------------------------------------------------------------
/images/数据一致性架构.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/数据一致性架构.png
--------------------------------------------------------------------------------
/images/数据读取流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/images/数据读取流程.png
--------------------------------------------------------------------------------
/layering-cache-aspectj/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | layering-cache
7 | com.github.xiaolyuh
8 | 3.5.0
9 |
10 | 4.0.0
11 |
12 | layering-cache-aspectj
13 | layering-cache-aspectj
14 | 多级缓存注解模块
15 | jar
16 |
17 |
18 |
19 | com.github.xiaolyuh
20 | layering-cache-core
21 | 3.5.0
22 |
23 |
24 |
25 | org.slf4j
26 | slf4j-api
27 | true
28 |
29 |
30 |
31 | com.github.ben-manes.caffeine
32 | caffeine
33 |
34 |
35 |
36 | org.springframework
37 | spring-core
38 | true
39 |
40 |
41 |
42 | org.springframework
43 | spring-aop
44 | true
45 |
46 |
47 |
48 | org.springframework
49 | spring-context
50 | true
51 |
52 |
53 |
54 | org.aspectj
55 | aspectjweaver
56 | true
57 |
58 |
59 |
60 | org.springframework.boot
61 | spring-boot-starter-test
62 | test
63 |
64 |
65 |
66 | org.slf4j
67 | slf4j-log4j12
68 | test
69 |
70 |
71 |
72 | com.alibaba
73 | fastjson
74 | test
75 |
76 |
77 |
78 | com.esotericsoftware
79 | kryo-shaded
80 | test
81 |
82 |
83 |
84 | io.protostuff
85 | protostuff-core
86 | test
87 |
88 |
89 |
90 | io.protostuff
91 | protostuff-runtime
92 | test
93 |
94 |
95 |
96 | io.lettuce
97 | lettuce-core
98 | test
99 |
100 |
101 |
102 | com.fasterxml.jackson.core
103 | jackson-databind
104 | test
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/annotation/BatchCacheable.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.annotation;
2 |
3 | import com.github.xiaolyuh.support.CacheMode;
4 | import java.lang.annotation.Documented;
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 | import org.springframework.core.annotation.AliasFor;
10 |
11 | /**
12 | * 表示调用的方法的结果是可以被缓存的。
13 | * 通过缓存key队列批量获取缓存数据。
14 | * 当该方法被调用时先检查缓存是否命中,如果没有命中再调用被缓存的方法,并将其返回值放到缓存中。
15 | * 这里的value和key都支持SpEL 表达式
16 | *
17 | * @author rayon177
18 | */
19 | @Target({ElementType.METHOD})
20 | @Retention(RetentionPolicy.RUNTIME)
21 | @Documented
22 | public @interface BatchCacheable {
23 |
24 | /**
25 | * 别名是 {@link #cacheNames}.
26 | *
27 | * @return String[]
28 | */
29 | @AliasFor("cacheNames")
30 | String[] value() default {};
31 |
32 | /**
33 | * 缓存名称,支持SpEL表达式
34 | *
35 | * @return String[]
36 | */
37 | @AliasFor("value")
38 | String[] cacheNames() default {};
39 |
40 | /**
41 | * 描述
42 | *
43 | * @return String
44 | */
45 | String depict() default "";
46 |
47 | /**
48 | * 缓存keys,需要是SpEL表达式。这个表达式会用于从参数和返回值中获取缓存keys
49 | * 该表达式需要返回一个List对象
50 | *
51 | * @return String
52 | */
53 | String keys();
54 |
55 | /**
56 | * 生成缓存的条件,默认空全部缓存。支持SpEL表达式
57 | *
58 | * @return
59 | */
60 | String condition() default "";
61 |
62 | /**
63 | * 缓存模式(只使用一级缓存或者二级缓存)
64 | *
65 | * @return boolean
66 | */
67 | CacheMode cacheMode() default CacheMode.ALL;
68 |
69 | /**
70 | * 一级缓存配置
71 | *
72 | * @return FirstCache
73 | */
74 | FirstCache firstCache() default @FirstCache();
75 |
76 | /**
77 | * 二级缓存配置
78 | *
79 | * @return SecondaryCache
80 | */
81 | SecondaryCache secondaryCache() default @SecondaryCache();
82 | }
83 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/annotation/CacheEvict.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.annotation;
2 |
3 | import com.github.xiaolyuh.support.CacheMode;
4 | import java.lang.annotation.Documented;
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Inherited;
7 | import java.lang.annotation.Retention;
8 | import java.lang.annotation.RetentionPolicy;
9 | import java.lang.annotation.Target;
10 | import org.springframework.core.annotation.AliasFor;
11 |
12 | /**
13 | * 删除缓存
14 | *
15 | * @author olafwang
16 | */
17 | @Target({ElementType.METHOD, ElementType.TYPE})
18 | @Retention(RetentionPolicy.RUNTIME)
19 | @Inherited
20 | @Documented
21 | public @interface CacheEvict {
22 |
23 | /**
24 | * 别名 {@link #cacheNames}.
25 | *
26 | * @return String[]
27 | */
28 | @AliasFor("cacheNames")
29 | String[] value() default {};
30 |
31 | /**
32 | * 缓存名称
33 | *
34 | * @return String[]
35 | */
36 | @AliasFor("value")
37 | String[] cacheNames() default {};
38 |
39 | /**
40 | * 缓存key,支持SpEL表达式
41 | *
42 | * {@code #root.method}, {@code #root.target}, and {@code #root.caches} for
43 | * references to the {@link java.lang.reflect.Method method}, target object, and
44 | * affected cache(s) respectively.
45 | * Shortcuts for the method name ({@code #root.methodName}) and target class
46 | * ({@code #root.targetClass}) are also available.
47 | * Method arguments can be accessed by index. For instance the second argument
48 | * can be accessed via {@code #root.args[1]}, {@code #p1} or {@code #a1}. Arguments
49 | * can also be accessed by name if that information is available.
50 | *
51 | *
52 | * @return String
53 | */
54 | String key() default "";
55 |
56 | /**
57 | * 缓存模式(只使用一级缓存或者二级缓存)
58 | *
59 | * @return boolean
60 | */
61 | CacheMode cacheMode() default CacheMode.ALL;
62 |
63 | /**
64 | * 是否删除缓存中所有数据
65 | * 默认情况下是只删除关联key的缓存数据
66 | *
注意:当该参数设置成 {@code true} 时 {@link #key} 参数将无效
67 | *
68 | * @return boolean
69 | */
70 | boolean allEntries() default false;
71 |
72 | /**
73 | * 是否异步删除缓存中所有数据
74 | *
75 | * @return boolean
76 | */
77 | boolean async() default false;
78 | }
79 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/annotation/CachePut.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.annotation;
2 |
3 | import com.github.xiaolyuh.support.CacheMode;
4 | import java.lang.annotation.Documented;
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Inherited;
7 | import java.lang.annotation.Retention;
8 | import java.lang.annotation.RetentionPolicy;
9 | import java.lang.annotation.Target;
10 | import org.springframework.core.annotation.AliasFor;
11 |
12 | /**
13 | * 将对应数据放到缓存中
14 | *
15 | * @author olafwang
16 | */
17 | @Target({ElementType.METHOD, ElementType.TYPE})
18 | @Retention(RetentionPolicy.RUNTIME)
19 | @Inherited
20 | @Documented
21 | public @interface CachePut {
22 |
23 | /**
24 | * 别名 {@link #cacheNames}.
25 | *
26 | * @return String[]
27 | */
28 | @AliasFor("cacheNames")
29 | String[] value() default {};
30 |
31 | /**
32 | * 缓存名称
33 | *
34 | * @return String[]
35 | */
36 | @AliasFor("value")
37 | String[] cacheNames() default {};
38 |
39 | /**
40 | * 描述
41 | *
42 | * @return String
43 | */
44 | String depict() default "";
45 |
46 | /**
47 | * 缓存key,支持SpEL表达式
48 | *
The SpEL expression evaluates against a dedicated context that provides the
49 | * following meta-data:
50 | *
51 | * {@code #result} for a reference to the result of the method invocation. For
52 | * supported wrappers such as {@code Optional}, {@code #result} refers to the actual
53 | * object, not the wrapper
54 | * {@code #root.method}, {@code #root.target}, and {@code #root.caches} for
55 | * references to the {@link java.lang.reflect.Method method}, target object, and
56 | * affected cache(s) respectively.
57 | * Shortcuts for the method name ({@code #root.methodName}) and target class
58 | * ({@code #root.targetClass}) are also available.
59 | * Method arguments can be accessed by index. For instance the second argument
60 | * can be accessed via {@code #root.args[1]}, {@code #p1} or {@code #a1}. Arguments
61 | * can also be accessed by name if that information is available.
62 | *
63 | *
64 | * @return String
65 | */
66 | String key() default "";
67 |
68 | /**
69 | * 缓存模式(只使用一级缓存或者二级缓存)
70 | *
71 | * @return boolean
72 | */
73 | CacheMode cacheMode() default CacheMode.ALL;
74 |
75 | /**
76 | * 一级缓存配置
77 | *
78 | * @return FirstCache
79 | */
80 | FirstCache firstCache() default @FirstCache();
81 |
82 | /**
83 | * 二级缓存配置
84 | *
85 | * @return SecondaryCache
86 | */
87 | SecondaryCache secondaryCache() default @SecondaryCache();
88 | }
89 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/annotation/Cacheable.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.annotation;
2 |
3 | import com.github.xiaolyuh.support.CacheMode;
4 | import java.lang.annotation.Documented;
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Inherited;
7 | import java.lang.annotation.Retention;
8 | import java.lang.annotation.RetentionPolicy;
9 | import java.lang.annotation.Target;
10 | import org.springframework.core.annotation.AliasFor;
11 |
12 | /**
13 | * 表示调用的方法(或类中的所有方法)的结果是可以被缓存的。
14 | * 当该方法被调用时先检查缓存是否命中,如果没有命中再调用被缓存的方法,并将其返回值放到缓存中。
15 | * 这里的value和key都支持SpEL 表达式
16 | *
17 | * @author olafwang
18 | */
19 | @Target({ElementType.METHOD, ElementType.TYPE})
20 | @Retention(RetentionPolicy.RUNTIME)
21 | @Inherited
22 | @Documented
23 | public @interface Cacheable {
24 |
25 | /**
26 | * 别名是 {@link #cacheNames}.
27 | *
28 | * @return String[]
29 | */
30 | @AliasFor("cacheNames")
31 | String[] value() default {};
32 |
33 | /**
34 | * 缓存名称,支持SpEL表达式
35 | *
36 | * @return String[]
37 | */
38 | @AliasFor("value")
39 | String[] cacheNames() default {};
40 |
41 | /**
42 | * 描述
43 | *
44 | * @return String
45 | */
46 | String depict() default "";
47 |
48 | /**
49 | * 缓存key,支持SpEL表达式
50 | * The SpEL expression evaluates against a dedicated context that provides the
51 | * following meta-data:
52 | *
53 | * {@code #root.method}, {@code #root.target}, and {@code #root.caches} for
54 | * references to the {@link java.lang.reflect.Method method}, target object, and
55 | * affected cache(s) respectively.
56 | * Shortcuts for the method name ({@code #root.methodName}) and target class
57 | * ({@code #root.targetClass}) are also available.
58 | * Method arguments can be accessed by index. For instance the second argument
59 | * can be accessed via {@code #root.args[1]}, {@code #p1} or {@code #a1}. Arguments
60 | * can also be accessed by name if that information is available.
61 | *
62 | *
63 | * @return String
64 | */
65 | String key() default "";
66 |
67 | /**
68 | * 生成缓存的条件,默认空全部缓存。支持SpEL表达式
69 | *
70 | * @return 条件表达式
71 | */
72 | String condition() default "";
73 |
74 | /**
75 | * 缓存模式(只使用一级缓存或者二级缓存)
76 | *
77 | * @return boolean
78 | */
79 | CacheMode cacheMode() default CacheMode.ALL;
80 |
81 | /**
82 | * 一级缓存配置
83 | *
84 | * @return FirstCache
85 | */
86 | FirstCache firstCache() default @FirstCache();
87 |
88 | /**
89 | * 二级缓存配置
90 | *
91 | * @return SecondaryCache
92 | */
93 | SecondaryCache secondaryCache() default @SecondaryCache();
94 | }
95 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/annotation/Caching.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.annotation;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Inherited;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | /**
11 | * 同时使用多个缓存注解
12 | *
13 | * @author olafwang
14 | */
15 | @Target({ElementType.METHOD, ElementType.TYPE})
16 | @Retention(RetentionPolicy.RUNTIME)
17 | @Inherited
18 | @Documented
19 | public @interface Caching {
20 | Cacheable[] cacheable() default {};
21 |
22 | CachePut[] put() default {};
23 |
24 | CacheEvict[] evict() default {};
25 | }
26 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/annotation/FirstCache.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.annotation;
2 |
3 | import com.github.xiaolyuh.support.ExpireMode;
4 | import java.lang.annotation.Documented;
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Inherited;
7 | import java.lang.annotation.Retention;
8 | import java.lang.annotation.RetentionPolicy;
9 | import java.lang.annotation.Target;
10 | import java.util.concurrent.TimeUnit;
11 |
12 | /**
13 | * 一级缓存配置项
14 | *
15 | * @author yuhao.wang
16 | */
17 | @Target({ElementType.METHOD, ElementType.TYPE})
18 | @Retention(RetentionPolicy.RUNTIME)
19 | @Inherited
20 | @Documented
21 | public @interface FirstCache {
22 | /**
23 | * 缓存初始Size
24 | *
25 | * @return int
26 | */
27 | int initialCapacity() default 10;
28 |
29 | /**
30 | * 缓存最大Size
31 | *
32 | * @return int
33 | */
34 | int maximumSize() default 5000;
35 |
36 | /**
37 | * 缓存有效时间
38 | *
39 | * @return int
40 | */
41 | int expireTime() default 9;
42 |
43 | /**
44 | * 缓存时间单位
45 | *
46 | * @return TimeUnit
47 | */
48 | TimeUnit timeUnit() default TimeUnit.MINUTES;
49 |
50 | /**
51 | * 缓存失效模式
52 | *
53 | * @return ExpireMode
54 | * @see ExpireMode
55 | */
56 | ExpireMode expireMode() default ExpireMode.WRITE;
57 | }
58 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/annotation/SecondaryCache.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.annotation;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Inherited;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | /**
12 | * 二级缓存配置项
13 | *
14 | * @author yuhao.wang
15 | */
16 | @Target({ElementType.METHOD, ElementType.TYPE})
17 | @Retention(RetentionPolicy.RUNTIME)
18 | @Inherited
19 | @Documented
20 | public @interface SecondaryCache {
21 | /**
22 | * 缓存有效时间
23 | *
24 | * @return long
25 | */
26 | long expireTime() default 5;
27 |
28 | /**
29 | * 缓存主动在失效前强制刷新缓存的时间
30 | * 建议是: preloadTime = expireTime * 0.2
31 | *
32 | * @return long
33 | */
34 | long preloadTime() default 1;
35 |
36 | /**
37 | * 时间单位 {@link TimeUnit}
38 | *
39 | * @return TimeUnit
40 | */
41 | TimeUnit timeUnit() default TimeUnit.HOURS;
42 |
43 | /**
44 | * 是否强制刷新(直接执行被缓存方法),默认是false
45 | * 不建议在批量缓存式开启强制刷新
46 | *
47 | * @return boolean
48 | */
49 | boolean forceRefresh() default false;
50 |
51 | /**
52 | * 非空值和null值之间的时间倍率,默认是1。isAllowNullValue=true才有效
53 | *
54 | * 如配置缓存的有效时间是200秒,倍率这设置成10,
55 | * 那么当缓存value为null时,缓存的有效时间将是20秒,非空时为200秒
56 | *
57 | *
58 | * @return int
59 | */
60 | int magnification() default 1;
61 | }
62 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/expression/CacheEvaluationContext.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2002-2016 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.xiaolyuh.expression;
18 |
19 | import java.lang.reflect.Method;
20 | import java.util.HashSet;
21 | import java.util.Set;
22 | import org.springframework.context.expression.MethodBasedEvaluationContext;
23 | import org.springframework.core.ParameterNameDiscoverer;
24 |
25 | /**
26 | * Cache specific evaluation context that adds a method parameters as SpEL
27 | * variables, in a lazy manner. The lazy nature eliminates unneeded
28 | * parsing of classes byte code for parameter discovery.
29 | *
30 | * Also define a set of "unavailable variables" (i.e. variables that should
31 | * lead to an exception right the way when they are accessed). This can be useful
32 | * to verify a condition does not match even when not all potential variables
33 | * are present.
34 | *
35 | *
To limit the creation of objects, an ugly constructor is used
36 | * (rather then a dedicated 'closure'-like class for deferred execution).
37 | *
38 | * @author Costin Leau
39 | * @author Stephane Nicoll
40 | * @author Juergen Hoeller
41 | * @since 3.1
42 | */
43 | class CacheEvaluationContext extends MethodBasedEvaluationContext {
44 |
45 | private final Set unavailableVariables = new HashSet(1);
46 |
47 |
48 | CacheEvaluationContext(Object rootObject, Method method, Object[] arguments,
49 | ParameterNameDiscoverer parameterNameDiscoverer) {
50 |
51 | super(rootObject, method, arguments, parameterNameDiscoverer);
52 | }
53 |
54 |
55 | /**
56 | * Add the specified variable name as unavailable for that context.
57 | * Any expression trying to access this variable should lead to an exception.
58 | * This permits the validation of expressions that could potentially a
59 | * variable even when such variable isn't available yet. Any expression
60 | * trying to use that variable should therefore fail to evaluate.
61 | */
62 | public void addUnavailableVariable(String name) {
63 | this.unavailableVariables.add(name);
64 | }
65 |
66 |
67 | /**
68 | * Load the param information only when needed.
69 | */
70 | @Override
71 | public Object lookupVariable(String name) {
72 | if (this.unavailableVariables.contains(name)) {
73 | throw new VariableNotAvailableException(name);
74 | }
75 | return super.lookupVariable(name);
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/expression/CacheExpressionRootObject.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2002-2013 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.xiaolyuh.expression;
18 |
19 | import java.lang.reflect.Method;
20 | import org.springframework.util.Assert;
21 |
22 | /**
23 | * Class describing the root object used during the expression evaluation.
24 | *
25 | * @author Costin Leau
26 | * @author Sam Brannen
27 | * @since 3.1
28 | */
29 | class CacheExpressionRootObject {
30 |
31 | private final Method method;
32 |
33 | private final Object[] args;
34 |
35 | private final Object target;
36 |
37 | private final Class> targetClass;
38 |
39 |
40 | public CacheExpressionRootObject(Method method, Object[] args, Object target, Class> targetClass) {
41 |
42 | Assert.notNull(method, "Method is required");
43 | Assert.notNull(targetClass, "targetClass is required");
44 | this.method = method;
45 | this.target = target;
46 | this.targetClass = targetClass;
47 | this.args = args;
48 | }
49 |
50 | public Method getMethod() {
51 | return this.method;
52 | }
53 |
54 | public String getMethodName() {
55 | return this.method.getName();
56 | }
57 |
58 | public Object[] getArgs() {
59 | return this.args;
60 | }
61 |
62 | public Object getTarget() {
63 | return this.target;
64 | }
65 |
66 | public Class> getTargetClass() {
67 | return this.targetClass;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/expression/VariableNotAvailableException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2002-2016 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.xiaolyuh.expression;
18 |
19 | import org.springframework.expression.EvaluationException;
20 |
21 | /**
22 | * A specific {@link EvaluationException} to mention that a given variable
23 | * used in the expression is not available in the context.
24 | *
25 | * @author Stephane Nicoll
26 | * @since 4.0.6
27 | */
28 | @SuppressWarnings("serial")
29 | class VariableNotAvailableException extends EvaluationException {
30 |
31 | private final String name;
32 |
33 | public VariableNotAvailableException(String name) {
34 | super("Variable '" + name + "' is not available");
35 | this.name = name;
36 | }
37 |
38 |
39 | public String getName() {
40 | return this.name;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/support/KeyGenerator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2002-2013 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.xiaolyuh.support;
18 |
19 | import java.lang.reflect.Method;
20 |
21 | /**
22 | * Cache key generator. Used for creating a key based on the given method
23 | * (used as context) and its parameters.
24 | *
25 | * @author Costin Leau
26 | * @author Chris Beams
27 | * @author Phillip Webb
28 | * @since 3.1
29 | */
30 | public interface KeyGenerator {
31 |
32 | /**
33 | * Generate a key for the given method and its parameters.
34 | *
35 | * @param target the target instance
36 | * @param method the method being called
37 | * @param params the method parameters (with any var-args expanded)
38 | * @return a generated key
39 | */
40 | Object generate(Object target, Method method, Object... params);
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/support/SimpleKey.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2002-2016 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.xiaolyuh.support;
18 |
19 | import java.io.Serializable;
20 | import java.util.Arrays;
21 | import org.springframework.util.Assert;
22 | import org.springframework.util.StringUtils;
23 |
24 | /**
25 | * A simple key as returned from the {@link SimpleKeyGenerator}.
26 | *
27 | * @author Phillip Webb
28 | * @see SimpleKeyGenerator
29 | * @since 4.0
30 | */
31 | public class SimpleKey implements Serializable {
32 |
33 | public static final SimpleKey EMPTY = new SimpleKey();
34 |
35 | private final Object[] params;
36 | private final int hashCode;
37 |
38 |
39 | /**
40 | * Create a new {@link SimpleKey} instance.
41 | *
42 | * @param elements the elements of the key
43 | */
44 | public SimpleKey(Object... elements) {
45 | Assert.notNull(elements, "Elements must not be null");
46 | this.params = new Object[elements.length];
47 | System.arraycopy(elements, 0, this.params, 0, elements.length);
48 | this.hashCode = Arrays.deepHashCode(this.params);
49 | }
50 |
51 | @Override
52 | public boolean equals(Object obj) {
53 | return (this == obj || (obj instanceof SimpleKey
54 | && Arrays.deepEquals(this.params, ((SimpleKey) obj).params)));
55 | }
56 |
57 | @Override
58 | public final int hashCode() {
59 | return this.hashCode;
60 | }
61 |
62 | @Override
63 | public String toString() {
64 | return getClass().getSimpleName() + " [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
65 | }
66 |
67 | public Object[] getParams() {
68 | return params;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/main/java/com/github/xiaolyuh/support/SimpleKeyGenerator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2002-2014 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.xiaolyuh.support;
18 |
19 | import java.lang.reflect.Method;
20 |
21 | /**
22 | * Simple key generator. Returns the parameter itself if a single non-null value
23 | * is given, otherwise returns a {@link SimpleKey} of the parameters.
24 | *
Unlike DefaultKeyGenerator, no collisions will occur with the keys
25 | * generated by this class. The returned {@link SimpleKey} object can be safely
26 | * used with a {@link org.springframework.cache.concurrent.ConcurrentMapCache},
27 | * however, might not be suitable for all {@link org.springframework.cache.Cache}
28 | * implementations.
29 | *
30 | * @author Phillip Webb
31 | * @author Juergen Hoeller
32 | * @see SimpleKey
33 | * @see org.springframework.cache.annotation.CachingConfigurer
34 | * @since 4.0
35 | */
36 | public class SimpleKeyGenerator implements KeyGenerator {
37 |
38 | @Override
39 | public Object generate(Object target, Method method, Object... params) {
40 | return generateKey(params);
41 | }
42 |
43 | /**
44 | * Generate a key based on the specified parameters.
45 | *
46 | * @param params params
47 | * @return Object
48 | */
49 | public static Object generateKey(Object... params) {
50 | if (params.length == 0) {
51 | return SimpleKey.EMPTY;
52 | }
53 | if (params.length == 1) {
54 | Object param = params[0];
55 | if (param != null && !param.getClass().isArray()) {
56 | return param;
57 | }
58 | }
59 | return new SimpleKey(params);
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/test/java/com/github/xiaolyuh/config/CacheClusterConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.config;
2 |
3 | import com.github.xiaolyuh.aspect.LayeringAspect;
4 | import com.github.xiaolyuh.manager.CacheManager;
5 | import com.github.xiaolyuh.manager.LayeringCacheManager;
6 | import com.github.xiaolyuh.redis.clinet.RedisClient;
7 | import com.github.xiaolyuh.test.BatchTestService;
8 | import com.github.xiaolyuh.test.TestService;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.context.annotation.EnableAspectJAutoProxy;
12 | import org.springframework.context.annotation.Import;
13 |
14 | @Configuration
15 | @Import({RedisClusterConfig.class})
16 | @EnableAspectJAutoProxy
17 | public class CacheClusterConfig {
18 |
19 | @Bean
20 | public CacheManager layeringCacheManager(RedisClient layeringCacheRedisClient) {
21 | LayeringCacheManager layeringCacheManager = new LayeringCacheManager(layeringCacheRedisClient);
22 | // 开启统计功能
23 | layeringCacheManager.setStats(true);
24 | return layeringCacheManager;
25 | }
26 |
27 | @Bean
28 | public LayeringAspect layeringAspect() {
29 | return new LayeringAspect();
30 | }
31 |
32 | @Bean
33 | public TestService testService() {
34 | return new TestService();
35 | }
36 |
37 | @Bean
38 | public BatchTestService testBatchTestService() {
39 | return new BatchTestService();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/test/java/com/github/xiaolyuh/config/CacheSentinelConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.config;
2 |
3 | import com.github.xiaolyuh.aspect.LayeringAspect;
4 | import com.github.xiaolyuh.manager.CacheManager;
5 | import com.github.xiaolyuh.manager.LayeringCacheManager;
6 | import com.github.xiaolyuh.redis.clinet.RedisClient;
7 | import com.github.xiaolyuh.test.BatchTestService;
8 | import com.github.xiaolyuh.test.TestService;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.context.annotation.EnableAspectJAutoProxy;
12 | import org.springframework.context.annotation.Import;
13 |
14 | @Configuration
15 | @Import({RedisSentinelConfig.class})
16 | @EnableAspectJAutoProxy
17 | public class CacheSentinelConfig {
18 |
19 | @Bean
20 | public CacheManager layeringCacheManager(RedisClient layeringCacheRedisClient) {
21 | LayeringCacheManager layeringCacheManager = new LayeringCacheManager(layeringCacheRedisClient);
22 | // 开启统计功能
23 | layeringCacheManager.setStats(true);
24 | return layeringCacheManager;
25 | }
26 |
27 | @Bean
28 | public LayeringAspect layeringAspect() {
29 | return new LayeringAspect();
30 | }
31 |
32 | @Bean
33 | public TestService testService() {
34 | return new TestService();
35 | }
36 |
37 | @Bean
38 | public BatchTestService testBatchTestService() {
39 | return new BatchTestService();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/test/java/com/github/xiaolyuh/config/CacheSingleConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.config;
2 |
3 | import com.github.xiaolyuh.aspect.LayeringAspect;
4 | import com.github.xiaolyuh.manager.CacheManager;
5 | import com.github.xiaolyuh.manager.LayeringCacheManager;
6 | import com.github.xiaolyuh.redis.clinet.RedisClient;
7 | import com.github.xiaolyuh.test.BatchTestService;
8 | import com.github.xiaolyuh.test.TestService;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.context.annotation.EnableAspectJAutoProxy;
12 | import org.springframework.context.annotation.Import;
13 |
14 | @Configuration
15 | @Import({RedisSingleConfig.class})
16 | @EnableAspectJAutoProxy
17 | public class CacheSingleConfig {
18 |
19 | @Bean
20 | public CacheManager layeringCacheManager(RedisClient layeringCacheRedisClient) {
21 | LayeringCacheManager layeringCacheManager = new LayeringCacheManager(layeringCacheRedisClient);
22 | // 开启统计功能
23 | layeringCacheManager.setStats(true);
24 | return layeringCacheManager;
25 | }
26 |
27 | @Bean
28 | public LayeringAspect layeringAspect() {
29 | return new LayeringAspect();
30 | }
31 |
32 | @Bean
33 | public TestService testService() {
34 | return new TestService();
35 | }
36 |
37 | @Bean
38 | public BatchTestService testBatchTestService() {
39 | return new BatchTestService();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/test/java/com/github/xiaolyuh/config/RedisClusterConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.config;
2 |
3 | import com.github.xiaolyuh.redis.clinet.ClusterRedisClient;
4 | import com.github.xiaolyuh.redis.clinet.RedisClient;
5 | import com.github.xiaolyuh.redis.clinet.RedisProperties;
6 | import com.github.xiaolyuh.redis.serializer.FastJsonRedisSerializer;
7 | import com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer;
8 | import com.github.xiaolyuh.redis.serializer.JdkRedisSerializer;
9 | import com.github.xiaolyuh.redis.serializer.KryoRedisSerializer;
10 | import com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer;
11 | import com.github.xiaolyuh.redis.serializer.StringRedisSerializer;
12 | import com.github.xiaolyuh.util.StringUtils;
13 | import org.springframework.beans.factory.annotation.Value;
14 | import org.springframework.context.annotation.Bean;
15 | import org.springframework.context.annotation.Configuration;
16 | import org.springframework.context.annotation.PropertySource;
17 |
18 | @Configuration
19 | @PropertySource({"classpath:application.properties"})
20 | public class RedisClusterConfig {
21 |
22 | @Value("${spring.redis.cluster:127.0.0.1:9000,127.0.0.1:9001,127.0.0.1:9002,127.0.0.1:9003,127.0.0.1:9004,127.0.0.1:9005}")
23 | private String cluster;
24 |
25 | @Value("${spring.redis.password:}")
26 | private String password;
27 |
28 | @Bean
29 | public RedisClient layeringCacheRedisClient() {
30 | RedisProperties redisProperties = new RedisProperties();
31 | redisProperties.setPassword(StringUtils.isBlank(password) ? null : password);
32 | redisProperties.setCluster(cluster);
33 |
34 | KryoRedisSerializer kryoRedisSerializer = new KryoRedisSerializer();
35 | FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer();
36 | JacksonRedisSerializer jacksonRedisSerializer = new JacksonRedisSerializer();
37 | JdkRedisSerializer jdkRedisSerializer = new JdkRedisSerializer();
38 | ProtostuffRedisSerializer protostuffRedisSerializer = new ProtostuffRedisSerializer();
39 |
40 | StringRedisSerializer keyRedisSerializer = new StringRedisSerializer();
41 | ClusterRedisClient redisClient = new ClusterRedisClient(redisProperties);
42 | redisClient.setKeySerializer(keyRedisSerializer);
43 | redisClient.setValueSerializer(kryoRedisSerializer);
44 | return redisClient;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/test/java/com/github/xiaolyuh/config/RedisSentinelConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.config;
2 |
3 | import com.github.xiaolyuh.redis.clinet.RedisClient;
4 | import com.github.xiaolyuh.redis.clinet.RedisProperties;
5 | import com.github.xiaolyuh.redis.clinet.SentinelRedisClient;
6 | import com.github.xiaolyuh.redis.serializer.FastJsonRedisSerializer;
7 | import com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer;
8 | import com.github.xiaolyuh.redis.serializer.JdkRedisSerializer;
9 | import com.github.xiaolyuh.redis.serializer.KryoRedisSerializer;
10 | import com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer;
11 | import com.github.xiaolyuh.redis.serializer.StringRedisSerializer;
12 | import com.github.xiaolyuh.util.StringUtils;
13 | import org.springframework.beans.factory.annotation.Value;
14 | import org.springframework.context.annotation.Bean;
15 | import org.springframework.context.annotation.Configuration;
16 | import org.springframework.context.annotation.PropertySource;
17 |
18 | @Configuration
19 | @PropertySource({"classpath:application.properties"})
20 | public class RedisSentinelConfig {
21 |
22 | @Value("${layering-cache.redis.nodes:127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381}")
23 | private String nodes;
24 |
25 | @Value("${spring.redis.password:123}")
26 | private String password;
27 |
28 | @Bean
29 | public RedisClient layeringCacheRedisClient() {
30 | RedisProperties redisProperties = new RedisProperties();
31 | redisProperties.setSentinelNodes(nodes);
32 | redisProperties.setPassword(StringUtils.isBlank(password) ? null : password);
33 |
34 | KryoRedisSerializer kryoRedisSerializer = new KryoRedisSerializer();
35 | FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer();
36 | JacksonRedisSerializer jacksonRedisSerializer = new JacksonRedisSerializer();
37 | JdkRedisSerializer jdkRedisSerializer = new JdkRedisSerializer();
38 | ProtostuffRedisSerializer protostuffRedisSerializer = new ProtostuffRedisSerializer();
39 |
40 | StringRedisSerializer keyRedisSerializer = new StringRedisSerializer();
41 |
42 | SentinelRedisClient redisClient = new SentinelRedisClient(redisProperties);
43 |
44 | redisClient.setKeySerializer(keyRedisSerializer);
45 | redisClient.setValueSerializer(protostuffRedisSerializer);
46 | return redisClient;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/test/java/com/github/xiaolyuh/config/RedisSingleConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.config;
2 |
3 | import com.github.xiaolyuh.redis.clinet.RedisClient;
4 | import com.github.xiaolyuh.redis.clinet.RedisProperties;
5 | import com.github.xiaolyuh.redis.clinet.SingleRedisClient;
6 | import com.github.xiaolyuh.redis.serializer.FastJsonRedisSerializer;
7 | import com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer;
8 | import com.github.xiaolyuh.redis.serializer.JdkRedisSerializer;
9 | import com.github.xiaolyuh.redis.serializer.KryoRedisSerializer;
10 | import com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer;
11 | import com.github.xiaolyuh.redis.serializer.StringRedisSerializer;
12 | import com.github.xiaolyuh.util.StringUtils;
13 | import org.springframework.beans.factory.annotation.Value;
14 | import org.springframework.context.annotation.Bean;
15 | import org.springframework.context.annotation.Configuration;
16 | import org.springframework.context.annotation.PropertySource;
17 |
18 | @Configuration
19 | @PropertySource({"classpath:application.properties"})
20 | public class RedisSingleConfig {
21 |
22 | @Value("${spring.redis.database:0}")
23 | private int database;
24 |
25 | @Value("${spring.redis.host:127.0.0.1}")
26 | private String host;
27 |
28 | @Value("${spring.redis.password:}")
29 | private String password;
30 |
31 | @Value("${spring.redis.port:6379}")
32 | private int port;
33 |
34 | @Bean
35 | public RedisClient layeringCacheRedisClient() {
36 | RedisProperties redisProperties = new RedisProperties();
37 | redisProperties.setDatabase(database);
38 | redisProperties.setHost(host);
39 | redisProperties.setPassword(StringUtils.isBlank(password) ? null : password);
40 | redisProperties.setPort(port);
41 |
42 | KryoRedisSerializer kryoRedisSerializer = new KryoRedisSerializer();
43 | FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer();
44 | JacksonRedisSerializer jacksonRedisSerializer = new JacksonRedisSerializer();
45 | JdkRedisSerializer jdkRedisSerializer = new JdkRedisSerializer();
46 | ProtostuffRedisSerializer protostuffRedisSerializer = new ProtostuffRedisSerializer();
47 |
48 | StringRedisSerializer keyRedisSerializer = new StringRedisSerializer();
49 |
50 | SingleRedisClient redisClient = new SingleRedisClient(redisProperties);
51 |
52 | redisClient.setKeySerializer(keyRedisSerializer);
53 | redisClient.setValueSerializer(protostuffRedisSerializer);
54 | return redisClient;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/test/java/com/github/xiaolyuh/domain/User.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.domain;
2 |
3 | import java.io.Serializable;
4 | import java.math.BigDecimal;
5 | import java.util.ArrayList;
6 | import java.util.Date;
7 | import java.util.HashSet;
8 | import java.util.List;
9 | import java.util.Set;
10 |
11 | public class User implements Serializable {
12 |
13 | public User() {
14 | this.userId = 11L;
15 | this.name = "name";
16 | this.address = new Address();
17 | this.lastName = new String[]{"w", "四川", "~!@#%……&*()——+{}:“?》:''\">?《~!@#$%^&*()_+{}\\"};
18 | List lastNameList = new ArrayList<>();
19 | lastNameList.add("W");
20 | lastNameList.add("成都");
21 | this.lastNameList = lastNameList;
22 | this.lastNameSet = new HashSet<>(lastNameList);
23 | this.lastName = new String[]{"w", "四川", "~!@#%……&*()——+{}:“?》:''\">?《~!@#$%^&*()_+{}\\"};
24 | this.age = 122;
25 | this.height = 18.2;
26 | this.income = new BigDecimal(22.22);
27 | this.birthday = new Date();
28 | }
29 |
30 | public User(long userId,int age) {
31 | this();
32 | this.userId = userId;
33 | this.age = age;
34 | }
35 |
36 | public User(long userId ) {
37 | this();
38 | this.userId = userId;
39 | }
40 |
41 | private long userId;
42 |
43 | private String name;
44 |
45 | private Address address;
46 |
47 | private String[] lastName;
48 |
49 | private List lastNameList;
50 |
51 | private Set lastNameSet;
52 |
53 | private int age;
54 |
55 | private double height;
56 |
57 | private BigDecimal income;
58 |
59 | private Date birthday;
60 |
61 | public long getUserId() {
62 | return userId;
63 | }
64 |
65 | public void setUserId(long userId) {
66 | this.userId = userId;
67 | }
68 |
69 | public String getName() {
70 | return name;
71 | }
72 |
73 | public void setName(String name) {
74 | this.name = name;
75 | }
76 |
77 | public String[] getLastName() {
78 | return lastName;
79 | }
80 |
81 | public void setLastName(String[] lastName) {
82 | this.lastName = lastName;
83 | }
84 |
85 | public int getAge() {
86 | return age;
87 | }
88 |
89 | public void setAge(int age) {
90 | this.age = age;
91 | }
92 |
93 | public Address getAddress() {
94 | return address;
95 | }
96 |
97 | public void setAddress(Address address) {
98 | this.address = address;
99 | }
100 |
101 | public List getLastNameList() {
102 | return lastNameList;
103 | }
104 |
105 | public void setLastNameList(List lastNameList) {
106 | this.lastNameList = lastNameList;
107 | }
108 |
109 | public Set getLastNameSet() {
110 | return lastNameSet;
111 | }
112 |
113 | public void setLastNameSet(Set lastNameSet) {
114 | this.lastNameSet = lastNameSet;
115 | }
116 |
117 | public BigDecimal getIncome() {
118 | return income.setScale(2, BigDecimal.ROUND_UP);
119 | }
120 |
121 | public void setIncome(BigDecimal income) {
122 | this.income = income;
123 | }
124 |
125 | public double getHeight() {
126 | return height;
127 | }
128 |
129 | public void setHeight(double height) {
130 | this.height = height;
131 | }
132 |
133 | public static class Address implements Serializable {
134 | private String addredd;
135 |
136 | public Address() {
137 | this.addredd = "成都";
138 | }
139 |
140 | public String getAddredd() {
141 | return addredd;
142 | }
143 |
144 | public void setAddredd(String addredd) {
145 | this.addredd = addredd;
146 | }
147 | }
148 |
149 | public Date getBirthday() {
150 | return birthday;
151 | }
152 |
153 | public void setBirthday(Date birthday) {
154 | this.birthday = birthday;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/test/java/com/github/xiaolyuh/test/BatchTestService.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.test;
2 |
3 | import com.github.xiaolyuh.annotation.BatchCacheable;
4 | import com.github.xiaolyuh.annotation.FirstCache;
5 | import com.github.xiaolyuh.annotation.SecondaryCache;
6 | import com.github.xiaolyuh.domain.User;
7 | import com.github.xiaolyuh.support.CacheMode;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.concurrent.TimeUnit;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | /**
15 | * BatchTestService
16 | *
17 | * @author heyirui
18 | * @date 2024/7/11
19 | */
20 | public class BatchTestService {
21 | Logger logger = LoggerFactory.getLogger(getClass());
22 |
23 | @BatchCacheable(value = "user:info", keys = "#users.![userId]", depict = "用户信息缓存",
24 | firstCache = @FirstCache(expireTime = 10, timeUnit = TimeUnit.SECONDS),
25 | secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3, timeUnit = TimeUnit.SECONDS))
26 | public List getUserByIds(List users) {
27 | logger.debug("测试正常配置的批量缓存方法");
28 |
29 | ArrayList res = new ArrayList<>();
30 | for (int i = 0; i < Math.min(users.size(), 2); i++) {
31 | User user = new User();
32 | user.setUserId(users.get(i).getUserId());
33 | user.setAge(i);
34 | user.setLastName(new String[]{"w", "y", Integer.toString(i)});
35 | res.add(user);
36 | }
37 | return res;
38 | }
39 |
40 | @BatchCacheable(value = "user:info", keys = "#users.![userId]", depict = "用户信息缓存",
41 | firstCache = @FirstCache(expireTime = 10, timeUnit = TimeUnit.SECONDS),
42 | secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3, timeUnit = TimeUnit.SECONDS))
43 | public List getUserByIdsReturnNone(List users) {
44 | logger.debug("测试批量缓存方法返回空数组");
45 | return new ArrayList<>();
46 | }
47 |
48 | @BatchCacheable(value = "user:info:01", keys = "#users.![userId]", cacheMode = CacheMode.FIRST,
49 | firstCache = @FirstCache(expireTime = 10, timeUnit = TimeUnit.SECONDS),
50 | secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 7, forceRefresh = true, timeUnit = TimeUnit.SECONDS))
51 | public List getUserByIdDisableFirstCache(List users) {
52 | logger.debug("user:info:01 测试禁用二级缓存");
53 | ArrayList res = new ArrayList<>();
54 | for (int i = 0; i < Math.min(users.size(), 2); i++) {
55 | User user = new User();
56 | user.setUserId(users.get(i).getUserId());
57 | user.setAge(i);
58 | user.setLastName(new String[]{"w", "y", Integer.toString(i)});
59 | res.add(user);
60 | }
61 | return res;
62 | }
63 |
64 | @BatchCacheable(value = "user:info:02", keys = "#users.![userId]", cacheMode = CacheMode.SECOND,
65 | firstCache = @FirstCache(expireTime = 10, timeUnit = TimeUnit.SECONDS),
66 | secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 3, forceRefresh = true, timeUnit = TimeUnit.SECONDS))
67 | public List getUserByIdDisableSecondCache(List users) {
68 | logger.debug("user:info:01 测试禁用二级缓存");
69 | ArrayList res = new ArrayList<>();
70 | for (int i = 0; i < Math.min(users.size(), 2); i++) {
71 | User user = new User();
72 | user.setUserId(users.get(i).getUserId());
73 | user.setAge(i);
74 | user.setLastName(new String[]{"w", "y", Integer.toString(i)});
75 | res.add(user);
76 | }
77 | return res;
78 | }
79 |
80 |
81 | @BatchCacheable(value = "user:info:all", keys = "#users.![userId]",
82 | firstCache = @FirstCache(expireTime = 4, timeUnit = TimeUnit.SECONDS),
83 | secondaryCache = @SecondaryCache(expireTime = 10, preloadTime = 9, forceRefresh = true, timeUnit = TimeUnit.SECONDS))
84 | public List batchRefreshSecondCacheSyncFistCache(List users) {
85 | logger.debug("测试刷新二级缓存,同步更新一级缓存");
86 | return users;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/test/resources/application.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/layering-cache-aspectj/src/test/resources/application.properties
--------------------------------------------------------------------------------
/layering-cache-aspectj/src/test/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.rootLogger=debug, stdout
2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
3 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
4 | log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
--------------------------------------------------------------------------------
/layering-cache-core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | layering-cache
7 | com.github.xiaolyuh
8 | 3.5.0
9 |
10 | 4.0.0
11 |
12 | layering-cache-core
13 | layering-cache-core
14 | 多级缓存核心模块
15 | jar
16 |
17 |
18 |
19 | com.github.ben-manes.caffeine
20 | caffeine
21 | true
22 |
23 |
24 |
25 | io.lettuce
26 | lettuce-core
27 | true
28 |
29 |
30 |
31 | org.springframework
32 | spring-core
33 | true
34 |
35 |
36 |
37 | org.springframework
38 | spring-beans
39 | true
40 |
41 |
42 |
43 | org.springframework
44 | spring-context
45 | true
46 |
47 |
48 |
49 | org.slf4j
50 | slf4j-api
51 | true
52 |
53 |
54 |
55 | com.alibaba
56 | fastjson
57 | true
58 |
59 |
60 |
61 | com.esotericsoftware
62 | kryo-shaded
63 | true
64 |
65 |
66 |
67 | io.protostuff
68 | protostuff-core
69 | true
70 |
71 |
72 |
73 | com.fasterxml.jackson.core
74 | jackson-databind
75 | true
76 |
77 |
78 |
79 | io.protostuff
80 | protostuff-runtime
81 | true
82 |
83 |
84 |
85 | org.springframework.boot
86 | spring-boot-starter-test
87 | true
88 | test
89 |
90 |
91 |
92 | org.slf4j
93 | slf4j-log4j12
94 | true
95 | test
96 |
97 |
98 |
99 | org.projectlombok
100 | lombok
101 | true
102 | provided
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/cache/Cache.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache;
2 |
3 | import com.github.xiaolyuh.manager.CacheManager;
4 | import com.github.xiaolyuh.stats.CacheStats;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.concurrent.Callable;
8 | import java.util.function.Function;
9 |
10 | /**
11 | * 缓存的顶级接口
12 | *
13 | * @author yuhao.wang
14 | */
15 | public interface Cache {
16 |
17 | /**
18 | * 返回缓存名称
19 | *
20 | * @return String
21 | */
22 | String getName();
23 |
24 | /**
25 | * 返回真实Cache对象
26 | *
27 | * @return Object
28 | */
29 | Object getNativeCache();
30 |
31 | /**
32 | * 根据KEY返回缓存中对应的值,并将其返回类型转换成对应类型,如果对应key不存在返回NULL
33 | *
34 | * @param key 缓存key
35 | * @param resultType 返回值类型
36 | * @param Object
37 | * @return 缓存key对应的值
38 | */
39 | T get(String key, Class resultType);
40 |
41 | /**
42 | * 根据KEY返回缓存中对应的值,并将其返回类型转换成对应类型,如果对应key不存在则调用valueLoader加载数据
43 | *
44 | * @param key 缓存key
45 | * @param resultType 返回值类型
46 | * @param valueLoader 加载缓存的回调方法
47 | * @param Object
48 | * @return 缓存key对应的值
49 | */
50 | T get(String key, Class resultType, Callable valueLoader);
51 |
52 | /**
53 | * 根据KEY返回缓存中对应的值,并将其返回类型转换成对应类型。只返回缓存中存在的KEY-VALUE对
54 | *
55 | * @param keys 缓存keys
56 | * @param resultType 返回值类型
57 | * @param 键的类型
58 | * @param 值的类型
59 | * @return 缓存key对应的值
60 | */
61 | Map getAll(List keys, Class resultType);
62 |
63 | /**
64 | * 根据KEY返回缓存中对应的值,并将其返回类型转换成对应类型,如果对应key不存在则调用valueLoader加载数据
65 | *
66 | * @param keys 缓存keys
67 | * @param resultType 返回值类型
68 | * @param valueLoader 加载缓存的回调方法
69 | * @param 键的类型
70 | * @param 值的类型
71 | * @return 缓存key对应的值
72 | */
73 | Map getAll(List keys, Class resultType, Function valueLoader);
74 |
75 | /**
76 | * 将对应key-value放到缓存,如果key原来有值就直接覆盖
77 | *
78 | * @param key 缓存key
79 | * @param value 缓存的值
80 | */
81 | void put(String key, Object value);
82 |
83 | /**
84 | * 如果缓存key没有对应的值就将值put到缓存,如果有就直接返回原有的值
85 | * 就相当于:
86 | *
87 | * Object existingValue = cache.get(key);
88 | * if (existingValue == null) {
89 | * cache.put(key, value);
90 | * return null;
91 | * } else {
92 | * return existingValue;
93 | * }
94 | *
95 | * except that the action is performed atomically. While all out-of-the-box
96 | * {@link CacheManager} implementations are able to perform the put atomically,
97 | * the operation may also be implemented in two steps, e.g. with a check for
98 | * presence and a subsequent put, in a non-atomic way. Check the documentation
99 | * of the native cache implementation that you are using for more details.
100 | *
101 | * @param key 缓存key
102 | * @param value 缓存key对应的值
103 | * @param resultType 返回值类型
104 | * @param T
105 | * @return 因为值本身可能为NULL,或者缓存key本来就没有对应值的时候也为NULL,
106 | * 所以如果返回NULL就表示已经将key-value键值对放到了缓存中
107 | * @since 4.1
108 | */
109 | T putIfAbsent(String key, Object value, Class resultType);
110 |
111 | /**
112 | * 在缓存中删除对应的key
113 | *
114 | * @param key 缓存key
115 | */
116 | void evict(String key);
117 |
118 | /**
119 | * 在缓存中删除对应的keys
120 | *
121 | * @param keys 缓存keys
122 | */
123 | void evictAll(List keys);
124 |
125 | /**
126 | * 清楚缓存
127 | */
128 | void clear();
129 |
130 | /**
131 | * 获取统计信息
132 | *
133 | * @return {@link CacheStats}
134 | */
135 | CacheStats getCacheStats();
136 |
137 | /**
138 | * 缓存预估size
139 | *
140 | * @return 预估大小
141 | */
142 | default long estimatedSize() {
143 | return 0;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/cache/redis/RedisCacheKey.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache.redis;
2 |
3 | import com.github.xiaolyuh.redis.serializer.RedisSerializer;
4 | import com.github.xiaolyuh.redis.serializer.StringRedisSerializer;
5 | import java.util.Arrays;
6 | import org.springframework.util.Assert;
7 |
8 | /**
9 | * redis key 生成
10 | *
11 | * @author yuhao.wang3
12 | */
13 | public class RedisCacheKey {
14 |
15 | /**
16 | * 前缀序列化器
17 | */
18 | private final RedisSerializer prefixSerializer1 = new StringRedisSerializer();
19 |
20 | /**
21 | * 缓存key
22 | */
23 | private final Object keyElement;
24 |
25 | /**
26 | * 缓存名称
27 | */
28 | private String cacheName;
29 |
30 | /**
31 | * 是否使用缓存前缀
32 | */
33 | private boolean usePrefix = true;
34 |
35 | /**
36 | * RedisTemplate 的key序列化器
37 | */
38 | private final RedisSerializer serializer;
39 |
40 | /**
41 | * 缓存预加载时间
42 | */
43 | private long preloadTime;
44 |
45 | /**
46 | * @param keyElement 缓存key
47 | * @param serializer RedisSerializer
48 | */
49 | public RedisCacheKey(Object keyElement, RedisSerializer serializer) {
50 |
51 | Assert.notNull(keyElement, "缓存key不能为NULL");
52 | Assert.notNull(serializer, "key的序列化器不能为NULL");
53 | this.keyElement = keyElement;
54 | this.serializer = serializer;
55 | }
56 |
57 | /**
58 | * 获取缓存key
59 | *
60 | * @return String
61 | */
62 | public String getKey() {
63 |
64 | return new String(getKeyBytes());
65 | }
66 |
67 | /**
68 | * 获取key的byte数组
69 | *
70 | * @return byte[]
71 | */
72 | public byte[] getKeyBytes() {
73 |
74 | byte[] rawKey = serializeKeyElement();
75 | if (!usePrefix) {
76 | return rawKey;
77 | }
78 | byte[] prefix = getPrefix();
79 | byte[] prefixedKey = Arrays.copyOf(prefix, prefix.length + rawKey.length);
80 | System.arraycopy(rawKey, 0, prefixedKey, prefix.length, rawKey.length);
81 |
82 | return prefixedKey;
83 | }
84 |
85 | private byte[] serializeKeyElement() {
86 |
87 | if (serializer == null && keyElement instanceof byte[]) {
88 | return (byte[]) keyElement;
89 | }
90 |
91 | return serializer.serialize(keyElement);
92 | }
93 |
94 | /**
95 | * 获取缓存前缀,默认缓存前缀是":"
96 | *
97 | * @return byte[]
98 | */
99 | public byte[] getPrefix() {
100 | return prefixSerializer1.serialize((cacheName.concat(":")));
101 | }
102 |
103 | /**
104 | * 设置缓存名称
105 | *
106 | * @param cacheName cacheName
107 | * @return RedisCacheKey
108 | */
109 | public RedisCacheKey cacheName(String cacheName) {
110 | this.cacheName = cacheName;
111 | return this;
112 | }
113 |
114 | /**
115 | * 设置预加载时间
116 | *
117 | * @param preloadTime preloadTime
118 | * @return RedisCacheKey
119 | */
120 | public RedisCacheKey preloadTime(long preloadTime) {
121 | this.preloadTime = preloadTime;
122 | return this;
123 | }
124 |
125 | /**
126 | * 设置是否使用缓存前缀,默认使用
127 | *
128 | * @param usePrefix usePrefix
129 | * @return RedisCacheKey
130 | */
131 | public RedisCacheKey usePrefix(boolean usePrefix) {
132 | this.usePrefix = usePrefix;
133 | return this;
134 | }
135 |
136 | public Object getKeyElement() {
137 | return keyElement;
138 | }
139 |
140 | public long getPreloadTime() {
141 | return preloadTime;
142 | }
143 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/listener/RedisMessageListener.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.listener;
2 |
3 | import com.github.xiaolyuh.manager.AbstractCacheManager;
4 | import com.github.xiaolyuh.util.BeanFactory;
5 | import io.lettuce.core.pubsub.RedisPubSubListener;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | /**
10 | * redis消息的订阅者
11 | *
12 | * @author yuhao.wang
13 | */
14 | public class RedisMessageListener implements RedisPubSubListener {
15 | private static final Logger log = LoggerFactory.getLogger(RedisMessageListener.class);
16 |
17 | public static final String CHANNEL = "layering-cache-channel";
18 |
19 | /**
20 | * redis消息处理器
21 | */
22 | private RedisMessageService redisMessageService;
23 |
24 | public void init(AbstractCacheManager cacheManager) {
25 | this.redisMessageService = BeanFactory.getBean(RedisMessageService.class).init(cacheManager);
26 | // 创建监听
27 | cacheManager.getRedisClient().subscribe(this, RedisMessageListener.CHANNEL);
28 | }
29 |
30 | @Override
31 | public void message(String channel, String message) {
32 | try {
33 | if (log.isDebugEnabled()) {
34 | log.debug("redis消息订阅者接收到频道【{}】发布的消息。消息内容:{}", channel, message);
35 | }
36 |
37 | // 更新最后一次处理拉消息的时间
38 | RedisMessageService.updateLastPushTime();
39 |
40 | redisMessageService.pullMessage();
41 | } catch (Exception e) {
42 | log.error("layering-cache 清楚一级缓存异常:{}", e.getMessage(), e);
43 | }
44 | }
45 |
46 | @Override
47 | public void message(String pattern, String channel, String message) {
48 |
49 | }
50 |
51 | @Override
52 | public void subscribed(String channel, long count) {
53 |
54 | }
55 |
56 | @Override
57 | public void psubscribed(String pattern, long count) {
58 |
59 | }
60 |
61 | @Override
62 | public void unsubscribed(String channel, long count) {
63 |
64 | }
65 |
66 | @Override
67 | public void punsubscribed(String pattern, long count) {
68 |
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/listener/RedisMessagePullTask.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.listener;
2 |
3 | import com.github.xiaolyuh.manager.AbstractCacheManager;
4 | import com.github.xiaolyuh.util.BeanFactory;
5 | import com.github.xiaolyuh.util.NamedThreadFactory;
6 | import java.util.Calendar;
7 | import java.util.Random;
8 | import java.util.concurrent.ScheduledThreadPoolExecutor;
9 | import java.util.concurrent.TimeUnit;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | /**
14 | * redis消息拉模式
15 | *
16 | * @author yuhao.wang
17 | */
18 | public class RedisMessagePullTask {
19 | private static final Logger log = LoggerFactory.getLogger(RedisMessagePullTask.class);
20 |
21 | /**
22 | * 定时任务线程池
23 | */
24 | private static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(3, new NamedThreadFactory("layering-cache-pull-message"));
25 |
26 | /**
27 | * redis消息处理器
28 | */
29 | private RedisMessageService redisMessageService;
30 |
31 | public void init(AbstractCacheManager cacheManager) {
32 | Random random = new Random();
33 | int initialDelay = Math.abs(random.nextInt()) % 23 + 1;
34 | int delay = Math.abs(random.nextInt()) % 7 + 1;
35 | log.info("一级缓存拉模式同步消息每隔{}秒,执行一次", delay);
36 | redisMessageService = BeanFactory.getBean(RedisMessageService.class).init(cacheManager);
37 | // 1. 服务启动同步最新的偏移量
38 | BeanFactory.getBean(RedisMessageService.class).syncOffset();
39 | // 2. 启动PULL TASK
40 | startPullTask(initialDelay, delay);
41 | // 3. 启动重置本地偏消息移量任务
42 | clearMessageQueueTask();
43 | // 4. 重连检测
44 | reconnectionTask(initialDelay, delay);
45 | }
46 |
47 | /**
48 | * 启动PULL TASK
49 | */
50 | private void startPullTask(int initialDelay, int delay) {
51 | EXECUTOR.scheduleWithFixedDelay(() -> {
52 | try {
53 | redisMessageService.pullMessage();
54 | } catch (Exception e) {
55 | log.error("layering-cache PULL 方式清楚一级缓存异常:{}", e.getMessage(), e);
56 | }
57 | // 初始时间间隔是7秒
58 | }, initialDelay, delay, TimeUnit.SECONDS);
59 | }
60 |
61 | /**
62 | * 启动清空消息队列的任务
63 | */
64 | private void clearMessageQueueTask() {
65 |
66 | Calendar cal = Calendar.getInstance();
67 | cal.set(Calendar.HOUR_OF_DAY, 3);
68 | cal.set(Calendar.MINUTE, 0);
69 | cal.set(Calendar.SECOND, 0);
70 | long initialDelay = System.currentTimeMillis() - cal.getTimeInMillis();
71 | initialDelay = initialDelay > 0 ? initialDelay : 1;
72 | // 每天晚上凌晨3:00执行任务
73 | EXECUTOR.scheduleWithFixedDelay(() -> {
74 | try {
75 | redisMessageService.clearMessageQueue();
76 | } catch (Exception e) {
77 | e.printStackTrace();
78 | log.error("layering-cache 重置本地消息偏移量异常:{}", e.getMessage(), e);
79 | }
80 | }, initialDelay, TimeUnit.DAYS.toMillis(1), TimeUnit.MILLISECONDS);
81 |
82 | }
83 |
84 | /**
85 | * 启动重连pub/sub检查
86 | */
87 | private void reconnectionTask(int initialDelay, int delay) {
88 | EXECUTOR.scheduleWithFixedDelay(() -> redisMessageService.reconnection(),
89 | initialDelay, delay, TimeUnit.SECONDS);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/listener/RedisPubSubMessage.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.listener;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * redis pub/sub 消息
7 | *
8 | * @author yuhao.wang3
9 | */
10 | public class RedisPubSubMessage implements Serializable {
11 | public static final String SOURCE = "web-manage";
12 |
13 | /**
14 | * 缓存名称
15 | */
16 | private String cacheName;
17 |
18 | /**
19 | * 缓存key
20 | */
21 | private String key;
22 |
23 | /**
24 | * 消息类型
25 | */
26 | private RedisPubSubMessageType messageType;
27 |
28 | /**
29 | * 消息来源
30 | */
31 | private String source;
32 |
33 | public String getCacheName() {
34 | return cacheName;
35 | }
36 |
37 | public void setCacheName(String cacheName) {
38 | this.cacheName = cacheName;
39 | }
40 |
41 | public String getKey() {
42 | return key;
43 | }
44 |
45 | public void setKey(String key) {
46 | this.key = key;
47 | }
48 |
49 | public RedisPubSubMessageType getMessageType() {
50 | return messageType;
51 | }
52 |
53 | public void setMessageType(RedisPubSubMessageType messageType) {
54 | this.messageType = messageType;
55 | }
56 |
57 | public String getSource() {
58 | return source;
59 | }
60 |
61 | public void setSource(String source) {
62 | this.source = source;
63 | }
64 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/listener/RedisPubSubMessageType.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.listener;
2 |
3 | /**
4 | * 消息类型
5 | *
6 | * @author yuhao.wang3
7 | */
8 | public enum RedisPubSubMessageType {
9 | /**
10 | * 删除缓存
11 | */
12 | EVICT("删除缓存"),
13 |
14 | /**
15 | * 清空缓存
16 | */
17 | CLEAR("清空缓存");
18 |
19 | private String label;
20 |
21 | RedisPubSubMessageType(String label) {
22 | this.label = label;
23 | }
24 |
25 | public String getLabel() {
26 | return label;
27 | }
28 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/listener/RedisPublisher.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.listener;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.github.xiaolyuh.redis.clinet.RedisClient;
5 | import com.github.xiaolyuh.util.GlobalConfig;
6 | import java.util.concurrent.TimeUnit;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | /**
11 | * redis消息的发布者
12 | *
13 | * @author yuhao.wang
14 | */
15 | public class RedisPublisher {
16 | private static final Logger logger = LoggerFactory.getLogger(RedisPublisher.class);
17 |
18 | private RedisPublisher() {
19 | }
20 |
21 | /**
22 | * 发布消息到频道(Channel)
23 | *
24 | * @param redisClient redis客户端
25 | * @param message 消息内容
26 | */
27 | public static void publisher(RedisClient redisClient, RedisPubSubMessage message) {
28 | publisher(redisClient, message, GlobalConfig.NAMESPACE);
29 | }
30 |
31 | /**
32 | * 发布消息到频道(Channel)
33 | *
34 | * @param redisClient redis客户端
35 | * @param message 消息内容
36 | * @param nameSpace 命名空间
37 | */
38 | public static void publisher(RedisClient redisClient, RedisPubSubMessage message, String nameSpace) {
39 | String messageJson = JSON.toJSONString(message);
40 | // pull 拉模式消息
41 | redisClient.lpush(GlobalConfig.getMessageRedisKey(nameSpace), GlobalConfig.GLOBAL_REDIS_SERIALIZER, messageJson);
42 | redisClient.expire(GlobalConfig.getMessageRedisKey(nameSpace), 25, TimeUnit.HOURS);
43 | // pub/sub 推模式消息
44 | redisClient.publish(RedisMessageListener.CHANNEL, "m");
45 | if (logger.isDebugEnabled()) {
46 | logger.debug("redis消息发布者向频道【{}】发布了【{}】消息", RedisMessageListener.CHANNEL, messageJson);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/manager/CacheManager.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.manager;
2 |
3 | import com.github.xiaolyuh.cache.Cache;
4 | import com.github.xiaolyuh.setting.LayeringCacheSetting;
5 | import java.util.Collection;
6 |
7 | /**
8 | * 缓存管理器
9 | * 允许通过缓存名称来获的对应的 {@link Cache}.
10 | *
11 | * @author yuhao.wang3
12 | */
13 | public interface CacheManager {
14 |
15 | /**
16 | * 根据缓存名称返回对应的{@link Collection}.
17 | *
18 | * @param name 缓存的名称 (不能为 {@code null})
19 | * @return 返回对应名称的Cache, 如果没找到返回 {@code null}
20 | */
21 | Collection getCache(String name);
22 |
23 | /**
24 | * 根据缓存名称返回对应的{@link Cache},如果没有找到就新建一个并放到容器
25 | *
26 | * @param name 缓存名称
27 | * @param layeringCacheSetting 多级缓存配置
28 | * @return {@link Cache}
29 | */
30 | Cache getCache(String name, LayeringCacheSetting layeringCacheSetting);
31 |
32 | /**
33 | * 获取所有缓存名称的集合
34 | *
35 | * @return 所有缓存名称的集合
36 | */
37 | Collection getCacheNames();
38 | }
39 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/manager/LayeringCacheManager.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.manager;
2 |
3 | import com.github.xiaolyuh.cache.Cache;
4 | import com.github.xiaolyuh.cache.LayeringCache;
5 | import com.github.xiaolyuh.cache.caffeine.CaffeineCache;
6 | import com.github.xiaolyuh.cache.redis.RedisCache;
7 | import com.github.xiaolyuh.redis.clinet.RedisClient;
8 | import com.github.xiaolyuh.setting.LayeringCacheSetting;
9 |
10 | /**
11 | * @author yuhao.wang
12 | */
13 | public class LayeringCacheManager extends AbstractCacheManager {
14 | public LayeringCacheManager(RedisClient redisClient) {
15 | this.redisClient = redisClient;
16 | cacheManagers.add(this);
17 | }
18 |
19 | @Override
20 | protected Cache getMissingCache(String name, LayeringCacheSetting layeringCacheSetting) {
21 | // 创建一级缓存
22 | CaffeineCache caffeineCache = new CaffeineCache(name, layeringCacheSetting.getFirstCacheSetting(), getStats(), layeringCacheSetting.getCacheMode());
23 | // 创建二级缓存
24 | RedisCache redisCache = new RedisCache(name, redisClient, layeringCacheSetting.getSecondaryCacheSetting(), getStats(), layeringCacheSetting.getCacheMode());
25 | return new LayeringCache(redisClient, caffeineCache, redisCache, super.getStats(), layeringCacheSetting);
26 | }
27 |
28 | @Override
29 | public boolean equals(Object o) {
30 | return super.equals(o);
31 | }
32 |
33 | @Override
34 | public int hashCode() {
35 | return super.hashCode();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/clinet/RedisClientException.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.clinet;
2 |
3 | /**
4 | * redis客户端异常
5 | */
6 | public class RedisClientException extends RuntimeException {
7 | public RedisClientException() {
8 | super();
9 | }
10 |
11 | public RedisClientException(String message) {
12 | super(message);
13 | }
14 |
15 | public RedisClientException(String message, Throwable cause) {
16 | super(message, cause);
17 | }
18 |
19 | public RedisClientException(Throwable cause) {
20 | super(cause);
21 | }
22 |
23 | protected RedisClientException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
24 | super(message, cause, enableSuppression, writableStackTrace);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/clinet/RedisProperties.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.clinet;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class RedisProperties {
7 | //********************单机配置项**************************/
8 | /**
9 | * 单机配置项
10 | */
11 | String host = "localhost";
12 | Integer port = 6379;
13 |
14 | //********************集群配置**************************/
15 | /**
16 | * 不为空表示集群版,示例
17 | * localhost:7379,localhost2:7379
18 | */
19 | String cluster = "";
20 |
21 | //********************哨兵配置**************************/
22 | /**
23 | * 哨兵master名称,示例:mymaster
24 | */
25 | String sentinelMaster = "mymaster";
26 |
27 | /**
28 | * 哨兵节点,示例:localhost:26397,localhost2:26397
29 | */
30 | String sentinelNodes = "";
31 |
32 | //********************通用配置**************************/
33 | /**
34 | * 使用数据库
35 | */
36 | Integer database = 0;
37 |
38 | /**
39 | * 密码
40 | */
41 | String password = null;
42 |
43 | /**
44 | * 超时时间 单位秒 默认一个小时
45 | */
46 | Integer timeout = 3600;
47 |
48 | /**
49 | * 序列化方式:
50 | * com.github.xiaolyuh.redis.serializer.KryoRedisSerializer
51 | * com.github.xiaolyuh.redis.serializer.FastJsonRedisSerializer
52 | * com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer
53 | * com.github.xiaolyuh.redis.serializer.JdkRedisSerializer
54 | * com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer
55 | */
56 | String serializer = "com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer";
57 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/command/TencentScan.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.command;
2 |
3 | import io.lettuce.core.dynamic.Commands;
4 | import io.lettuce.core.dynamic.annotation.Command;
5 | import java.util.List;
6 |
7 | /**
8 | * 腾讯云redis scan命令
9 | *
10 | * @author olafwang
11 | */
12 | public interface TencentScan extends Commands {
13 |
14 | @Command("scan ?0 match ?1 count ?2 ?3")
15 | List scan(long cursor, String pattern, int count, String nodeId);
16 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/serializer/AbstractRedisSerializer.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.serializer;
2 |
3 | import com.github.xiaolyuh.support.NullValue;
4 | import java.util.Objects;
5 |
6 | /**
7 | * 序列化方式的抽象实现
8 | *
9 | * @author yuhao.wang
10 | */
11 | public abstract class AbstractRedisSerializer implements RedisSerializer {
12 | private byte[] nullValueBytes;
13 |
14 | /**
15 | * 获取空值的序列化值
16 | *
17 | * @return byte[]
18 | */
19 | public byte[] getNullValueBytes() {
20 | if (Objects.isNull(nullValueBytes)) {
21 | synchronized (this) {
22 | nullValueBytes = serialize(NullValue.INSTANCE);
23 | }
24 | }
25 | return nullValueBytes;
26 | }
27 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/serializer/FastJsonRedisSerializer.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.serializer;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibaba.fastjson.parser.Feature;
5 | import com.alibaba.fastjson.parser.ParserConfig;
6 | import com.alibaba.fastjson.serializer.SerializerFeature;
7 | import com.alibaba.fastjson.util.IOUtils;
8 | import java.util.Arrays;
9 |
10 | /**
11 | * FastJson 序列化方式
12 | *
13 | * @author yuhao.wang
14 | */
15 | public class FastJsonRedisSerializer extends AbstractRedisSerializer {
16 | private static final ParserConfig DEFAULT_REDIS_CONFIG = new ParserConfig();
17 |
18 | static {
19 | DEFAULT_REDIS_CONFIG.setAutoTypeSupport(true);
20 | }
21 |
22 | @Override
23 | public byte[] serialize(T value) throws SerializationException {
24 | if (value == null) {
25 | return SerializationUtils.EMPTY_ARRAY;
26 | }
27 |
28 | try {
29 | return JSON.toJSONBytes(value, SerializerFeature.WriteClassName);
30 | } catch (Exception e) {
31 | throw new SerializationException(String.format("FastJsonRedisSerializer 序列化异常: %s, 【%s】", e.getMessage(), JSON.toJSONString(value)), e);
32 | }
33 | }
34 |
35 | @Override
36 | public T deserialize(byte[] bytes, Class resultType) throws SerializationException {
37 | if (SerializationUtils.isEmpty(bytes)) {
38 | return null;
39 | }
40 |
41 | if (Arrays.equals(getNullValueBytes(), bytes)) {
42 | return null;
43 | }
44 |
45 | try {
46 | return JSON.parseObject(new String(bytes, IOUtils.UTF8), resultType, DEFAULT_REDIS_CONFIG, new Feature[0]);
47 | } catch (Exception e) {
48 | throw new SerializationException(String.format("FastJsonRedisSerializer 反序列化异常: %s, 【%s】", e.getMessage(), JSON.toJSONString(bytes)), e);
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/serializer/JacksonRedisSerializer.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.serializer;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.fasterxml.jackson.annotation.JsonAutoDetect;
5 | import com.fasterxml.jackson.annotation.JsonTypeInfo;
6 | import com.fasterxml.jackson.annotation.PropertyAccessor;
7 | import com.fasterxml.jackson.databind.ObjectMapper;
8 | import com.fasterxml.jackson.databind.SerializationFeature;
9 | import java.util.Arrays;
10 |
11 | /**
12 | * JackJson 序列化方式
13 | *
14 | * @author yuhao.wang
15 | */
16 | public class JacksonRedisSerializer extends AbstractRedisSerializer {
17 | private final ObjectMapper objectMapper = new ObjectMapper();
18 |
19 | {
20 | objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
21 | objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
22 | objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
23 | }
24 |
25 | @Override
26 | public byte[] serialize(T value) throws SerializationException {
27 | if (value == null) {
28 | return SerializationUtils.EMPTY_ARRAY;
29 | }
30 | try {
31 | return this.objectMapper.writeValueAsBytes(value);
32 | } catch (Exception e) {
33 | throw new SerializationException(String.format("JacksonRedisSerializer 序列化异常: %s, 【%s】", e.getMessage(), JSON.toJSONString(value)), e);
34 | }
35 | }
36 |
37 | @Override
38 | public T deserialize(byte[] bytes, Class resultType) throws SerializationException {
39 | if (SerializationUtils.isEmpty(bytes)) {
40 | return null;
41 | }
42 |
43 | if (Arrays.equals(getNullValueBytes(), bytes)) {
44 | return null;
45 | }
46 |
47 | try {
48 | return this.objectMapper.readValue(bytes, 0, bytes.length, resultType);
49 | } catch (Exception e) {
50 | throw new SerializationException(String.format("JacksonRedisSerializer 反序列化异常: %s, 【%s】", e.getMessage(), JSON.toJSONString(bytes)), e);
51 | }
52 |
53 | }
54 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/serializer/JdkRedisSerializer.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.serializer;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import java.io.ByteArrayInputStream;
5 | import java.io.ByteArrayOutputStream;
6 | import java.io.ObjectInputStream;
7 | import java.io.ObjectOutputStream;
8 | import java.io.Serializable;
9 | import java.util.Arrays;
10 |
11 | /**
12 | * JDK 序列化方式
13 | *
14 | * @author yuhao.wang
15 | */
16 | public class JdkRedisSerializer extends AbstractRedisSerializer {
17 | @Override
18 | public byte[] serialize(T value) throws SerializationException {
19 | if (value == null) {
20 | return SerializationUtils.EMPTY_ARRAY;
21 | }
22 |
23 | if (!(value instanceof Serializable)) {
24 | throw new IllegalArgumentException(this.getClass().getSimpleName() + " requires a Serializable payload but received an object of type [" + value.getClass().getName() + "]");
25 | }
26 |
27 | try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
28 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {
29 |
30 | objectOutputStream.writeObject(value);
31 | objectOutputStream.flush();
32 | return outputStream.toByteArray();
33 | } catch (Exception e) {
34 | throw new SerializationException(String.format("JdkRedisSerializer 序列化异常: %s, 【%s】", e.getMessage(), JSON.toJSONString(value)), e);
35 | }
36 | }
37 |
38 | @Override
39 | public T deserialize(byte[] bytes, Class resultType) throws SerializationException {
40 | if (SerializationUtils.isEmpty(bytes)) {
41 | return null;
42 | }
43 |
44 | if (Arrays.equals(getNullValueBytes(), bytes)) {
45 | return null;
46 | }
47 |
48 | try (ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
49 | ObjectInputStream stream = new ObjectInputStream(byteStream)) {
50 | return (T) stream.readObject();
51 | } catch (Exception e) {
52 | throw new SerializationException(String.format("JdkRedisSerializer 反序列化异常: %s, 【%s】", e.getMessage(), JSON.toJSONString(bytes)), e);
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/serializer/KryoRedisSerializer.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.serializer;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.esotericsoftware.kryo.Kryo;
5 | import com.esotericsoftware.kryo.io.Input;
6 | import com.esotericsoftware.kryo.io.Output;
7 | import java.io.ByteArrayOutputStream;
8 | import java.util.Arrays;
9 |
10 | /**
11 | * kryo 序列化方式
12 | *
13 | * @author yuhao.wang
14 | */
15 | public class KryoRedisSerializer extends AbstractRedisSerializer {
16 | private static final ThreadLocal KRYO = ThreadLocal.withInitial(Kryo::new);
17 |
18 | @Override
19 | public byte[] serialize(T t) throws SerializationException {
20 | if (t == null) {
21 | return SerializationUtils.EMPTY_ARRAY;
22 | }
23 |
24 | Kryo kryo = KRYO.get();
25 | // 设置成false 序列化速度更快,但是遇到循环应用序列化器会报栈内存溢出
26 | kryo.setReferences(false);
27 | kryo.register(t.getClass());
28 |
29 | try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
30 | Output output = new Output(baos)) {
31 | kryo.writeClassAndObject(output, t);
32 | output.flush();
33 | return baos.toByteArray();
34 | } catch (Exception e) {
35 | throw new SerializationException(String.format("KryoRedisSerializer 序列化异常: %s, 【%s】", e.getMessage(), JSON.toJSONString(t)), e);
36 | } finally {
37 | KRYO.remove();
38 | }
39 | }
40 |
41 | @Override
42 | public T deserialize(byte[] bytes, Class resultType) throws SerializationException {
43 | if (SerializationUtils.isEmpty(bytes)) {
44 | return null;
45 | }
46 |
47 | if (Arrays.equals(getNullValueBytes(), bytes)) {
48 | return null;
49 | }
50 |
51 | Kryo kryo = KRYO.get();
52 | // 设置成false 序列化速度更快,但是遇到循环应用序列化器会报栈内存溢出
53 | kryo.setReferences(false);
54 | kryo.register(resultType);
55 |
56 | try (Input input = new Input(bytes)) {
57 | Object result = kryo.readClassAndObject(input);
58 | return (T) result;
59 | } catch (Exception e) {
60 | throw new SerializationException(String.format("KryoRedisSerializer 反序列化异常: %s, 【%s】", e.getMessage(), JSON.toJSONString(bytes)), e);
61 | } finally {
62 | KRYO.remove();
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/serializer/ProtostuffRedisSerializer.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.serializer;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import io.protostuff.LinkedBuffer;
5 | import io.protostuff.ProtostuffIOUtil;
6 | import io.protostuff.runtime.DefaultIdStrategy;
7 | import io.protostuff.runtime.IdStrategy;
8 | import io.protostuff.runtime.RuntimeSchema;
9 | import java.util.Arrays;
10 |
11 | /**
12 | * Protostuff 序列化方式
13 | *
14 | * @author yuhao.wang
15 | */
16 | public class ProtostuffRedisSerializer extends AbstractRedisSerializer {
17 | static {
18 | System.getProperties().setProperty("protostuff.runtime.always_use_sun_reflection_factory", "true");
19 | System.getProperties().setProperty("protostuff.runtime.preserve_null_elements", "true");
20 | System.getProperties().setProperty("protostuff.runtime.morph_collection_interfaces", "true");
21 | System.getProperties().setProperty("protostuff.runtime.morph_map_interfaces", "true");
22 | System.getProperties().setProperty("protostuff.runtime.morph_non_final_pojos", "true");
23 | }
24 |
25 | IdStrategy strategy = new DefaultIdStrategy(IdStrategy.DEFAULT_FLAGS | IdStrategy.COLLECTION_SCHEMA_ON_REPEATED_FIELDS, null, 0);
26 | RuntimeSchema schema = RuntimeSchema.createFrom(Wrapper.class, strategy);
27 |
28 | @Override
29 | public byte[] serialize(T value) throws SerializationException {
30 | if (value == null) {
31 | return SerializationUtils.EMPTY_ARRAY;
32 | }
33 |
34 | LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
35 | try {
36 | return ProtostuffIOUtil.toByteArray(new Wrapper<>(value), schema, buffer);
37 | } catch (Exception e) {
38 | throw new SerializationException(String.format("ProtostuffRedisSerializer 序列化异常: %s, 【%s】", e.getMessage(), JSON.toJSONString(value)), e);
39 | } finally {
40 | buffer.clear();
41 | }
42 | }
43 |
44 | @Override
45 | public T deserialize(byte[] bytes, Class resultType) throws SerializationException {
46 | if (SerializationUtils.isEmpty(bytes)) {
47 | return null;
48 | }
49 |
50 | if (Arrays.equals(getNullValueBytes(), bytes)) {
51 | return null;
52 | }
53 |
54 |
55 | try {
56 | Wrapper wrapper = new Wrapper<>(null);
57 | ProtostuffIOUtil.mergeFrom(bytes, wrapper, schema);
58 | return wrapper.getData();
59 | } catch (Exception e) {
60 | throw new SerializationException(String.format("ProtostuffRedisSerializer 反序列化异常: %s, 【%s】", e.getMessage(), JSON.toJSONString(bytes)), e);
61 | }
62 | }
63 |
64 | /**
65 | * protobuff只能序列化pojo类,不能直接序列化List 或者Map,如果要序列化list或者map,需要用一个wrapper类包装一下
66 | *
67 | * @param T
68 | */
69 | static class Wrapper {
70 | T data;
71 |
72 | public Wrapper(T data) {
73 | this.data = data;
74 | }
75 |
76 | public T getData() {
77 | return data;
78 | }
79 | }
80 |
81 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/serializer/RedisSerializer.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.serializer;
2 |
3 | import org.springframework.lang.Nullable;
4 |
5 | /**
6 | * redis序列化
7 | *
8 | * @author olafwang
9 | * @since 2020/6/29 3:25 下午
10 | */
11 | public interface RedisSerializer {
12 | /**
13 | * 将给定对象序列化为二进制数据。
14 | *
15 | * @param value 需要序列化的对象.允许为 {@literal null}.
16 | * @param T
17 | * @return 返回对象的二进制数据. 允许为 {@literal null}.
18 | * @throws SerializationException 序列化异常
19 | */
20 | @Nullable
21 | byte[] serialize(T value) throws SerializationException;
22 |
23 | /**
24 | * 将给定的二进制数据中反序列化对象。
25 | *
26 | * @param bytes 给定的二进制数据. 允许为 {@literal null}.
27 | * @param resultType 返回值类型
28 | * @param T
29 | * @return 反序列化后的对象.允许为 {@literal null}.
30 | * @throws SerializationException 序列化异常
31 | */
32 | @Nullable
33 | T deserialize(@Nullable byte[] bytes, Class resultType) throws SerializationException;
34 | }
35 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/serializer/SerializationException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011-2013 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.xiaolyuh.redis.serializer;
17 |
18 |
19 | import com.github.xiaolyuh.support.NestedRuntimeException;
20 |
21 | /**
22 | * Generic exception indicating a serialization/deserialization error.
23 | *
24 | * @author Costin Leau
25 | */
26 | public class SerializationException extends NestedRuntimeException {
27 |
28 | /**
29 | * Constructs a new SerializationException
instance.
30 | *
31 | * @param msg msg
32 | * @param cause 原因
33 | */
34 | public SerializationException(String msg, Throwable cause) {
35 | super(msg, cause);
36 | }
37 |
38 | /**
39 | * Constructs a new SerializationException
instance.
40 | *
41 | * @param msg msg
42 | */
43 | public SerializationException(String msg) {
44 | super(msg);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/serializer/SerializationUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.serializer;
2 |
3 | /**
4 | * 序列化工具类
5 | *
6 | * @author yuhao.wang3
7 | */
8 | public abstract class SerializationUtils {
9 |
10 | static final byte[] EMPTY_ARRAY = new byte[0];
11 |
12 | static boolean isEmpty(byte[] data) {
13 | return (data == null || data.length == 0);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/redis/serializer/StringRedisSerializer.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.redis.serializer;
2 |
3 | import java.nio.charset.Charset;
4 | import java.nio.charset.StandardCharsets;
5 | import org.springframework.util.Assert;
6 |
7 | /**
8 | * 必须重写序列化器,否则@Cacheable注解的key会报类型转换错误
9 | *
10 | * @author yuhao.wang
11 | */
12 | public class StringRedisSerializer implements RedisSerializer {
13 |
14 | private final Charset charset;
15 |
16 | public StringRedisSerializer() {
17 | this(StandardCharsets.UTF_8);
18 | }
19 |
20 | public StringRedisSerializer(Charset charset) {
21 | Assert.notNull(charset, "Charset must not be null!");
22 | this.charset = charset;
23 | }
24 |
25 | @Override
26 | public byte[] serialize(T value) throws SerializationException {
27 | if (value == null) {
28 | return null;
29 | }
30 |
31 | if (value instanceof String) {
32 | return ((String) value).getBytes(charset);
33 | }
34 | throw new UnsupportedOperationException("String序列化方式不支持其他数据类型的序列化");
35 | }
36 |
37 | @Override
38 | public String deserialize(byte[] bytes, Class resultType) throws SerializationException {
39 |
40 | return (bytes == null ? null : new String(bytes, charset));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/setting/FirstCacheSetting.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.setting;
2 |
3 | import com.github.xiaolyuh.support.ExpireMode;
4 | import java.io.Serializable;
5 | import java.util.concurrent.TimeUnit;
6 |
7 | /**
8 | * 一级缓存配置项
9 | *
10 | * @author yuhao.wang
11 | */
12 | public class FirstCacheSetting implements Serializable {
13 |
14 | /**
15 | * 缓存初始Size
16 | */
17 | private int initialCapacity = 10;
18 |
19 | /**
20 | * 缓存最大Size
21 | */
22 | private int maximumSize = 500;
23 |
24 | /**
25 | * 缓存有效时间
26 | */
27 | private int expireTime = 0;
28 |
29 | /**
30 | * 缓存时间单位
31 | */
32 | private TimeUnit timeUnit = TimeUnit.MILLISECONDS;
33 |
34 | /**
35 | * 缓存失效模式{@link ExpireMode}
36 | */
37 | private ExpireMode expireMode = ExpireMode.WRITE;
38 |
39 | public FirstCacheSetting() {
40 | }
41 |
42 | /**
43 | * @param initialCapacity 缓存初始Size
44 | * @param maximumSize 缓存最大Size
45 | * @param expireTime 缓存有效时间
46 | * @param timeUnit 缓存时间单位 {@link TimeUnit}
47 | * @param expireMode 缓存失效模式{@link ExpireMode}
48 | */
49 | public FirstCacheSetting(int initialCapacity, int maximumSize, int expireTime, TimeUnit timeUnit, ExpireMode expireMode) {
50 | this.initialCapacity = initialCapacity;
51 | this.maximumSize = maximumSize;
52 | this.expireTime = expireTime;
53 | this.timeUnit = timeUnit;
54 | this.expireMode = expireMode;
55 | }
56 |
57 | public int getInitialCapacity() {
58 | return initialCapacity;
59 | }
60 |
61 | public void setInitialCapacity(int initialCapacity) {
62 | this.initialCapacity = initialCapacity;
63 | }
64 |
65 | public int getMaximumSize() {
66 | return maximumSize;
67 | }
68 |
69 | public void setMaximumSize(int maximumSize) {
70 | this.maximumSize = maximumSize;
71 | }
72 |
73 | public int getExpireTime() {
74 | return expireTime;
75 | }
76 |
77 | public void setExpireTime(int expireTime) {
78 | this.expireTime = expireTime;
79 | }
80 |
81 | public TimeUnit getTimeUnit() {
82 | return timeUnit;
83 | }
84 |
85 | public void setTimeUnit(TimeUnit timeUnit) {
86 | this.timeUnit = timeUnit;
87 | }
88 |
89 | public ExpireMode getExpireMode() {
90 | return expireMode;
91 | }
92 |
93 | public void setExpireMode(ExpireMode expireMode) {
94 | this.expireMode = expireMode;
95 | }
96 |
97 | public boolean isAllowNullValues() {
98 | return false;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/setting/LayeringCacheSetting.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.setting;
2 |
3 | import com.alibaba.fastjson.annotation.JSONField;
4 | import com.github.xiaolyuh.support.CacheMode;
5 | import java.io.Serializable;
6 |
7 | /**
8 | * 多级缓存配置项
9 | *
10 | * @author yuhao.wang
11 | */
12 | public class LayeringCacheSetting implements Serializable {
13 | private static final String SPLIT = "-";
14 | /**
15 | * 内部缓存名,由[一级缓存有效时间-二级缓存有效时间]组成
16 | */
17 | private String internalKey;
18 |
19 | /**
20 | * 描述,数据监控页面使用
21 | */
22 | private String depict;
23 |
24 | /**
25 | * 是否使用一级缓存
26 | */
27 | CacheMode cacheMode = CacheMode.ALL;
28 |
29 | /**
30 | * 一级缓存配置
31 | */
32 | private FirstCacheSetting firstCacheSetting;
33 |
34 | /**
35 | * 二级缓存配置
36 | */
37 | private SecondaryCacheSetting secondaryCacheSetting;
38 |
39 | public LayeringCacheSetting() {
40 | }
41 |
42 | public LayeringCacheSetting(FirstCacheSetting firstCacheSetting, SecondaryCacheSetting secondaryCacheSetting,
43 | String depict, CacheMode cacheMode) {
44 | this.firstCacheSetting = firstCacheSetting;
45 | this.secondaryCacheSetting = secondaryCacheSetting;
46 | this.depict = depict;
47 | this.cacheMode = cacheMode;
48 | internalKey();
49 | }
50 |
51 | @JSONField(serialize = false, deserialize = false)
52 | private void internalKey() {
53 | // 一级缓存有效时间-二级缓存有效时间
54 | StringBuilder sb = new StringBuilder();
55 | if (firstCacheSetting != null) {
56 | sb.append(firstCacheSetting.getTimeUnit().toMillis(firstCacheSetting.getExpireTime()));
57 | }
58 | if (secondaryCacheSetting != null) {
59 | sb.append(SPLIT);
60 | sb.append(secondaryCacheSetting.getTimeUnit().toMillis(secondaryCacheSetting.getExpiration()));
61 | }
62 | internalKey = sb.toString();
63 | }
64 |
65 | public FirstCacheSetting getFirstCacheSetting() {
66 | return firstCacheSetting;
67 | }
68 |
69 | public SecondaryCacheSetting getSecondaryCacheSetting() {
70 | return secondaryCacheSetting;
71 | }
72 |
73 | public String getInternalKey() {
74 | return internalKey;
75 | }
76 |
77 | public CacheMode getCacheMode() {
78 | return cacheMode;
79 | }
80 |
81 | public void internalKey(String internalKey) {
82 | this.internalKey = internalKey;
83 | }
84 |
85 | public void setFirstCacheSetting(FirstCacheSetting firstCacheSetting) {
86 | this.firstCacheSetting = firstCacheSetting;
87 | }
88 |
89 | public void setSecondaryCacheSetting(SecondaryCacheSetting secondaryCacheSetting) {
90 | this.secondaryCacheSetting = secondaryCacheSetting;
91 | }
92 |
93 | public void setInternalKey(String internalKey) {
94 | this.internalKey = internalKey;
95 | }
96 |
97 | public String getDepict() {
98 | return depict;
99 | }
100 |
101 | public void setDepict(String depict) {
102 | this.depict = depict;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/setting/SecondaryCacheSetting.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.setting;
2 |
3 | import java.io.Serializable;
4 | import java.util.concurrent.TimeUnit;
5 |
6 | /**
7 | * 二级缓存配置项
8 | *
9 | * @author yuhao.wang
10 | */
11 | public class SecondaryCacheSetting implements Serializable {
12 | /**
13 | * 缓存有效时间
14 | */
15 | private long expiration = 0;
16 |
17 | /**
18 | * 缓存主动在失效前强制刷新缓存的时间
19 | */
20 | private long preloadTime = 0;
21 |
22 | /**
23 | * 时间单位 {@link TimeUnit}
24 | */
25 | private TimeUnit timeUnit = TimeUnit.MICROSECONDS;
26 |
27 | /**
28 | * 是否强制刷新(走数据库),默认是false
29 | */
30 | private boolean forceRefresh = false;
31 |
32 | /**
33 | * 是否使用缓存名称作为 redis key 前缀
34 | */
35 | private boolean usePrefix = true;
36 |
37 | /**
38 | * 是否允许存NULL值
39 | */
40 | boolean allowNullValue = false;
41 |
42 | /**
43 | * 非空值和null值之间的时间倍率,默认是1。allowNullValue=true才有效
44 | *
45 | * 如配置缓存的有效时间是200秒,倍率这设置成10,
46 | * 那么当缓存value为null时,缓存的有效时间将是20秒,非空时为200秒
47 | *
48 | */
49 | int magnification = 1;
50 |
51 | public SecondaryCacheSetting() {
52 | }
53 |
54 | /**
55 | * @param expiration 缓存有效时间
56 | * @param preloadTime 缓存刷新时间
57 | * @param timeUnit 时间单位 {@link TimeUnit}
58 | * @param forceRefresh 是否强制刷新
59 | * @param allowNullValues 是否允许存NULL值,模式允许
60 | * @param magnification 非空值和null值之间的时间倍率
61 | */
62 | public SecondaryCacheSetting(long expiration, long preloadTime, TimeUnit timeUnit, boolean forceRefresh,
63 | boolean allowNullValues, int magnification) {
64 | this.expiration = expiration;
65 | this.preloadTime = preloadTime;
66 | this.timeUnit = timeUnit;
67 | this.forceRefresh = forceRefresh;
68 | this.allowNullValue = allowNullValues;
69 | this.magnification = magnification;
70 | this.usePrefix = true;
71 | }
72 |
73 | public long getExpiration() {
74 | return expiration;
75 | }
76 |
77 | public void setExpiration(long expiration) {
78 | this.expiration = expiration;
79 | }
80 |
81 | public long getPreloadTime() {
82 | return preloadTime;
83 | }
84 |
85 | public void setPreloadTime(long preloadTime) {
86 | this.preloadTime = preloadTime;
87 | }
88 |
89 | public TimeUnit getTimeUnit() {
90 | return timeUnit;
91 | }
92 |
93 | public void setTimeUnit(TimeUnit timeUnit) {
94 | this.timeUnit = timeUnit;
95 | }
96 |
97 | public boolean isForceRefresh() {
98 | return forceRefresh;
99 | }
100 |
101 | public void setForceRefresh(boolean forceRefresh) {
102 | this.forceRefresh = forceRefresh;
103 | }
104 |
105 | public boolean isUsePrefix() {
106 | return usePrefix;
107 | }
108 |
109 | public void setUsePrefix(boolean usePrefix) {
110 | this.usePrefix = usePrefix;
111 | }
112 |
113 | public boolean isAllowNullValue() {
114 | return allowNullValue;
115 | }
116 |
117 | public void setAllowNullValue(boolean allowNullValue) {
118 | this.allowNullValue = allowNullValue;
119 | }
120 |
121 | public int getMagnification() {
122 | return magnification;
123 | }
124 |
125 | public void setMagnification(int magnification) {
126 | this.magnification = magnification;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/stats/CacheStats.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.stats;
2 |
3 | import java.io.Serializable;
4 | import java.util.concurrent.atomic.LongAdder;
5 |
6 | /**
7 | * 缓存统计信息实体类
8 | *
9 | * @author yuhao.wang3
10 | */
11 | public final class CacheStats implements Serializable {
12 | /**
13 | * 请求缓存总数
14 | */
15 | private LongAdder cacheRequestCount;
16 |
17 | /**
18 | * 请求被缓存方法总数
19 | */
20 | private LongAdder cachedMethodRequestCount;
21 |
22 | /**
23 | * 请求被缓存方法总耗时(毫秒)
24 | */
25 | private LongAdder cachedMethodRequestTime;
26 |
27 | public CacheStats() {
28 | this.cacheRequestCount = new LongAdder();
29 | this.cachedMethodRequestCount = new LongAdder();
30 | this.cachedMethodRequestTime = new LongAdder();
31 | }
32 |
33 | /**
34 | * 自增请求缓存总数
35 | *
36 | * @param add 自增数量
37 | */
38 | public void addCacheRequestCount(long add) {
39 | cacheRequestCount.add(add);
40 | }
41 |
42 | /**
43 | * 自增请求被缓存方法总数
44 | *
45 | * @param add 自增数量
46 | */
47 | public void addCachedMethodRequestCount(long add) {
48 | cachedMethodRequestCount.add(add);
49 | }
50 |
51 | /**
52 | * 自增请求被缓存方法总耗时(毫秒)
53 | *
54 | * @param time 自增数量
55 | */
56 | public void addCachedMethodRequestTime(long time) {
57 | cachedMethodRequestTime.add(time);
58 | }
59 |
60 | public LongAdder getCacheRequestCount() {
61 | return cacheRequestCount;
62 | }
63 |
64 | public void setCacheRequestCount(LongAdder cacheRequestCount) {
65 | this.cacheRequestCount = cacheRequestCount;
66 | }
67 |
68 | public LongAdder getCachedMethodRequestCount() {
69 | return cachedMethodRequestCount;
70 | }
71 |
72 | public void setCachedMethodRequestCount(LongAdder cachedMethodRequestCount) {
73 | this.cachedMethodRequestCount = cachedMethodRequestCount;
74 | }
75 |
76 | public LongAdder getCachedMethodRequestTime() {
77 | return cachedMethodRequestTime;
78 | }
79 |
80 | public void setCachedMethodRequestTime(LongAdder cachedMethodRequestTime) {
81 | this.cachedMethodRequestTime = cachedMethodRequestTime;
82 | }
83 |
84 |
85 | public long getAndResetCacheRequestCount() {
86 | long lodValue = cacheRequestCount.longValue();
87 | cacheRequestCount.reset();
88 | return lodValue;
89 | }
90 |
91 | public long getAndResetCachedMethodRequestCount() {
92 | long lodValue = cachedMethodRequestCount.longValue();
93 | cachedMethodRequestCount.reset();
94 | return lodValue;
95 | }
96 |
97 | public long getAndResetCachedMethodRequestTime() {
98 | long lodValue = cachedMethodRequestTime.longValue();
99 | cachedMethodRequestTime.reset();
100 | return lodValue;
101 | }
102 |
103 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/stats/extend/CacheStatsReportService.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.stats.extend;
2 |
3 | import com.github.xiaolyuh.stats.CacheStatsInfo;
4 | import java.util.List;
5 |
6 | /**
7 | * 缓存统计信息上报扩展类
8 | *
9 | * @author olafwang
10 | */
11 | public interface CacheStatsReportService {
12 |
13 | /**
14 | * 上报缓存统计信息
15 | *
16 | * @param cacheStatsInfos {@link CacheStatsInfo}
17 | */
18 | void reportCacheStats(List cacheStatsInfos);
19 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/stats/extend/DefaultCacheStatsReportServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.stats.extend;
2 |
3 | import com.github.xiaolyuh.stats.CacheStatsInfo;
4 | import java.util.List;
5 |
6 | /**
7 | * 缓存统计信息上报扩展类
8 | *
9 | * @author olafwang
10 | */
11 | public class DefaultCacheStatsReportServiceImpl implements CacheStatsReportService {
12 |
13 | @Override
14 | public void reportCacheStats(List cacheStatsInfos) {
15 |
16 | }
17 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/support/AwaitThreadContainer.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.support;
2 |
3 | import java.util.Comparator;
4 | import java.util.Map;
5 | import java.util.Set;
6 | import java.util.concurrent.ConcurrentHashMap;
7 | import java.util.concurrent.ConcurrentSkipListSet;
8 | import java.util.concurrent.TimeUnit;
9 | import java.util.concurrent.locks.LockSupport;
10 | import org.springframework.util.CollectionUtils;
11 |
12 | /**
13 | * 等待线程容器
14 | *
15 | * @author yuhao.wang
16 | */
17 | public class AwaitThreadContainer {
18 | private final Map> threadMap = new ConcurrentHashMap<>();
19 |
20 | /**
21 | * 线程等待,最大等待100毫秒
22 | *
23 | * @param key 缓存Key
24 | * @param milliseconds 等待时间
25 | * @throws InterruptedException {@link InterruptedException}
26 | */
27 | public final void await(String key, long milliseconds) throws InterruptedException {
28 | // 测试当前线程是否已经被中断
29 | if (Thread.interrupted()) {
30 | throw new InterruptedException();
31 | }
32 | Set threadSet = threadMap.get(key);
33 | // 判断线程容器是否是null,如果是就新创建一个
34 | if (threadSet == null) {
35 | threadSet = new ConcurrentSkipListSet<>(Comparator.comparing(Thread::toString));
36 | threadMap.put(key, threadSet);
37 | }
38 | // 将线程放到容器
39 | threadSet.add(Thread.currentThread());
40 | // 阻塞一定的时间
41 | LockSupport.parkNanos(this, TimeUnit.MILLISECONDS.toNanos(milliseconds));
42 | }
43 |
44 | /**
45 | * 线程唤醒
46 | *
47 | * @param key key
48 | */
49 | public final void signalAll(String key) {
50 | Set threadSet = threadMap.get(key);
51 | // 判断key所对应的等待线程容器是否是null
52 | if (!CollectionUtils.isEmpty(threadSet)) {
53 | synchronized (threadSet) {
54 | if (!CollectionUtils.isEmpty(threadSet)) {
55 | for (Thread thread : threadSet) {
56 | LockSupport.unpark(thread);
57 | }
58 | // 清空等待线程容器
59 | threadSet.clear();
60 | }
61 | }
62 | }
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/support/CacheMode.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.support;
2 |
3 | /**
4 | * 缓存模式
5 | *
6 | * @author yuhao.wang3
7 | */
8 | public enum CacheMode {
9 | /**
10 | * 只开启一级缓存
11 | */
12 | FIRST("只是用一级缓存"),
13 |
14 | /**
15 | * 只开启二级缓存
16 | */
17 | SECOND("只是使用二级缓存"),
18 |
19 | /**
20 | * 同时开启一级缓存和二级缓存
21 | */
22 | ALL("同时开启一级缓存和二级缓存");
23 |
24 | private String label;
25 |
26 | CacheMode(String label) {
27 | this.label = label;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/support/ExpireMode.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.support;
2 |
3 | /**
4 | * 缓存失效模式
5 | *
6 | * @author yuhao.wang3
7 | */
8 | public enum ExpireMode {
9 | /**
10 | * 每写入一次重新计算一次缓存的有效时间
11 | */
12 | WRITE("最后一次写入后到期失效"),
13 |
14 | /**
15 | * 每访问一次重新计算一次缓存的有效时间
16 | */
17 | ACCESS("最后一次访问后到期失效");
18 |
19 | private String label;
20 |
21 | ExpireMode(String label) {
22 | this.label = label;
23 | }
24 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/support/MdcThreadPoolTaskExecutor.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.support;
2 |
3 | import java.util.Map;
4 | import org.slf4j.MDC;
5 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
6 |
7 | /**
8 | * 这是{@link ThreadPoolTaskExecutor}的一个简单替换,可以在每个任务之前设置子线程的MDC数据。
9 | *
10 | * 在记录日志的时候,一般情况下我们会使用MDC来存储每个线程的特有参数,如身份信息等,以便更好的查询日志。
11 | * 但是Logback在最新的版本中因为性能问题,不会自动的将MDC的内存传给子线程。所以Logback建议在执行异步线程前
12 | * 先通过MDC.getCopyOfContextMap()方法将MDC内存获取出来,再传给线程。
13 | * 并在子线程的执行的最开始调用MDC.setContextMap(context)方法将父线程的MDC内容传给子线程。
14 | *
15 | * https://logback.qos.ch/manual/mdc.html
16 | *
17 | * @author yuhao.wang3
18 | */
19 | public class MdcThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
20 |
21 | /**
22 | * 所有线程都会委托给这个execute方法,在这个方法中我们把父线程的MDC内容赋值给子线程
23 | * https://logback.qos.ch/manual/mdc.html#managedThreads
24 | *
25 | * @param runnable runnable
26 | */
27 | @Override
28 | public void execute(Runnable runnable) {
29 | // 获取父线程MDC中的内容,必须在run方法之前,否则等异步线程执行的时候有可能MDC里面的值已经被清空了,这个时候就会返回null
30 | Map context = MDC.getCopyOfContextMap();
31 | super.execute(() -> run(runnable, context));
32 | }
33 |
34 | /**
35 | * 子线程委托的执行方法
36 | *
37 | * @param runnable {@link Runnable}
38 | * @param context 父线程MDC内容
39 | */
40 | private void run(Runnable runnable, Map context) {
41 | // 将父线程的MDC内容传给子线程
42 | if (context != null) {
43 | try {
44 | MDC.setContextMap(context);
45 | } catch (Exception e) {
46 | logger.error(e.getMessage(), e);
47 | }
48 | }
49 | try {
50 | // 执行异步操作
51 | runnable.run();
52 | } finally {
53 | // 清空MDC内容
54 | MDC.clear();
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/support/NullValue.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2002-2015 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.xiaolyuh.support;
18 |
19 | import com.github.xiaolyuh.cache.AbstractValueAdaptingCache;
20 | import java.io.Serializable;
21 |
22 | /**
23 | * 空值标识
24 | *
25 | * @author olafwang
26 | * @see AbstractValueAdaptingCache
27 | */
28 | public final class NullValue implements Serializable {
29 |
30 | public static final Object INSTANCE = new NullValue();
31 |
32 | private static final long serialVersionUID = 1L;
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/support/Type.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.support;
2 |
3 | /**
4 | * 对象类型
5 | *
6 | * @author yuhao.wang3
7 | */
8 | public enum Type {
9 | /**
10 | * null
11 | */
12 | NULL("null"),
13 |
14 | /**
15 | * string
16 | */
17 | STRING("string"),
18 |
19 | /**
20 | * object
21 | */
22 | OBJECT("Object 对象"),
23 |
24 | /**
25 | * List集合
26 | */
27 | LIST("List集合"),
28 |
29 | /**
30 | * Set集合
31 | */
32 | SET("Set集合"),
33 |
34 | /**
35 | * 数组
36 | */
37 | ARRAY("数组"),
38 |
39 | /**
40 | * 枚举
41 | */
42 | ENUM("枚举"),
43 |
44 | /**
45 | * 其他类型
46 | */
47 | OTHER("其他类型");
48 |
49 | private String label;
50 |
51 | Type(String label) {
52 | this.label = label;
53 | }
54 |
55 | public static Type parse(String name) {
56 | for (Type type : Type.values()) {
57 | if (type.name().equals(name)) {
58 | return type;
59 | }
60 | }
61 | return OTHER;
62 | }
63 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/util/BeanFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.util;
2 |
3 | import java.util.concurrent.ConcurrentHashMap;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | /**
8 | * Bean 工厂类
9 | *
10 | * @author yuhao.wang3
11 | */
12 | public class BeanFactory {
13 | private static Logger logger = LoggerFactory.getLogger(BeanFactory.class);
14 |
15 | /**
16 | * bean 容器
17 | */
18 | private static ConcurrentHashMap beanContainer = new ConcurrentHashMap<>();
19 |
20 | public static T getBean(Class aClass) {
21 | return (T) beanContainer.computeIfAbsent(aClass, aClass1 -> {
22 | try {
23 | return aClass1.newInstance();
24 | } catch (Exception e) {
25 | logger.error(e.getMessage(), e);
26 | }
27 | return null;
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/util/GlobalConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.util;
2 |
3 | import com.github.xiaolyuh.redis.serializer.JdkRedisSerializer;
4 | import com.github.xiaolyuh.redis.serializer.RedisSerializer;
5 |
6 | /**
7 | * 全局配置
8 | *
9 | * @author yuhao.wang3
10 | */
11 | public class GlobalConfig {
12 | public static final String MESSAGE_KEY = "layering-cache:message-key:%s";
13 |
14 | public static String NAMESPACE = "";
15 |
16 | public static void setNamespace(String namespace) {
17 | GlobalConfig.NAMESPACE = namespace;
18 | }
19 |
20 | public static String getMessageRedisKey() {
21 | return String.format(MESSAGE_KEY, GlobalConfig.NAMESPACE);
22 | }
23 |
24 | public static String getMessageRedisKey(String nameSpace) {
25 | return String.format(MESSAGE_KEY, nameSpace);
26 | }
27 |
28 | /**
29 | * 缓存统计和消息推送序列化器
30 | */
31 | public static final RedisSerializer GLOBAL_REDIS_SERIALIZER = new JdkRedisSerializer();
32 | }
33 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/util/NamedThreadFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.util;
2 |
3 | import java.util.concurrent.ThreadFactory;
4 | import java.util.concurrent.atomic.AtomicInteger;
5 |
6 | /**
7 | * 设置线程名称的ThreadFactory
8 | *
9 | * @author olafwang
10 | */
11 | public class NamedThreadFactory implements ThreadFactory {
12 |
13 | private final AtomicInteger poolNumber = new AtomicInteger(1);
14 |
15 | private final ThreadGroup threadGroup;
16 |
17 | private final AtomicInteger threadNumber = new AtomicInteger(1);
18 |
19 | public final String namePrefix;
20 |
21 | public NamedThreadFactory(String name) {
22 | SecurityManager s = System.getSecurityManager();
23 | threadGroup = (s != null) ? s.getThreadGroup() :
24 | Thread.currentThread().getThreadGroup();
25 | if (null == name || "".equals(name.trim())) {
26 | name = "pool";
27 | }
28 | namePrefix = name + "-" + poolNumber.getAndIncrement() + "-thread-";
29 | }
30 |
31 | @Override
32 | public Thread newThread(Runnable runnable) {
33 | Thread thread = new Thread(threadGroup, runnable, namePrefix + threadNumber.getAndIncrement(), 0);
34 | if (thread.isDaemon()) {
35 | thread.setDaemon(false);
36 | }
37 | if (thread.getPriority() != Thread.NORM_PRIORITY) {
38 | thread.setPriority(Thread.NORM_PRIORITY);
39 | }
40 | return thread;
41 | }
42 | }
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/util/RandomUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.util;
2 |
3 | import java.util.concurrent.ThreadLocalRandom;
4 |
5 | /**
6 | * RandomUtils
7 | *
8 | * @author heyirui
9 | */
10 | public class RandomUtils {
11 |
12 |
13 | /**
14 | * 生成带有随机浮动的预加载时间
15 | *
16 | * @param basePreloadTime basePreloadTime
17 | * @return 带有随机浮动的预加载时间
18 | */
19 | public static long getRandomPreloadTime(long basePreloadTime) {
20 | double randomFactor = ThreadLocalRandom.current().nextDouble(0.9, 1.1);
21 | return (long) (basePreloadTime * randomFactor);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/util/ThreadTaskUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.util;
2 |
3 | import com.github.xiaolyuh.support.MdcThreadPoolTaskExecutor;
4 | import java.util.concurrent.ThreadPoolExecutor;
5 |
6 | /**
7 | * 线程池
8 | *
9 | * @author yuhao.wang3
10 | */
11 | public class ThreadTaskUtils {
12 | private static MdcThreadPoolTaskExecutor refreshTaskExecutor = null;
13 | private static MdcThreadPoolTaskExecutor deleteTaskExecutor = null;
14 |
15 | static {
16 | refreshTaskExecutor = new MdcThreadPoolTaskExecutor();
17 | // 核心线程数
18 | refreshTaskExecutor.setCorePoolSize(8);
19 | // 最大线程数
20 | refreshTaskExecutor.setMaxPoolSize(64);
21 | // 队列最大长度
22 | refreshTaskExecutor.setQueueCapacity(1000);
23 | // 线程池维护线程所允许的空闲时间(单位秒)
24 | refreshTaskExecutor.setKeepAliveSeconds(120);
25 | refreshTaskExecutor.setThreadNamePrefix("layering-cache-refresh-thread");
26 | /*
27 | * 线程池对拒绝任务(无限程可用)的处理策略
28 | * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
29 | * ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
30 | * ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
31 | * ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务,如果执行器已关闭,则丢弃.
32 | */
33 | refreshTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
34 | refreshTaskExecutor.initialize();
35 |
36 |
37 | deleteTaskExecutor = new MdcThreadPoolTaskExecutor();
38 | // 核心线程数
39 | deleteTaskExecutor.setCorePoolSize(8);
40 | // 最大线程数
41 | deleteTaskExecutor.setMaxPoolSize(64);
42 | // 队列最大长度
43 | deleteTaskExecutor.setQueueCapacity(1000);
44 | // 线程池维护线程所允许的空闲时间(单位秒)
45 | deleteTaskExecutor.setKeepAliveSeconds(120);
46 | deleteTaskExecutor.setThreadNamePrefix("layering-cache-delete-thread");
47 | deleteTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
48 | deleteTaskExecutor.initialize();
49 | }
50 |
51 | public static void refreshCacheRun(Runnable runnable) {
52 | refreshTaskExecutor.execute(runnable);
53 | }
54 |
55 | public static void deleteCacheRun(Runnable runnable) {
56 | deleteTaskExecutor.execute(runnable);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/layering-cache-core/src/main/java/com/github/xiaolyuh/util/ToStringUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.util;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import java.util.Objects;
5 |
6 | /**
7 | * 将一个Object转换成String
8 | *
9 | * @author yuhao.wang3
10 | */
11 | public class ToStringUtils {
12 |
13 | /**
14 | * 将一个Object转换成String
15 | *
16 | * @param object Object
17 | * @return String
18 | */
19 | public static String toString(final Object object) {
20 | if (Objects.isNull(object)) {
21 | return null;
22 | }
23 | if (object instanceof String) {
24 | return (String) object;
25 | }
26 | String string = JSON.toJSONString(object);
27 | return string.replace("\"", "").replace("{", "").replace("}", "");
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/layering-cache-core/src/test/java/com/github/xiaolyuh/cache/MainTest.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.github.xiaolyuh.redis.serializer.FastJsonRedisSerializer;
5 | import com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer;
6 | import com.github.xiaolyuh.redis.serializer.JdkRedisSerializer;
7 | import com.github.xiaolyuh.redis.serializer.KryoRedisSerializer;
8 | import com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer;
9 | import com.github.xiaolyuh.support.NullValue;
10 |
11 | import java.util.Arrays;
12 |
13 | /**
14 | * @author olafwang
15 | * @since 2020/9/25 4:10 下午
16 | */
17 | public class MainTest {
18 | public static void main(String[] args) {
19 | double b = 0.0f / 0.0;
20 | System.out.println(Double.isNaN(b));
21 | System.out.println(Double.isInfinite(b));
22 | System.out.println(Double.isInfinite(1.0));
23 | System.out.println(JSON.toJSONString(null));
24 | System.out.println(JSON.toJSONString(new NullValue()));
25 |
26 | KryoRedisSerializer kryoRedisSerializer = new KryoRedisSerializer();
27 | System.out.println("KryoRedisSerializer:" + Arrays.toString(kryoRedisSerializer.getNullValueBytes()));
28 |
29 | FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer();
30 | System.out.println("FastJsonRedisSerializer:" + Arrays.toString(fastJsonRedisSerializer.getNullValueBytes()));
31 |
32 | JacksonRedisSerializer jacksonRedisSerializer = new JacksonRedisSerializer();
33 | System.out.println("JacksonRedisSerializer:" + Arrays.toString(jacksonRedisSerializer.getNullValueBytes()));
34 |
35 | JdkRedisSerializer jdkRedisSerializer = new JdkRedisSerializer();
36 | System.out.println("JdkRedisSerializer:" + Arrays.toString(jdkRedisSerializer.getNullValueBytes()));
37 |
38 | ProtostuffRedisSerializer protostuffRedisSerializer = new ProtostuffRedisSerializer();
39 | System.out.println("ProtostuffRedisSerializer:" + Arrays.toString(protostuffRedisSerializer.getNullValueBytes()));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/layering-cache-core/src/test/java/com/github/xiaolyuh/cache/config/CacheClusterConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache.config;
2 |
3 | import com.github.xiaolyuh.manager.CacheManager;
4 | import com.github.xiaolyuh.manager.LayeringCacheManager;
5 | import com.github.xiaolyuh.redis.clinet.RedisClient;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.context.annotation.Import;
9 |
10 | @Configuration
11 | @Import({RedisClusterConfig.class})
12 | public class CacheClusterConfig {
13 |
14 | @Bean
15 | public CacheManager layeringCacheManager(RedisClient layeringCacheRedisClient) {
16 | LayeringCacheManager layeringCacheManager = new LayeringCacheManager(layeringCacheRedisClient);
17 | // 开启统计功能
18 | layeringCacheManager.setStats(true);
19 | return layeringCacheManager;
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/layering-cache-core/src/test/java/com/github/xiaolyuh/cache/config/CacheSentinelConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache.config;
2 |
3 | import com.github.xiaolyuh.manager.CacheManager;
4 | import com.github.xiaolyuh.manager.LayeringCacheManager;
5 | import com.github.xiaolyuh.redis.clinet.RedisClient;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.context.annotation.Import;
9 |
10 | @Configuration
11 | @Import({RedisSentinelConfig.class})
12 | public class CacheSentinelConfig {
13 |
14 | @Bean
15 | public CacheManager layeringCacheManager(RedisClient layeringCacheRedisClient) {
16 | LayeringCacheManager layeringCacheManager = new LayeringCacheManager(layeringCacheRedisClient);
17 | // 开启统计功能
18 | layeringCacheManager.setStats(true);
19 | return layeringCacheManager;
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/layering-cache-core/src/test/java/com/github/xiaolyuh/cache/config/CacheSingleConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache.config;
2 |
3 | import com.github.xiaolyuh.manager.CacheManager;
4 | import com.github.xiaolyuh.manager.LayeringCacheManager;
5 | import com.github.xiaolyuh.redis.clinet.RedisClient;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.context.annotation.Import;
9 |
10 | @Configuration
11 | @Import({RedisSingleConfig.class})
12 | public class CacheSingleConfig {
13 |
14 | @Bean
15 | public CacheManager layeringCacheManager(RedisClient layeringCacheRedisClient) {
16 | LayeringCacheManager layeringCacheManager = new LayeringCacheManager(layeringCacheRedisClient);
17 | // 开启统计功能
18 | layeringCacheManager.setStats(true);
19 | return layeringCacheManager;
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/layering-cache-core/src/test/java/com/github/xiaolyuh/cache/config/RedisClusterConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache.config;
2 |
3 | import com.github.xiaolyuh.redis.clinet.ClusterRedisClient;
4 | import com.github.xiaolyuh.redis.clinet.RedisClient;
5 | import com.github.xiaolyuh.redis.clinet.RedisProperties;
6 | import com.github.xiaolyuh.redis.serializer.FastJsonRedisSerializer;
7 | import com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer;
8 | import com.github.xiaolyuh.redis.serializer.JdkRedisSerializer;
9 | import com.github.xiaolyuh.redis.serializer.KryoRedisSerializer;
10 | import com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer;
11 | import com.github.xiaolyuh.redis.serializer.StringRedisSerializer;
12 | import com.github.xiaolyuh.util.StringUtils;
13 | import org.springframework.beans.factory.annotation.Value;
14 | import org.springframework.context.annotation.Bean;
15 | import org.springframework.context.annotation.Configuration;
16 | import org.springframework.context.annotation.PropertySource;
17 |
18 | @Configuration
19 | @PropertySource({"classpath:application.properties"})
20 | public class RedisClusterConfig {
21 |
22 | @Value("${spring.redis.cluster:127.0.0.1:9000,127.0.0.1:9001,127.0.0.1:9002,127.0.0.1:9003,127.0.0.1:9004,127.0.0.1:9005}")
23 | private String cluster;
24 |
25 | @Value("${spring.redis.password:}")
26 | private String password;
27 |
28 | @Bean
29 | public RedisClient layeringCacheRedisClient() {
30 | RedisProperties redisProperties = new RedisProperties();
31 | redisProperties.setCluster(cluster);
32 | redisProperties.setPassword(StringUtils.isBlank(password) ? null : password);
33 |
34 | KryoRedisSerializer kryoRedisSerializer = new KryoRedisSerializer();
35 | FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer();
36 | JacksonRedisSerializer jacksonRedisSerializer = new JacksonRedisSerializer();
37 | JdkRedisSerializer jdkRedisSerializer = new JdkRedisSerializer();
38 | ProtostuffRedisSerializer protostuffRedisSerializer = new ProtostuffRedisSerializer();
39 |
40 | StringRedisSerializer keyRedisSerializer = new StringRedisSerializer();
41 | RedisClient redisClient = new ClusterRedisClient(redisProperties);
42 |
43 | redisClient.setKeySerializer(keyRedisSerializer);
44 | redisClient.setValueSerializer(kryoRedisSerializer);
45 | return redisClient;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/layering-cache-core/src/test/java/com/github/xiaolyuh/cache/config/RedisSentinelConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache.config;
2 |
3 | import com.github.xiaolyuh.redis.clinet.RedisClient;
4 | import com.github.xiaolyuh.redis.clinet.RedisProperties;
5 | import com.github.xiaolyuh.redis.clinet.SentinelRedisClient;
6 | import com.github.xiaolyuh.redis.serializer.FastJsonRedisSerializer;
7 | import com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer;
8 | import com.github.xiaolyuh.redis.serializer.JdkRedisSerializer;
9 | import com.github.xiaolyuh.redis.serializer.KryoRedisSerializer;
10 | import com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer;
11 | import com.github.xiaolyuh.redis.serializer.StringRedisSerializer;
12 | import com.github.xiaolyuh.util.StringUtils;
13 | import org.springframework.beans.factory.annotation.Value;
14 | import org.springframework.context.annotation.Bean;
15 | import org.springframework.context.annotation.Configuration;
16 | import org.springframework.context.annotation.PropertySource;
17 |
18 | @Configuration
19 | @PropertySource({"classpath:application.properties"})
20 | public class RedisSentinelConfig {
21 |
22 | @Value("${layering-cache.redis.nodes:127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381}")
23 | private String nodes;
24 |
25 | @Value("${spring.redis.password:123}")
26 | private String password;
27 |
28 | @Bean
29 | public RedisClient layeringCacheRedisClient() {
30 | RedisProperties redisProperties = new RedisProperties();
31 | redisProperties.setSentinelNodes(nodes);
32 | redisProperties.setPassword(StringUtils.isBlank(password) ? null : password);
33 |
34 | KryoRedisSerializer kryoRedisSerializer = new KryoRedisSerializer();
35 | FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer();
36 | JacksonRedisSerializer jacksonRedisSerializer = new JacksonRedisSerializer();
37 | JdkRedisSerializer jdkRedisSerializer = new JdkRedisSerializer();
38 | ProtostuffRedisSerializer protostuffRedisSerializer = new ProtostuffRedisSerializer();
39 |
40 | StringRedisSerializer keyRedisSerializer = new StringRedisSerializer();
41 | RedisClient redisClient = new SentinelRedisClient(redisProperties);
42 |
43 | redisClient.setKeySerializer(keyRedisSerializer);
44 | redisClient.setValueSerializer(kryoRedisSerializer);
45 | return redisClient;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/layering-cache-core/src/test/java/com/github/xiaolyuh/cache/config/RedisSingleConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache.config;
2 |
3 | import com.github.xiaolyuh.redis.clinet.RedisClient;
4 | import com.github.xiaolyuh.redis.clinet.RedisProperties;
5 | import com.github.xiaolyuh.redis.clinet.SingleRedisClient;
6 | import com.github.xiaolyuh.redis.serializer.FastJsonRedisSerializer;
7 | import com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer;
8 | import com.github.xiaolyuh.redis.serializer.JdkRedisSerializer;
9 | import com.github.xiaolyuh.redis.serializer.KryoRedisSerializer;
10 | import com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer;
11 | import com.github.xiaolyuh.redis.serializer.StringRedisSerializer;
12 | import com.github.xiaolyuh.util.StringUtils;
13 | import org.springframework.beans.factory.annotation.Value;
14 | import org.springframework.context.annotation.Bean;
15 | import org.springframework.context.annotation.Configuration;
16 | import org.springframework.context.annotation.PropertySource;
17 |
18 | @Configuration
19 | @PropertySource({"classpath:application.properties"})
20 | public class RedisSingleConfig {
21 |
22 | @Value("${layering-cache.redis.database:0}")
23 | private int database;
24 |
25 | @Value("${layering-cache.redis.host:127.0.0.1}")
26 | private String host;
27 |
28 | @Value("${layering-cache.redis.password:}")
29 | private String password;
30 |
31 | @Value("${layering-cache.redis.port:6379}")
32 | private int port;
33 |
34 | @Bean
35 | public RedisClient layeringCacheRedisClient() {
36 | RedisProperties redisProperties = new RedisProperties();
37 | redisProperties.setDatabase(database);
38 | redisProperties.setHost(host);
39 | redisProperties.setPassword(StringUtils.isBlank(password) ? null : password);
40 | redisProperties.setPort(port);
41 |
42 | KryoRedisSerializer kryoRedisSerializer = new KryoRedisSerializer();
43 | FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer();
44 | JacksonRedisSerializer jacksonRedisSerializer = new JacksonRedisSerializer();
45 | JdkRedisSerializer jdkRedisSerializer = new JdkRedisSerializer();
46 | ProtostuffRedisSerializer protostuffRedisSerializer = new ProtostuffRedisSerializer();
47 |
48 | StringRedisSerializer keyRedisSerializer = new StringRedisSerializer();
49 |
50 | SingleRedisClient redisClient = new SingleRedisClient(redisProperties);
51 | redisClient.setKeySerializer(keyRedisSerializer);
52 | redisClient.setValueSerializer(protostuffRedisSerializer);
53 | return redisClient;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/layering-cache-core/src/test/resources/application.properties:
--------------------------------------------------------------------------------
1 | layering-cache.redis.database=0
2 | layering-cache.redis.password=
3 | layering-cache.redis.host=127.0.0.1
4 | layering-cache.redis.port=6379
--------------------------------------------------------------------------------
/layering-cache-core/src/test/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.rootLogger=debug, stdout
2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
3 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
4 | log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
--------------------------------------------------------------------------------
/layering-cache-starter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | layering-cache
7 | com.github.xiaolyuh
8 | 3.5.0
9 |
10 | 4.0.0
11 |
12 | layering-cache-starter
13 | layering-cache-starter
14 | 多级缓存 Starter
15 | jar
16 |
17 |
18 |
19 | com.github.xiaolyuh
20 | layering-cache-aspectj
21 | 3.5.0
22 |
23 |
24 |
25 | org.slf4j
26 | slf4j-api
27 | true
28 |
29 |
30 |
31 | com.github.ben-manes.caffeine
32 | caffeine
33 | true
34 |
35 |
36 |
37 | org.springframework
38 | spring-core
39 | true
40 |
41 |
42 |
43 | org.springframework
44 | spring-aop
45 | true
46 |
47 |
48 |
49 | org.springframework
50 | spring-context
51 | true
52 |
53 |
54 |
55 | org.aspectj
56 | aspectjweaver
57 | true
58 |
59 |
60 |
61 | org.springframework.boot
62 | spring-boot-autoconfigure
63 | true
64 |
65 |
66 |
67 | org.projectlombok
68 | lombok
69 | provided
70 |
71 |
72 |
73 | org.springframework.boot
74 | spring-boot-configuration-processor
75 | true
76 |
77 |
78 | io.lettuce
79 | lettuce-core
80 | true
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/layering-cache-starter/src/main/java/com/github/xiaolyuh/cache/config/EnableLayeringCache.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache.config;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 | import org.springframework.context.annotation.Import;
9 |
10 | @Target({ElementType.TYPE})
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Documented
13 | @Import({LayeringCacheAutoConfig.class})
14 | public @interface EnableLayeringCache {
15 |
16 | }
--------------------------------------------------------------------------------
/layering-cache-starter/src/main/java/com/github/xiaolyuh/cache/properties/LayeringCacheProperties.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache.properties;
2 |
3 | import org.springframework.boot.context.properties.ConfigurationProperties;
4 |
5 | /**
6 | * @author yuhao.wang3
7 | */
8 | @ConfigurationProperties("layering-cache")
9 | public class LayeringCacheProperties {
10 |
11 | /**
12 | * 是否开启缓存统计
13 | */
14 | private boolean stats = true;
15 |
16 | /**
17 | * 命名空间,必须唯一般使用服务名
18 | */
19 | private String namespace;
20 |
21 | public boolean isStats() {
22 | return stats;
23 | }
24 |
25 | public void setStats(boolean stats) {
26 | this.stats = stats;
27 | }
28 |
29 | public String getNamespace() {
30 | return namespace;
31 | }
32 |
33 | public void setNamespace(String namespace) {
34 | this.namespace = namespace;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/layering-cache-starter/src/main/java/com/github/xiaolyuh/cache/properties/LayeringCacheRedisProperties.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.cache.properties;
2 |
3 |
4 | import com.github.xiaolyuh.util.StringUtils;
5 | import lombok.Data;
6 | import org.springframework.boot.context.properties.ConfigurationProperties;
7 |
8 | /**
9 | * Redis 配置
10 | *
11 | * @author wangyuhao
12 | */
13 | @Data
14 | @ConfigurationProperties(prefix = "layering-cache.redis")
15 | public class LayeringCacheRedisProperties {
16 | //********************单机配置项**************************/
17 | /**
18 | * 单机配置项
19 | */
20 | String host = "localhost";
21 | Integer port = 6379;
22 |
23 | //********************集群配置**************************/
24 | /**
25 | * 不为空表示集群版,示例
26 | * localhost:7379,localhost2:7379
27 | */
28 | String cluster = "";
29 |
30 | //********************哨兵配置**************************/
31 | /**
32 | * 哨兵master名称,示例:mymaster
33 | */
34 | String sentinelMaster = "mymaster";
35 |
36 | /**
37 | * 哨兵节点,示例:localhost:26397,localhost2:26397
38 | */
39 | String sentinelNodes = "";
40 |
41 | //********************通用配置**************************/
42 | /**
43 | * 使用数据库
44 | */
45 | Integer database = 0;
46 |
47 | /**
48 | * 密码
49 | */
50 | String password = null;
51 |
52 | /**
53 | * 超时时间 单位秒 默认一个小时
54 | */
55 | Integer timeout = 3600;
56 | /**
57 | * 序列化方式:
58 | * com.github.xiaolyuh.redis.serializer.KryoRedisSerializer
59 | * com.github.xiaolyuh.redis.serializer.FastJsonRedisSerializer
60 | * com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer
61 | * com.github.xiaolyuh.redis.serializer.JdkRedisSerializer
62 | * com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer
63 | */
64 | String serializer = "com.github.xiaolyuh.redis.serializer.ProtostuffRedisSerializer";
65 |
66 | public String getPassword() {
67 | return StringUtils.isBlank(password) ? null : password;
68 | }
69 | }
--------------------------------------------------------------------------------
/layering-cache-starter/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/layering-cache-web/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 | .sts4-cache
12 |
13 | ### IntelliJ IDEA ###
14 | .idea
15 | *.iws
16 | *.iml
17 | *.ipr
18 |
19 | ### NetBeans ###
20 | /nbproject/private/
21 | /build/
22 | /nbbuild/
23 | /dist/
24 | /nbdist/
25 | /.nb-gradle/
--------------------------------------------------------------------------------
/layering-cache-web/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.github.xiaolyuh
7 | layering-cache-web
8 | 3.2.0
9 | jar
10 |
11 | layering-cache-web
12 | layering-cache web
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 2.1.1.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 |
26 |
27 |
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-web
32 |
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-test
37 | test
38 |
39 |
40 |
41 | com.alibaba
42 | fastjson
43 | 1.2.58
44 |
45 |
46 |
47 | io.lettuce
48 | lettuce-core
49 |
50 |
51 |
52 | com.github.ben-manes.caffeine
53 | caffeine
54 |
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-starter-thymeleaf
59 | 2.1.1.RELEASE
60 |
61 |
62 |
63 | com.squareup.okhttp3
64 | okhttp
65 | 3.6.0
66 |
67 |
68 |
69 | com.esotericsoftware
70 | kryo-shaded
71 | 3.0.3
72 |
73 |
74 |
75 | org.projectlombok
76 | lombok
77 | true
78 |
79 |
80 |
81 | io.protostuff
82 | protostuff-core
83 | 1.7.2
84 |
85 |
86 |
87 | io.protostuff
88 | protostuff-runtime
89 | 1.7.2
90 |
91 |
92 |
93 | com.github.xiaolyuh
94 | layering-cache-core
95 | 3.5.0
96 |
97 |
98 |
99 |
100 |
101 |
102 | org.springframework.boot
103 | spring-boot-maven-plugin
104 |
105 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/LayeringCacheWebApplication.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class LayeringCacheWebApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(LayeringCacheWebApplication.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/config/WebMvcConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.config;
2 |
3 | import com.github.xiaolyuh.web.interceptor.LoginInterceptor;
4 | import com.github.xiaolyuh.web.service.UserService;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
9 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
10 |
11 | @Configuration
12 | public class WebMvcConfig implements WebMvcConfigurer {
13 |
14 | @Autowired
15 | private UserService userService;
16 |
17 | @Bean
18 | public WebMvcConfig getMyWebMvcConfig() {
19 | WebMvcConfig webMvcConfig = new WebMvcConfig() {
20 | //注册拦截器
21 | @Override
22 | public void addInterceptors(InterceptorRegistry registry) {
23 | registry.addInterceptor(new LoginInterceptor(userService))
24 | .addPathPatterns("/**")
25 | .excludePathPatterns("/login**", "/user/submit-login", "/toLogin", "/redis/redis-config", "/css/**", "/js/**", "/fonts/**", "/i/**");
26 | }
27 | };
28 | return webMvcConfig;
29 | }
30 | }
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/constant/URLConstant.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.constant;
2 |
3 | /**
4 | * URL 常量
5 | *
6 | * @author yuhao.wang3
7 | */
8 | public class URLConstant {
9 |
10 | /**
11 | * 用户登录页面
12 | */
13 | public static final String USER_LOGIN_PAGE = "/login.html";
14 |
15 | /**
16 | * 用户登录URL
17 | */
18 | public static final String USER_SUBMIT_LOGIN = "/user/submit-login";
19 |
20 | /**
21 | * 用户登出URL
22 | */
23 | public static final String USER_LOGIN_OUT = "/user/login-out";
24 |
25 | /**
26 | * 重置缓存统计数据
27 | */
28 | public static final String RESET_CACHE_STAT = "/cache-stats/reset-stats";
29 |
30 | /**
31 | * 缓存统计列表
32 | */
33 | public static final String CACHE_STATS_LIST = "/cache-stats/list";
34 |
35 | /**
36 | * 删除缓存统计
37 | */
38 | public static final String CACHE_STATS_DELETE_CACHW = "/cache-stats/delete-cache";
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/controller/RedisController.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.controller;
2 |
3 | import com.github.xiaolyuh.redis.clinet.ClusterRedisClient;
4 | import com.github.xiaolyuh.redis.clinet.RedisClient;
5 | import com.github.xiaolyuh.redis.clinet.RedisProperties;
6 | import com.github.xiaolyuh.redis.clinet.SingleRedisClient;
7 | import com.github.xiaolyuh.redis.serializer.StringRedisSerializer;
8 | import com.github.xiaolyuh.util.GlobalConfig;
9 | import com.github.xiaolyuh.util.StringUtils;
10 | import com.github.xiaolyuh.web.utils.Result;
11 | import org.springframework.stereotype.Controller;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.bind.annotation.ResponseBody;
14 |
15 | import java.util.ArrayList;
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.Map;
19 | import java.util.concurrent.ConcurrentHashMap;
20 |
21 | @Controller
22 | public class RedisController {
23 | public static final Map redisClientMap = new ConcurrentHashMap<>();
24 |
25 | @RequestMapping("/redis/redis-config")
26 | @ResponseBody
27 | public Result login(String address, String password, Integer port, Integer database, String serializer) {
28 | try {
29 | RedisProperties redisProperties = new RedisProperties();
30 | if (address.contains(":")) {
31 | redisProperties.setCluster(address);
32 | } else {
33 | redisProperties.setHost(address);
34 | }
35 | redisProperties.setPassword(StringUtils.isBlank(password) ? null : password);
36 | redisProperties.setPort(port);
37 | redisProperties.setDatabase(database);
38 | redisProperties.setSerializer("com.github.xiaolyuh.redis.serializer.JacksonRedisSerializer");
39 |
40 | String key = address + ":" + port + ":" + database;
41 | redisClientMap.put(key, getRedisClient(redisProperties));
42 |
43 | RedisClient redisClient = redisClientMap.get(key);
44 | redisClient.get("test", String.class);
45 | return Result.success();
46 | } catch (Exception e) {
47 | return Result.error("配置redis失败" + e.getMessage());
48 | }
49 | }
50 |
51 | @RequestMapping("/redis/redis-list")
52 | @ResponseBody
53 | public Result redisList() {
54 | List> list = new ArrayList<>();
55 | try {
56 | for (String key : redisClientMap.keySet()) {
57 | Map map = new HashMap<>();
58 | map.put("address", key);
59 | list.add(map);
60 | }
61 | return Result.success(list);
62 | } catch (Exception e) {
63 | return Result.error("配置redis失败" + e.getMessage());
64 | }
65 | }
66 |
67 |
68 | private RedisClient getRedisClient(RedisProperties redisProperties) {
69 | StringRedisSerializer keyRedisSerializer = new StringRedisSerializer();
70 |
71 | RedisClient redisClient;
72 | if (StringUtils.isNotBlank(redisProperties.getCluster())) {
73 | redisClient = new ClusterRedisClient(redisProperties);
74 | } else {
75 | redisClient = new SingleRedisClient(redisProperties);
76 | }
77 |
78 | redisClient.setKeySerializer(keyRedisSerializer);
79 | redisClient.setValueSerializer(GlobalConfig.GLOBAL_REDIS_SERIALIZER);
80 | return redisClient;
81 | }
82 | }
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/controller/UserController.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.controller;
2 |
3 | import com.github.xiaolyuh.web.service.UserService;
4 | import com.github.xiaolyuh.web.utils.Result;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Controller;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.ResponseBody;
9 |
10 | import java.util.UUID;
11 |
12 | @Controller
13 | public class UserController {
14 |
15 | @Autowired
16 | private UserService userService;
17 |
18 | @RequestMapping("/index")
19 | public String index() {
20 | return "index";
21 | }
22 |
23 | @RequestMapping("/toLogin")
24 | public String toLogin() {
25 | return "login";
26 | }
27 |
28 | @RequestMapping("/user/login-out")
29 | @ResponseBody
30 | public Result loginOut(String token) {
31 | userService.loginOut(token);
32 | return Result.success();
33 | }
34 |
35 | @RequestMapping("/user/submit-login")
36 | @ResponseBody
37 | public Result login(String loginUsername, String loginPassword) {
38 | String token = UUID.randomUUID().toString();
39 | if (userService.login(loginUsername, loginPassword, token)) {
40 | return Result.success(token);
41 | }
42 | return Result.error("用户名或密码错误");
43 | }
44 | }
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/interceptor/LoginInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.interceptor;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.github.xiaolyuh.web.service.UserService;
5 | import com.github.xiaolyuh.web.utils.Result;
6 | import org.springframework.web.servlet.HandlerInterceptor;
7 |
8 | import javax.servlet.http.HttpServletRequest;
9 | import javax.servlet.http.HttpServletResponse;
10 |
11 | /**
12 | * @author olafwang
13 | * @since 2020/7/10 3:00 下午
14 | */
15 | public class LoginInterceptor implements HandlerInterceptor {
16 | public static final String PARAM_NAME_TOKEN = "token";
17 |
18 | private UserService userService;
19 |
20 | public LoginInterceptor(UserService userService) {
21 | this.userService = userService;
22 | }
23 |
24 | @Override
25 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
26 | throws Exception {
27 |
28 | String contextPath = request.getContextPath();
29 | String servletPath = request.getServletPath();
30 | String requestURI = request.getRequestURI();
31 |
32 | response.setCharacterEncoding("utf-8");
33 |
34 | // root context
35 | if (contextPath == null) {
36 | contextPath = "";
37 | }
38 | String path = requestURI.substring(contextPath.length() + servletPath.length());
39 |
40 | // 登录校验
41 | String token = request.getParameter(PARAM_NAME_TOKEN);
42 |
43 | if (!userService.checkLogin(token)) {
44 | response.sendRedirect("/toLogin");
45 | // request.getRequestDispatcher("/toLogin").forward(request, response);
46 | // response.getWriter().write(JSON.toJSONString(Result.error("请登录")));
47 | return false;
48 | }
49 |
50 | return true;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/service/CacheService.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.service;
2 |
3 | import com.github.xiaolyuh.cache.Cache;
4 | import com.github.xiaolyuh.manager.AbstractCacheManager;
5 | import com.github.xiaolyuh.setting.FirstCacheSetting;
6 | import com.github.xiaolyuh.setting.LayeringCacheSetting;
7 | import com.github.xiaolyuh.setting.SecondaryCacheSetting;
8 | import com.github.xiaolyuh.stats.StatsService;
9 | import com.github.xiaolyuh.support.CacheMode;
10 | import com.github.xiaolyuh.util.BeanFactory;
11 | import com.github.xiaolyuh.util.StringUtils;
12 | import org.springframework.stereotype.Service;
13 | import org.springframework.util.CollectionUtils;
14 |
15 | import java.util.Collection;
16 | import java.util.Set;
17 |
18 | /**
19 | * 操作缓存的服务
20 | *
21 | * @author yuhao.wang3
22 | */
23 | @Service
24 | public class CacheService {
25 | /**
26 | * 删除缓存
27 | *
28 | * @param cacheName 缓存名称
29 | * @param internalKey 内部缓存名,由[一级缓存有效时间-二级缓存有效时间]组成
30 | * @param key key,可以为NULL,如果是NULL则清空缓存
31 | */
32 | public void deleteCache(String cacheName, String internalKey, String key) {
33 | if (StringUtils.isBlank(cacheName) || StringUtils.isBlank(internalKey)) {
34 | return;
35 | }
36 | LayeringCacheSetting defaultSetting = new LayeringCacheSetting(new FirstCacheSetting(), new SecondaryCacheSetting(), "默认缓存配置(删除时生成)", CacheMode.ALL);
37 | Set cacheManagers = AbstractCacheManager.getCacheManager();
38 | if (StringUtils.isBlank(key)) {
39 | // 清空缓存
40 | for (AbstractCacheManager cacheManager : cacheManagers) {
41 | // 删除缓存统计信息
42 | String redisKey = StatsService.CACHE_STATS_KEY_PREFIX + cacheName + internalKey;
43 | BeanFactory.getBean(StatsService.class).resetCacheStat(redisKey);
44 |
45 | // 删除缓存
46 | Collection caches = cacheManager.getCache(cacheName);
47 | if (CollectionUtils.isEmpty(caches)) {
48 | // 如果没有找到Cache就新建一个默认的
49 | Cache cache = cacheManager.getCache(cacheName, defaultSetting);
50 | cache.clear();
51 |
52 | // 删除统计信息
53 | redisKey = StatsService.CACHE_STATS_KEY_PREFIX + cacheName + defaultSetting.getInternalKey();
54 | BeanFactory.getBean(StatsService.class).resetCacheStat(redisKey);
55 | } else {
56 | for (Cache cache : caches) {
57 | cache.clear();
58 | }
59 | }
60 | }
61 |
62 | return;
63 | }
64 |
65 | // 删除指定key
66 | for (AbstractCacheManager cacheManager : cacheManagers) {
67 | Collection caches = cacheManager.getCache(cacheName);
68 | if (CollectionUtils.isEmpty(caches)) {
69 | // 如果没有找到Cache就新建一个默认的
70 | Cache cache = cacheManager.getCache(cacheName, defaultSetting);
71 | cache.evict(key);
72 | } else {
73 | for (Cache cache : caches) {
74 | cache.evict(key);
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.service;
2 |
3 | import com.github.benmanes.caffeine.cache.Cache;
4 | import com.github.benmanes.caffeine.cache.Caffeine;
5 | import com.github.xiaolyuh.util.StringUtils;
6 | import org.springframework.beans.factory.annotation.Value;
7 | import org.springframework.stereotype.Service;
8 |
9 | import java.io.IOException;
10 | import java.util.Objects;
11 | import java.util.concurrent.TimeUnit;
12 |
13 | /**
14 | * 用户服务
15 | *
16 | * @author yuhao.wang3
17 | */
18 | @Service
19 | public class UserService {
20 | private static Cache manualCache = Caffeine.newBuilder()
21 | .expireAfterWrite(30, TimeUnit.MINUTES)
22 | .maximumSize(1000)
23 | .build();
24 |
25 |
26 | @Value("${layering-cache.web.user-name:admin}")
27 | private String userName;
28 |
29 | @Value("${layering-cache.web.password:admin}")
30 | private String password;
31 |
32 | /**
33 | * 登录校验
34 | *
35 | * @param token 唯一标示
36 | */
37 | public boolean checkLogin(String token) {
38 | if (StringUtils.isBlank(token)) {
39 | return false;
40 | }
41 | // 检查是否登录
42 | if (!isLogin(token)) {
43 | return false;
44 | }
45 |
46 | return true;
47 | }
48 |
49 | /**
50 | * 登录
51 | *
52 | * @param usernameParam 用户名
53 | * @param passwordParam 密码
54 | * @param token 唯一标示
55 | * @return
56 | * @throws IOException
57 | */
58 | public boolean login(String usernameParam, String passwordParam, String token) {
59 | if (userName.equals(usernameParam) && password.equals(passwordParam)) {
60 | manualCache.put(token, userName);
61 | return true;
62 | }
63 | return false;
64 | }
65 |
66 | /**
67 | * 是否登录
68 | *
69 | * @param token 唯一标示
70 | * @return
71 | */
72 | public boolean isLogin(String token) {
73 | if (Objects.nonNull(manualCache.getIfPresent(token))) {
74 | refreshSession(token);
75 | return true;
76 | }
77 | return false;
78 | }
79 |
80 | /**
81 | * 退出
82 | *
83 | * @param token 唯一标示
84 | * @return boolean
85 | */
86 | public boolean loginOut(String token) {
87 | manualCache.invalidate(token);
88 | return true;
89 | }
90 |
91 | /**
92 | * 刷新session
93 | *
94 | * @param token 唯一标示
95 | * @return boolean
96 | */
97 | public boolean refreshSession(String token) {
98 | manualCache.put(token, token);
99 | return true;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/service/WebStatsService.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.service;
2 |
3 | import com.github.xiaolyuh.redis.clinet.RedisClient;
4 | import com.github.xiaolyuh.stats.CacheStatsInfo;
5 | import com.github.xiaolyuh.stats.StatsService;
6 | import com.github.xiaolyuh.util.GlobalConfig;
7 | import com.github.xiaolyuh.util.StringUtils;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.springframework.stereotype.Service;
11 | import org.springframework.util.CollectionUtils;
12 |
13 | import java.util.ArrayList;
14 | import java.util.Collections;
15 | import java.util.Comparator;
16 | import java.util.List;
17 | import java.util.Objects;
18 | import java.util.Set;
19 | import java.util.concurrent.TimeUnit;
20 | import java.util.stream.Collectors;
21 |
22 | /**
23 | * 统计服务
24 | *
25 | * @author yuhao.wang3
26 | */
27 | @Service
28 | public class WebStatsService {
29 | private static Logger logger = LoggerFactory.getLogger(WebStatsService.class);
30 |
31 | /**
32 | * 获取缓存统计list
33 | *
34 | * @param redisClient redisClient
35 | * @param cacheNameParam 缓存名称
36 | * @return List<CacheStatsInfo>
37 | */
38 | public List listCacheStats(RedisClient redisClient, String cacheNameParam) {
39 | logger.debug("获取缓存统计数据");
40 |
41 | Set layeringCacheKeys = redisClient.scan(StatsService.CACHE_STATS_KEY_PREFIX + "*");
42 | if (CollectionUtils.isEmpty(layeringCacheKeys)) {
43 | return Collections.emptyList();
44 | }
45 | // 遍历找出对应统计数据
46 | List statsList = new ArrayList<>();
47 | for (String key : layeringCacheKeys) {
48 | if (StringUtils.isNotBlank(cacheNameParam) && !key.startsWith(StatsService.CACHE_STATS_KEY_PREFIX + cacheNameParam)) {
49 | continue;
50 | }
51 |
52 | try {
53 | CacheStatsInfo cacheStats = redisClient.get(key, CacheStatsInfo.class, GlobalConfig.GLOBAL_REDIS_SERIALIZER);
54 | if (!Objects.isNull(cacheStats)) {
55 | statsList.add(cacheStats);
56 | }
57 | } catch (Exception e) {
58 | redisClient.delete(key);
59 | logger.error(e.getMessage(), e);
60 | }
61 | }
62 |
63 | return statsList.stream().sorted(Comparator.comparing(CacheStatsInfo::getHitRate)).collect(Collectors.toList());
64 | }
65 |
66 |
67 | /**
68 | * 重置缓存统计数据
69 | */
70 | public void resetCacheStat(RedisClient redisClient) {
71 | Set layeringCacheKeys = redisClient.scan(StatsService.CACHE_STATS_KEY_PREFIX + "*");
72 |
73 | for (String key : layeringCacheKeys) {
74 | resetCacheStat(redisClient, key);
75 | }
76 | }
77 |
78 | /**
79 | * 重置缓存统计数据
80 | *
81 | * @param redisKey redisKey
82 | */
83 | public void resetCacheStat(RedisClient redisClient, String redisKey) {
84 | try {
85 | CacheStatsInfo cacheStats = redisClient.get(redisKey, CacheStatsInfo.class, GlobalConfig.GLOBAL_REDIS_SERIALIZER);
86 | if (Objects.nonNull(cacheStats)) {
87 | cacheStats.clearStatsInfo();
88 | // 将缓存统计数据写到redis
89 | redisClient.set(redisKey, cacheStats, 24, TimeUnit.HOURS);
90 | }
91 | } catch (Exception e) {
92 | redisClient.delete(redisKey);
93 | }
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/utils/OkHttpClientUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.utils;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import okhttp3.FormBody;
5 | import okhttp3.MediaType;
6 | import okhttp3.OkHttpClient;
7 | import okhttp3.Request;
8 | import okhttp3.RequestBody;
9 | import okhttp3.Response;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 | import org.springframework.remoting.RemoteAccessException;
13 |
14 | import java.io.IOException;
15 | import java.util.Map;
16 | import java.util.concurrent.TimeUnit;
17 |
18 | /**
19 | * OkHttpClient工具
20 | *
21 | * @author yuhao.wang3
22 | */
23 | public abstract class OkHttpClientUtil {
24 | private static final Logger logger = LoggerFactory.getLogger(OkHttpClientUtil.class);
25 |
26 | private static OkHttpClient okHttpClient = new OkHttpClient.Builder()
27 | .connectTimeout(10, TimeUnit.SECONDS)
28 | .writeTimeout(10, TimeUnit.SECONDS)
29 | .readTimeout(20, TimeUnit.SECONDS)
30 | .build();
31 |
32 |
33 | /**
34 | * 发起post请求,不做任何签名
35 | *
36 | * @param url 发送请求的URL
37 | * @param requestBody 请求体
38 | * @throws IOException
39 | */
40 | public static String post(String url, RequestBody requestBody) throws IOException {
41 | Request request = new Request.Builder()
42 | //请求的url
43 | .url(url)
44 | .post(requestBody)
45 | .build();
46 |
47 | //创建/Call
48 | Response response = okHttpClient.newCall(request).execute();
49 | if (!response.isSuccessful()) {
50 | logger.error("访问外部系统异常 {}: {}", url, JSON.toJSONString(response));
51 | throw new RemoteAccessException("访问外部系统异常 " + url);
52 | }
53 | return response.body().string();
54 | }
55 |
56 | /**
57 | * 发起post请求,不做任何签名 宽松的参数构造
58 | *
59 | * @param url 发送请求的URL
60 | * @param builder 请求体
61 | * @throws IOException
62 | */
63 | public static String post(String url, Request.Builder builder) throws IOException {
64 |
65 | Request request = builder.url(url).build();
66 | //创建/Call
67 | Response response = okHttpClient.newCall(request).execute();
68 | if (!response.isSuccessful()) {
69 | logger.error("访问外部系统异常 {}: {}", url, JSON.toJSONString(response));
70 | throw new RemoteAccessException("访问外部系统异常 " + url);
71 | }
72 | return response.body().string();
73 | }
74 |
75 |
76 | public static String post(String url, Map param, Map header) throws Exception {
77 | // 生成requestBody
78 | RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8")
79 | , JSON.toJSONString(param));
80 |
81 | Request.Builder builder = new Request.Builder()
82 | //请求的url
83 | .url(url)
84 | .post(requestBody);
85 |
86 | for (String key : header.keySet()) {
87 | builder.header(key, header.get(key));
88 | }
89 |
90 | Request request = builder.build();
91 |
92 | //创建/Call
93 | Response response = okHttpClient.newCall(request).execute();
94 | if (!response.isSuccessful()) {
95 | logger.error("访问外部系统异常 {}: {}", url, JSON.toJSONString(response));
96 | throw new RemoteAccessException("访问外部系统异常 " + url);
97 | }
98 | return response.body().string();
99 | }
100 |
101 | public static String get(String url) throws IOException {
102 | Request request = new Request.Builder()
103 | //请求的url
104 | .url(url)
105 | .get()
106 | .build();
107 |
108 | Response response = okHttpClient.newCall(request).execute();
109 | if (!response.isSuccessful()) {
110 | logger.error("访问外部系统异常 {}: {}", url, JSON.toJSONString(response));
111 | throw new RemoteAccessException("访问外部系统异常 " + url);
112 | }
113 | return response.body().string();
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/utils/Result.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.utils;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * 与前端交互对象
7 | *
8 | * @param
9 | */
10 | public class Result implements Serializable {
11 | private static final long serialVersionUID = 5925101851082556646L;
12 | private T data;
13 | private Status status;
14 | private String code;
15 | private String message;
16 |
17 | public static Result success() {
18 | Result result = new Result<>();
19 | result.setCode("200");
20 | result.setStatus(Status.SUCCESS);
21 | result.setMessage(Status.SUCCESS.name());
22 | return result;
23 | }
24 |
25 | public static Result success(T data) {
26 | Result result = new Result<>();
27 | result.setCode("200");
28 | result.setStatus(Status.SUCCESS);
29 | result.setMessage(Status.SUCCESS.name());
30 | result.setData(data);
31 | return result;
32 | }
33 |
34 | public static Result error(String msg) {
35 | Result result = new Result<>();
36 | result.setCode("500");
37 | result.setStatus(Status.ERROR);
38 | result.setMessage(msg);
39 | return result;
40 | }
41 |
42 | public T getData() {
43 | return data;
44 | }
45 |
46 | public void setData(T data) {
47 | this.data = data;
48 | }
49 |
50 | public String getCode() {
51 | return code;
52 | }
53 |
54 | public void setCode(String code) {
55 | this.code = code;
56 | }
57 |
58 | public String getMessage() {
59 | return message;
60 | }
61 |
62 | public void setMessage(String message) {
63 | this.message = message;
64 | }
65 |
66 | public Status getStatus() {
67 | return status;
68 | }
69 |
70 | public void setStatus(Status status) {
71 | this.status = status;
72 | }
73 |
74 | public static enum Status {
75 | SUCCESS("OK"),
76 | ERROR("ERROR");
77 |
78 | private String code;
79 |
80 | private Status(String code) {
81 | this.code = code;
82 | }
83 |
84 | public String code() {
85 | return this.code;
86 | }
87 | }
88 |
89 | }
--------------------------------------------------------------------------------
/layering-cache-web/src/main/java/com/github/xiaolyuh/web/vo/CacheStatsInfoVo.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.web.vo;
2 |
3 | import com.github.xiaolyuh.stats.CacheStatsInfo;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * @author olafwang
9 | * @since 2020/9/25 3:07 下午
10 | */
11 | public class CacheStatsInfoVo {
12 | List cacheStats;
13 |
14 | long time;
15 |
16 | public List getCacheStats() {
17 | return cacheStats;
18 | }
19 |
20 | public void setCacheStats(List cacheStats) {
21 | this.cacheStats = cacheStats;
22 | }
23 |
24 | public long getTime() {
25 | return time;
26 | }
27 |
28 | public void setTime(long time) {
29 | this.time = time;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | # 不能改
2 | spring.thymeleaf.prefix=classpath:/html/
3 |
4 | # 端口号
5 | server.port=8081
6 | spring.application.name=layering-cache-web
7 |
8 | # 管理界面登录的用户名密码
9 | layering-cache.web.user-name=admin
10 | layering-cache.web.password=admin
11 |
12 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/html/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Layering Cache
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
26 |
27 |
28 |
35 |
36 |
37 |
登录
38 |
39 |
42 |
43 |
44 |
45 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/html/nopermit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Layering Cache
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
22 |
23 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
没有权限访问该页面
43 |
Sorry, you are not permitted to view this page.
44 |
45 | .----.
46 | _.'__ `.
47 | .--($)($$)---/#\
48 | .' @ /###\
49 | : , #####
50 | `-..__.-' _.-\###/
51 | `;_: `"'
52 | .'"""""`.
53 | /, ya ,\\
54 | // 没权限! \\
55 | `-._______.-'
56 | ___`. | .'___
57 | (______|______)
58 |
59 |
60 |
61 |
62 |
63 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/css/app.css:
--------------------------------------------------------------------------------
1 | /* Write your styles */
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/layering-cache-web/src/main/resources/static/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/layering-cache-web/src/main/resources/static/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/layering-cache-web/src/main/resources/static/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/layering-cache-web/src/main/resources/static/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/layering-cache-web/src/main/resources/static/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/i/app-icon72x72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/layering-cache-web/src/main/resources/static/i/app-icon72x72@2x.png
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/i/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/layering-cache-web/src/main/resources/static/i/favicon.png
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/i/startup-640x1096.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaolyuh/layering-cache/282d3c10fc935bbfd981397a0997bb27ffd9aeae/layering-cache-web/src/main/resources/static/i/startup-640x1096.png
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/js/lib/app.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | 'use strict';
3 |
4 | $(function() {
5 | var $fullText = $('.admin-fullText');
6 | $('#admin-fullscreen').on('click', function() {
7 | $.AMUI.fullscreen.toggle();
8 | });
9 |
10 | $(document).on($.AMUI.fullscreen.raw.fullscreenchange, function() {
11 | $fullText.text($.AMUI.fullscreen.isFullscreen ? '退出全屏' : '开启全屏');
12 | });
13 | });
14 | })(jQuery);
15 |
16 |
17 |
--------------------------------------------------------------------------------
/layering-cache-web/src/main/resources/static/js/views/login.js:
--------------------------------------------------------------------------------
1 | (function ($) {
2 |
3 | var constant = {
4 | LOGIN_FORM: '#login-form',
5 | LOGIN_BUTTON: '#login-button',
6 | };
7 |
8 | var bindEvent = {
9 | bindLogin:function () {
10 | $(constant.LOGIN_BUTTON).on("click", function () {
11 | $.ajax({
12 | type: 'POST',
13 | url: 'user/submit-login',
14 | dataType: 'JSON',
15 | data: $(constant.LOGIN_FORM).serialize(),
16 | success: function (data) {
17 | if (data.status == "SUCCESS") {
18 | window.location.href = "/index?token=" + data.data;
19 | } else {
20 | alert("用户名或密码错误");
21 | }
22 | }
23 | });
24 | });
25 | }
26 | };
27 |
28 | var index = {
29 | init: function () {
30 | bindEvent.bindLogin();
31 | }
32 | };
33 |
34 | $(function () {
35 | index.init();
36 | });
37 | })(jQuery);
--------------------------------------------------------------------------------
/layering-cache-web/src/test/java/com/github/xiaolyuh/demo/LayeringCacheWebApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.github.xiaolyuh.demo;
2 |
3 | import org.junit.runner.RunWith;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 | import org.springframework.test.context.junit4.SpringRunner;
6 |
7 | @RunWith(SpringRunner.class)
8 | @SpringBootTest
9 | public class LayeringCacheWebApplicationTests {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | Copyright 1999-2018 Alibaba Group Holding Ltd.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------