Case Study(记一次SpringAOP实践过程-包扫描和嵌套注解)
前言
每一次实践得出结论,得出的对过往理论的印证,都是一次悟道,其收益远大于争论和抱怨。
技术是一件比较客观的事,正确与错误,其实就摆在哪里,意见不统一,写段代码试验一下就好了,一段代码印证不了的时候,就多写几段。
先同一个案例说起
挺简单的一个案例,通过SpringAOP和注解,使用Guava缓存。代码如下:
GuavaCache.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GuavaCache {
/**
* group : 一个group代表一个cache,不传或者""则使用 class+method 作为cache名字
* @return
*/
public String group() default "";
/**
* key : 注意,所有参数必须实现GuavaCacheInterface接口,如果不实现,则会用toString()的MD5作为Key
* @return
*/
public String key() default "";
/**
* 过期时间,缺省30秒
* @return
*/
public long timeout() default 30;
/**
* 缓存最大条目,缺省10000
* @return
*/
public long size() default 10000;
/**
* 是否打印日志
* @return
*/
public boolean debug() default false;
}
GuavaInterface.java
/**
* 使用GuavaCache注解时,如果传入参数是对象,则必须实现这个类
*
*/
public interface GuavaCacheInterface {
public String getCacheKey();
}
GuavaCacheProcessor.java
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Maps;
/**
* GuavaCache注解处理器
*
*/
@Component
@Aspect
public class GuavaCacheProcessor {
private static final Logger logger = LoggerFactory.getLogger(GuavaCacheProcessor.class);
private static Map<String, Cache<String, Object>> cacheMap = Maps.newConcurrentMap();
@Around("execution(* *(..)) && @annotation(guavaCache)")
public Object aroundMethod(ProceedingJoinPoint pjd, GuavaCache guavaCache) throws Throwable {
Cache<String, Object> cache = getCache(pjd, guavaCache);
String key = getKey(pjd);
boolean keyisnull = (null == key) || ("".equals(key));
if(guavaCache.debug()) {
logger.info("GuavaCache key : {} begin", key);
}
Object result = null;
if(!keyisnull) {
result = cache.getIfPresent(key);
if(result != null) {
return result;
}
}
try {
result = pjd.proceed();
if(!keyisnull) {
cache.put(key, result);
}
} catch (Exception e) {
throw e;
}
if(guavaCache.debug()) {
logger.info("GuavaCache key : {} end", key);
}
return result;
}
/**
* 获取Cache
* @param pjd
* @param guavaCache
* @return
*/
private Cache<String, Object> getCache(ProceedingJoinPoint pjd, GuavaCache guavaCache) {
String group = guavaCache.group();
if(group == null || "".equals(group)) {
MethodSignature signature = (MethodSignature) pjd.getSignature();
Method method = signature.getMethod();
Class<?> clazz = method.getDeclaringClass();
group = clazz.getName();
}
Cache<String, Object> cache = cacheMap.get(group);
if(cache == null) {
cache = CacheBuilder.newBuilder()
.maximumSize(guavaCache.size())
.expireAfterWrite(guavaCache.timeout(), TimeUnit.SECONDS)
.build();
cacheMap.put(group, cache);
}
return cache;
}
/**
* 获取Key:方法名+getCacheKey方法(如果没有,则用toString())的MD5值
* @param pjd
* @return
*/
private String getKey(ProceedingJoinPoint pjd) {
StringBuilder sb = new StringBuilder();
MethodSignature signature = (MethodSignature) pjd.getSignature();
Method method = signature.getMethod();
sb.append(method.getName());
for(Object param : pjd.getArgs()) {
if(GuavaCacheInterface.class.isAssignableFrom(param.getClass())) {
sb.append(((GuavaCacheInterface)param).getCacheKey());
} else {
if(!param.getClass().isPrimitive()) {
return null;
}
sb.append(param.toString());
}
}
String key = md5(sb.toString());
return key;
}
/**
* 进行MD5加密
*
* @param info
* 要加密的信息
* @return String 加密后的字符串
*/
private static String md5(String info) {
byte[] digesta = null;
try {
// 得到一个md5的消息摘要
MessageDigest alga = MessageDigest.getInstance("MD5");
// 添加要进行计算摘要的信息
alga.update(info.getBytes());
// 得到该摘要
digesta = alga.digest();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 将摘要转为字符串
String rs = byte2hex(digesta);
return rs;
}
/**
* 将二进制转化为16进制字符串
*
* @param b
* 二进制字节数组
* @return String
*/
private static String byte2hex(byte[] b) {
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1) {
hs = hs + "0" + stmp;
} else {
hs = hs + stmp;
}
}
return hs.toUpperCase();
}
}
遇到的第一个问题
问题出现
当bean在applicationContext.xml中通过
初步解决
把对于AOP的定义
期间的胡思乱想 context:component-scan 会不会不扫依赖的jar中的bean(因为注解时在依赖的jar包中定义的) 会不会扫描有顺序,先扫自己的,再扫依赖的jar的,由于有先后顺序,导致Spring加载Service类时需要的注解类先没扫到