1 import java.lang.annotation.Annotation;
2 import java.lang.reflect.Method;
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.concurrent.TimeoutException;
8 import java.util.regex.Matcher;
9 import java.util.regex.Pattern;
10
11 import javax.annotation.Resource;
12
13 import net.rubyeye.xmemcached.MemcachedClient;
14 import net.rubyeye.xmemcached.exception.MemcachedException;
15
16 import ognl.Ognl;
17 import ognl.OgnlException;
18
19 import org.aspectj.lang.ProceedingJoinPoint;
20 import org.aspectj.lang.annotation.Around;
21 import org.aspectj.lang.annotation.Aspect;
22 import org.aspectj.lang.reflect.MethodSignature;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25 import org.springframework.stereotype.Component;
26
27 @Component
28 @Aspect
29 public class MemcachedCacheInterceptor {
30
31 private final String GET = "@annotation(LoadFromMemcached)";
32 private final String UPDATE = "@annotation(UpdateForMemcached)";
33 //替换为其他缓存组件即可切换为其他缓存系统,这里是使用的Memcached。如果再抽象一层缓存系统管理,则可以动态的更换缓存系统。
34 @Resource
35 private MemcachedClient cache;
36
37 private Logger log = LoggerFactory.getLogger(MemcachedCacheInterceptor.class);
38
39 /**
40 *
41 * @Title: get
42 * @Description: 首先从缓存中加载数据,缓存命中则返回数据,未命中则从数据库查找,并加入缓存
43 * @param @param call
44 * @param @return
45 * @param @throws Throwable
46 * @return Object
47 * @throws
48 */
49 @Around(GET)
50 public Object get(ProceedingJoinPoint call) throws Throwable {
51
52 LoadFromMemcached anno = getAnnotation(call,LoadFromMemcached.class);
53 String key = anno.value();
54 int timeSocpe = anno.timeScope();
55
56 if(!executeCondition(anno.condition(),call)){//不满足条件,直接调用方法,不进行缓存AOP操作
57 return call.proceed();
58 }
59
60 key = getKeyNameFromParam(key,call);
61
62 Object value = null;
63
64 try {
65 value = cache.get(key);
66 } catch (TimeoutException e) {
67 log.error("Get Data From Memcached TimeOut!About Key:"+key,e);
68 e.printStackTrace();
69 } catch (InterruptedException e) {
70 log.error("Get Data From Memcached TimeOut And Interrupted!About Key:"+key,e);
71 e.printStackTrace();
72 } catch (MemcachedException e) {
73 log.error("Get Data From Memcached And Happend A Unexpected Error!About Key:"+key,e);
74 e.printStackTrace();
75 }
76
77 if(value == null){
78 value = call.proceed();
79 if(value != null){
80 try {
81 cache.add(key, timeSocpe, value);
82 log.info("Add Data For Memcached Success!About Key:"+key);
83 } catch (TimeoutException e) {
84 log.error("Add Data For Memcached TimeOut!About Key:"+key,e);
85 e.printStackTrace();
86 } catch (InterruptedException e) {
87 log.error("Add Data For Memcached TimeOut And Interrupted!About Key:"+key,e);
88 e.printStackTrace();
89 } catch (MemcachedException e) {
90 log.error("Add Data For Memcached And Happend A Unexpected Error!About Key:"+key,e);
91 e.printStackTrace();
92 }
93 }
94 }
95
96 return value;
97 }
98
99 /**
100 *
101 * @Title: update
102 * @Description: 执行方法的同时更新缓存中的数据
103 * @param @param call
104 * @param @return
105 * @param @throws Throwable
106 * @return Object
107 * @throws
108 */
109 @Around(UPDATE)
110 public Object update(ProceedingJoinPoint call) throws Throwable {
111
112 UpdateForMemcached anno = getAnnotation(call,UpdateForMemcached.class);
113 String[] key = anno.value();//可能需要更新多个key
114
115 Object value = call.proceed();
116 if(!executeCondition(anno.condition(),call)){//不满足条件,直接调用方法,不进行缓存AOP操作
117 return value;
118 }
119
120 if(value != null){
121 try {
122 for(String singleKey:key){//循环处理所有需要更新的key
123 String tempKey = getKeyNameFromParam(singleKey, call);
124 cache.delete(tempKey);
125 }
126 log.info("Update Data For Memcached Success!About Key:"+key);
127 } catch (TimeoutException e) {
128 log.error("Update Data For Memcached TimeOut!About Key:"+key,e);
129 e.printStackTrace();
130 } catch (InterruptedException e) {
131 log.error("Update Data For Memcached TimeOut And Interrupted!About Key:"+key,e);
132 e.printStackTrace();
133 } catch (MemcachedException e) {
134 log.error("Update Data For Memcached And Happend A Unexpected Error!About Key:"+key,e);
135 e.printStackTrace();
136 }
137
138 }
139 return value;
140 }
141
142 /**
143 *
144 * @Title: getAnnotation
145 * @Description: 获得Annotation对象
146 * @param @param <T>
147 * @param @param jp
148 * @param @param clazz
149 * @param @return
150 * @return T
151 * @throws
152 */
153 private <T extends Annotation> T getAnnotation(ProceedingJoinPoint jp,Class<T> clazz){
154 MethodSignature joinPointObject = (MethodSignature) jp.getSignature();
155 Method method = joinPointObject.getMethod();
156 return method.getAnnotation(clazz);
157 }
158
159 /**
160 *
161 * @Title: getKeyNameFromParam
162 * @Description: 获得组合后的KEY值
163 * @param @param key
164 * @param @param jp
165 * @param @return
166 * @return String
167 * @throws
168 */
169 private String getKeyNameFromParam(String key,ProceedingJoinPoint jp){
170 if(!key.contains("$")){
171 return key;
172 }
173
174 String regexp = "\\$\\{[^\\}]+\\}";
175 Pattern pattern = Pattern.compile(regexp);
176 Matcher matcher = pattern.matcher(key);
177 List<String> names = new ArrayList<String>();
178 try{
179 while(matcher.find()){
180 names.add(matcher.group());
181 }
182 key = executeNames(key,names,jp);
183 }catch (Exception e) {
184 log.error("Regex Parse Error!", e);
185 }
186
187
188 return key;
189 }
190
191 /**
192 *
193 * @Title: executeNames
194 * @Description: 对KEY中的参数进行替换
195 * @param @param key
196 * @param @param names
197 * @param @param jp
198 * @param @return
199 * @param @throws OgnlException
200 * @return String
201 * @throws
202 */
203 private String executeNames(String key, List<String> names,ProceedingJoinPoint jp) throws OgnlException {
204
205 Method method = ((MethodSignature)jp.getSignature()).getMethod();
206
207 //形参列表
208 List<String> param = MethodParamNamesScaner.getParamNames(method);
209
210 if(names==null||names.size()==0){
211 return key;
212 }
213
214 Object[] params = jp.getArgs();
215
216 Map<String,Object> map = new HashMap<String,Object>();
217 for(int i=0;i<param.size();i++){
218 map.put(param.get(i), params);
219 }
220
221 for(String name:names){
222 String temp = name.substring(2);
223 temp = temp.substring(0,temp.length()-1);
224 key = myReplace(key,name, (String)Ognl.getValue(temp, map));
225 }
226
227 return key;
228 }
229
230 /**
231 *
232 * @Title: myReplace
233 * @Description: 不依赖Regex的替换,避免$符号、{}等在String.replaceAll方法中当做Regex处理时候的问题。
234 * @param @param src
235 * @param @param from
236 * @param @param to
237 * @param @return
238 * @return String
239 * @throws
240 */
241 private String myReplace(String src,String from,String to){
242 int index = src.indexOf(from);
243 if(index == -1){
244 return src;
245 }
246
247 return src.substring(0,index)+to+src.substring(index+from.length());
248 }
249
250
251 /**
252 *
253 * @Title: executeCondition
254 * @Description: 判断是否需要进行缓存操作
255 * @param @param condition parm
256 * @param @return
257 * @return boolean true:需要 false:不需要
258 * @throws
259 */
260 private boolean executeCondition(String condition,ProceedingJoinPoint jp){
261
262 if("".equals(condition)){
263 return true;
264 }
265
266 Method method = ((MethodSignature)jp.getSignature()).getMethod();
267
268 //形参列表
269 List<String> param = MethodParamNamesScaner.getParamNames(method);
270
271 if(param==null||param.size()==0){
272 return true;
273 }
274
275 Object[] params = jp.getArgs();
276
277 Map<String,Object> map = new HashMap<String,Object>();
278 for(int i=0;i<param.size();i++){
279 map.put(param.get(i), params);
280 }
281 boolean returnVal = false;
282 try {
283 returnVal = (Boolean) Ognl.getValue(condition, map);
284 } catch (OgnlException e) {
285 e.printStackTrace();
286 }
287
288 return returnVal;
289 }
290
291 public void setCache(MemcachedClient cache) {
292 this.cache = cache;
293 }
294
295 }