Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,31 +55,31 @@
<argLine>-Dfile.encoding=UTF-8</argLine>
</configuration>
</plugin>
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<configLocation>${basedir}/.circleci/checkstyle.xml</configLocation>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<enableRulesSummary>false</enableRulesSummary>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>8.29</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<configLocation>${basedir}/.circleci/checkstyle.xml</configLocation>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<enableRulesSummary>false</enableRulesSummary>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>8.29</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
5 changes: 5 additions & 0 deletions src/main/java/com/github/hcsp/annotation/Cache.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.github.hcsp.annotation;


import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
// 标记缓存的时长(秒),默认60s
int cacheSeconds() default 60;
Expand Down
109 changes: 107 additions & 2 deletions src/main/java/com/github/hcsp/annotation/CacheClassDecorator.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,119 @@
package com.github.hcsp.annotation;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.*;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

public class CacheClassDecorator {
// 将传入的服务类Class进行增强
// 使得返回一个具有如下功能的Class:
// 如果某个方法标注了@Cache注解,则返回值能够被自动缓存注解所指定的时长
// 这意味着,在短时间内调用同一个服务的同一个@Cache方法两次
// 它实际上只被调用一次,第二次的结果直接从缓存中获取
// 注意,缓存的实现需要是线程安全的
public static <T> Class<T> decorate(Class<T> klass) {
return klass;

@SuppressWarnings("unchecked")
public static <T> Class<T> decorate(Class<T> klass) throws IllegalAccessException, InstantiationException {
return (Class) new ByteBuddy()
.subclass(klass)
.method(ElementMatchers.isAnnotatedWith(Cache.class))
.intercept(MethodDelegation.to(CacheAdvisor.class))
.make()
.load(klass.getClassLoader())
.getLoaded();
}

private static class CacheKey {
private String method;
private Object object;
private Object[] argument;

CacheKey(String method, Object object, Object[] argument) {
this.method = method;
this.object = object;
this.argument = argument;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CacheKey cacheKey = (CacheKey) o;
return Objects.equals(method, cacheKey.method) &&
Objects.equals(object, cacheKey.object) &&
Arrays.equals(argument, cacheKey.argument);
}

@Override
public int hashCode() {
int result = Objects.hash(method, object);
result = 31 * result + Arrays.hashCode(argument);
return result;
}
}

private static class CacheValue {
private Object value;
private long time;

CacheValue(Object value, long time) {
this.value = value;
this.time = time;
}
}

public static class CacheAdvisor {
private static ConcurrentHashMap<CacheKey, CacheValue> cache = new ConcurrentHashMap<>();

@RuntimeType
public static Object cache(
//调用父类的方法
@SuperCall Callable<Object> superCall,
//当前正在被调用的方法
@Origin Method method,
//实体方法的对象
@This Object object,
//所有的参数
@AllArguments Object[] arguments) throws Exception {
//获取缓存的结果
CacheKey cacheKey = new CacheKey(method.getName(), object, arguments);
int cacheSeconds = method.getAnnotation(Cache.class).cacheSeconds();
final CacheValue obj = cache.get(cacheKey);

if (obj != null) {
long time = cache.get(cacheKey).time;
if (cacheIsExpired(time, cacheSeconds)) {
//缓存过期
return putNewValueIntoCache(superCall, cacheKey);
} else {
return obj.value;
}
} else {
return putNewValueIntoCache(superCall, cacheKey);
}
}

private static boolean cacheIsExpired(long time, long cacheSeconds) {
return ((System.currentTimeMillis() - time) > cacheSeconds * 1000);
}

private static Object putNewValueIntoCache(@SuperCall Callable<Object> superCall, CacheKey cacheKey) throws Exception {
Object realResult = superCall.call();
CacheValue cacheValue = new CacheValue(realResult, System.currentTimeMillis());
cache.put(cacheKey, cacheValue);
return realResult;
}
}

public static void main(String[] args) throws Exception {
Expand Down