package com.skyline.common.cache;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.util.StringUtils;
import com.skyline.common.bean.Page;
import com.skyline.common.cache.annotation.Cache;
import com.skyline.common.cache.annotation.CacheCategoryType;
import com.skyline.common.cache.annotation.CacheDelete;
import com.skyline.common.cache.annotation.Fk;
import com.skyline.common.cache.annotation.GenericTable;
import com.skyline.common.cache.annotation.Param;
public class CacheMethodDefinition {
private final Method method;
private final Object target;
private String keyPattern;
private CacheCategoryType categoryType;
private long expire;
private boolean isPagination;
private final Map<Class<? extends Annotation>, Annotation[]> parameterAnnotations = new HashMap<Class<? extends Annotation>, Annotation[]>(
8, 1.0f);
private int parameterCount;
private boolean isCacheDelete;
private boolean isCachable;
private boolean isReturnCollection;
private boolean isReturnCachable;
private boolean isGenericCache;
private int pageParameterIndex = -1;
private int fkAnnotationIndex = -1;
private int genericTableIndex = -1;
public CacheMethodDefinition(Object target, Method method) {
this.method = method;
this.target = target;
Annotation[] annotations = method.getAnnotations();
parseMethodAnnotation(annotations);
if (!isCachable && !isCacheDelete) { // 如果非Cache需要的对象则不再做解析
return;
}
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
parseParameterAnnotations(parameterAnnotations);
Class<? extends Object> returnType = method.getReturnType();
isReturnCollection = isTypeCollection(returnType);
isReturnCachable = isTypeCachable(returnType);
Class<?>[] parameterTypes = method.getParameterTypes();
getPageIndex(parameterTypes);
judgeCacheParams();
}
private void parseMethodAnnotation(Annotation[] annotations) {
for (Annotation annotation : annotations) {
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType.equals(Cache.class)) {
Cache cache = (Cache) annotation;
isCachable = true;
keyPattern = cache.keyPattern();
expire = cache.expire();
isPagination = cache.pagination();
categoryType = cache.type();
isGenericCache = isCategoryTypeGeneric(categoryType);
}
if (annotationType.equals(CacheDelete.class)) { // 如categoryType等可能重复的数据以删除为准
isCacheDelete = true;
CacheDelete cacheDelete = (CacheDelete) annotation;
categoryType = cacheDelete.type();
expire = cacheDelete.expire();
isGenericCache = isCategoryTypeGeneric(categoryType);
}
}
}
private void parseParameterAnnotations(Annotation[][] annotations) {
parameterCount = annotations.length;
for (int index = 0; index < parameterCount; index++) {
for (Annotation annotation : annotations[index]) {
Class<? extends Annotation> annotationType = annotation.annotationType();
Annotation[] annotationArray = parameterAnnotations.get(annotationType);
if (annotationArray == null) {
annotationArray = (Annotation[]) Array.newInstance(annotationType,
parameterCount);
parameterAnnotations.put(annotationType, annotationArray);
}
annotationArray[index] = annotation;
if (annotationType.equals(Fk.class)) {
fkAnnotationIndex = index;
}
if (annotationType.equals(GenericTable.class)) {
genericTableIndex = index;
}
}
}
}
private void judgeCacheParams() {
if (isPagination && pageParameterIndex == -1) { // 如果解析结果参数中没有Page类型,则不支持分页
isPagination = false;
}
if (isCacheDelete && fkAnnotationIndex == -1) { // 如果参数中没有@Fk注解,则不支持Cache更新
isCacheDelete = false;
}
if (isGenericCache && genericTableIndex == -1) { // 如果为同一处理的缓存但却没有相应的@GenericTable注解,则不支持同一处理,且不缓存
isGenericCache = false;
isCachable = false;
}
if ((isCachable && isReturnCollection && fkAnnotationIndex == -1) // 如果返回为集合但是参数中没有@Fk注解,则不支持Cache
|| (isCachable && !isReturnCollection && !isReturnCachable)) { // 如果返回不为集合但是却没有实现Cachable接口,则不支持Cache
isCachable = false;
}
if (isCachable && isCacheDelete) { // 不能同时支持两个,已删除为准
isCachable = false;
}
}
private void getPageIndex(Class<?>[] parameterTypes) {
int length = parameterTypes.length;
for (int index = 0; index < length; index++) {
Class<?> parameterType = parameterTypes[index];
if (parameterType.equals(Page.class)) {
pageParameterIndex = index;
}
}
}
private boolean isTypeCollection(Class<? extends Object> type) {
return (type != null && (type.isArray() || Collection.class.isAssignableFrom(type)));
}
private boolean isTypeCachable(Class<? extends Object> type) {
return (type != null && Cachable.class.isAssignableFrom(type));
}
private boolean isCategoryTypeGeneric(CacheCategoryType type) {
switch (type) {
case COMMENT:
return true;
default:
return false;
}
}
private boolean isNeedCacheOpration() {
return (isCachable || isCacheDelete);
}
public String buildCacheKey(Object[] arguments) {
if (!isCachable) {
return "";
}
String key = keyPattern;
Param[] paramAnnotations = (Param[]) parameterAnnotations.get(Param.class);
for (int index = 0; index < parameterCount; index++) {
Param param = paramAnnotations[index];
if (param == null) {
continue;
}
String value = param.value();
String replaceToken = new StringBuilder(":").append(value).toString();
key = key.replace(replaceToken, arguments[index].toString());
}
StringBuilder actualKey = new StringBuilder();
if (isGenericCache) {
actualKey.append(arguments[genericTableIndex]).append(key);
} else {
actualKey.append(key);
}
if (isPagination) {
Page page = (Page) arguments[pageParameterIndex];
if (page == null) {// 由于每个分页需要一个key,支持分页但是没有传分页对象,则表示复用了分页,此时不分页,加上-nop以区分
actualKey.append("-nop");
} else {
actualKey.append("-p").append(page.getCurpage());
}
}
return actualKey.toString();
}
public String buildVersionKey(Object[] arguments, Cachable cachedObject) {
if (!isNeedCacheOpration()) {
return "";
}
StringBuilder builder = null;
if (isGenericCache) {
builder = new StringBuilder(arguments[genericTableIndex].toString());
} else {
builder = new StringBuilder(categoryType.toString());
}
String fk = (fkAnnotationIndex == -1) ? cachedObject.getFkId()
: arguments[fkAnnotationIndex].toString();
builder.append('-').append(fk).append("-v");
return builder.toString();
}
public String buildCachePageKey(String key, Object[] arguments) {
if (!isCachable) {
return "";
}
Page page = (Page) arguments[pageParameterIndex];
if (page == null) {
return "";
}
if (!StringUtils.hasLength(key)) {
key = buildCacheKey(arguments);
}
StringBuilder builder = new StringBuilder("page-");
builder.append(key);
return builder.toString();
}
public Method getMethod() {
return method;
}
public Object getTarget() {
return target;
}
public boolean isCacheDelete() {
return isCacheDelete;
}
public boolean isCachable() {
return isCachable;
}
public String getKeyPattern() {
return keyPattern;
}
public long getExpire() {
return expire;
}
public boolean isPagination() {
return isPagination;
}
public Map<Class<? extends Annotation>, Annotation[]> getParameterAnnotations() {
return parameterAnnotations;
}
public int getParameterCount() {
return parameterCount;
}
public boolean isReturnCollection() {
return isReturnCollection;
}
public int getPageParameterIndex() {
return pageParameterIndex;
}
public int getFkAnnotationIndex() {
return fkAnnotationIndex;
}
public CacheCategoryType getCategoryType() {
return categoryType;
}
public int getGenericTableIndex() {
return genericTableIndex;
}
}