主题:
这份代码是开发中常见的代码,查询数据库某个主表的数据,为了提高性能,做一次缓存,每次调用时先拿缓存数据,有则直接返回,没有才向数据库查数据,降低数据库压力。

public Merchant loadCachedMerchant(String merchantId) {
String key = this.createCacheKey(merchantId);
Merchant merchant = (Merchant) this.memCachedClient.get(key);// 先看缓存
if (merchant == null) {
merchant = this.merchantDao.searchMerchantByMerchantId1(merchantId);//候查数据库
if (merchant != null) {
merchant = extraCacheMerchant(merchant);
this.memCachedClient.put(key, merchant, 5 * 60);
}
} return merchant;
}

然而一般我门在项目初始或在发展的过程中,并不能很好的规划出哪些业务时需要缓存的,而我们发现这个先取缓存再数据库的逻辑是共通的,那么我们就想到了面向切面开发来解决这个问题也是很合适的。

构想下,可以想到我们可以利用注解,对那些需要有这段缓存逻辑的方法单独提供一个注解,这样我们就能找到这些切面,也就是有这个特定注解的方法,比如叫@ServiceCache

这个@ServiceCache注解需要的参数有缓存key的组成方式,以及有效时间。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceCache {
int value() default -1; int expire() default 60; ServiceCache.Key key() default ServiceCache.Key.JSON; String[] includeKeys() default {}; boolean sync() default false; boolean nullPattern() default true; public static enum Key {
JSON,
TO_STRING; private Key() {
}
}
}

这里又加了一个sync参数用来控制同步执行,这个同步的参数是用来解决什么问题的呢?我们都明白这里使用缓存的方式是为了解决对数据库的频繁调用的问题,这些并发的调用可能导致数据库的压力,那么在我们的逻辑中:先检查缓存,在没有的情况下会去数据库找,而有一种极端的情况是,当大量请求并发时,到这个判断逻辑检查缓存同时发现没有,也就是数据库的数据尚未放入缓存,此时这些并发都会去数据库找,那么数据库依然有风险出现并发调用。

@Aspect
public class ServiceCacheAnnotationAspect {
private static Logger log = LoggerFactory.getLogger(ServiceCacheAnnotationAspect.class);
private ICache serviceCache;
private Object syncLock = new Object(); public ServiceCacheAnnotationAspect() {
}
/**
* 切面匹配为class使用了@Service 或 @Repository 并且方法使用了自定义的@ServiceCache
**/
@Around("(@within(org.springframework.stereotype.Service)||
    @within(org.springframework.stereotype.Repository))&&
    @annotation(com.xiaoka.freework.cache.annotation.ServiceCache)")
private Object cacheProcess(ProceedingJoinPoint jp) throws Throwable {
Class targetClz = jp.getTarget().getClass();
String methodName = jp.getSignature().getName();
if(!(jp.getSignature() instanceof MethodSignature)) {
log.warn("该方法接口无法启用缓存功能: {}", jp.getSignature().toLongString());
return jp.proceed();
} else {
MethodSignature methodSign = (MethodSignature)jp.getSignature();
ServiceCache sc = ServiceCacheUtils.single().findServiceCache(targetClz, methodSign.getMethod());
if(sc == null) {
return jp.proceed();
} else {
int expire = sc.value() >= 0?sc.value():sc.expire();//获取定义的过期时间
if(expire > 0) {
String cacheKey = ServiceCacheUtils.single().buildCacheKey(sc, targetClz, methodName, jp.getArgs());
Object rval = null;
if(sc.sync()) {
Object var9 = this.syncLock;
synchronized(this.syncLock) {// 这里做了同步
rval = this.cacheInvoke(sc, jp, cacheKey, expire);//这里实现我们核心逻辑
}
} else {
rval = this.cacheInvoke(sc, jp, cacheKey, expire);
} return rval instanceof ServiceCacheAnnotationAspect.Blank?null:rval;
} else {
return jp.proceed();
}
}
}
} private Object cacheInvoke(ServiceCache sc, ProceedingJoinPoint jp, String cacheKey, int expire) throws Throwable {
log.debug("Load from cache for key : {}", cacheKey);
Object rval = this.serviceCache.get(cacheKey);
if(rval == null) {//缓存中没有,就要去数据库拿,拿完就放缓存
log.info("Miss from cache, load backend for key : {}", cacheKey);
rval = jp.proceed();//执行目标方法
rval = rval == null && sc.nullPattern()?ServiceCacheAnnotationAspect.Blank.INST:rval;
if(rval != null) {
this.serviceCache.put(cacheKey, rval, expire);
}
} return rval;
} public void setServiceCache(ICache serviceCache) {
this.serviceCache = serviceCache;
ServiceCacheUtils.single().setCache(serviceCache);
} private static class Blank implements Serializable {
private static final long serialVersionUID = 3203712628835590212L;
private static final ServiceCacheAnnotationAspect.Blank INST = new ServiceCacheAnnotationAspect.Blank(); private Blank() {
}
}
}

实际使用中的例子是这样的:

@ServiceCache(expire = 600, includeKeys = { "name" })
public CarProvinceEntity selectProvinceByName(String name) {
return commonDao.mapper(CarProvinceEntity.class).source(Source.SLAVE)
.sql("selectByName").session().selectOne(name);
}

如此,对于开发业务的人员来说就比较方便了。通过aop结合注解,可以在项目中做一些切面的事情已经成为很多项目底层框架的一部分,比如计算方法耗时,打日志,国际化等等的业务。

相关知识点链接:

http://www.cnblogs.com/killbug/p/5271291.html

http://samter.iteye.com/blog/410618
http://itindex.net/detail/29812-aop
http://www.ibm.com/developerworks/cn/java/j-lo-aopi18n/index.html

--------------------20161020 补充线-----------------------

发现,实际项目编码时经常用到aop的思想,利用spring实现非常方便,所以再补充一个简易的例子:

因为业务方法调用可能抛出异常,但是有些方法在有异常情况下也不应该阻断流程,所以要try catch住,所以就写一个注解统一将这个需求做掉。

注解:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoThrowException { }

注解解析,利用spring切面匹配注解,代理实际业务方法,catch住异常。

@Aspect
public class NoThrowExceptionAspect { @Around("@annotation(aspect.test.annotation.NoThrowException)")
public void tryAround(ProceedingJoinPoint joinPoint){
try{
joinPoint.proceed();
} catch (Throwable throwable) {
// throwable.printStackTrace();
}
}
}

实际使用:

 @NoThrowException
public void dost() throws Exception { System.out.printf("do st");
throw new Exception("TEST");
}

以上非常简单明了,spring的很多功能后续要深入使用。

遇到瓶颈,要静下心来,加油!

利用spring AOP 和注解实现方法中查cache-我们到底能走多远系列(46)的更多相关文章

  1. js中this和回调方法循环-我们到底能走多远系列(35)

    我们到底能走多远系列(35) 扯淡: 13年最后一个月了,你们在13年初的计划实现了吗?还来得及吗? 请加油~ 主题: 最近一直在写js,遇到了几个问题,可能初入门的时候都会遇到吧,总结下. 例子: ...

  2. 服务调用方案(Spring Http Invoker) - 我们到底能走多远系列(40)

    我们到底能走多远系列(40) 扯淡:  判断是否加可以效力于这家公司,一个很好的判断是,接触下这公司工作几年的员工,了解下生活工作状态,这就是你几年后的状态,如果满意就可以考虑加入了. 主题: 场景: ...

  3. 定时任务管理中心(dubbo+spring)-我们到底能走多远系列47

    我们到底能走多远系列47 扯淡: 又是一年新年时,不知道上一年你付出了多少,收获了多少呢?也许你正想着老板会发多少奖金,也许你正想着明年去哪家公司投靠. 这个时间点好好整理一下,思考总结一下,的确是个 ...

  4. Spring mvc源码url路由-我们到底能走多远系列(38)

    我们到底能走多远系列38 扯淡: 马航的事,挺震惊的.还是多多珍惜身边的人吧. 主题: Spring mvc 作为表现层的框架,整个流程是比较好理解的,毕竟我们做web开发的,最早也经常接触的就是一个 ...

  5. Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)

    我们到底能走多远系列(33) 扯淡: 各位:    命运就算颠沛流离   命运就算曲折离奇   命运就算恐吓着你做人没趣味   别流泪 心酸 更不应舍弃   ... 主题: Spring源码阅读还在继 ...

  6. 初始化IoC容器(Spring源码阅读)-我们到底能走多远系列(31)

    我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? 毕竟,对自己的收入的分配差不多体现了自己的 ...

  7. 利用Spring AOP自定义注解解决日志和签名校验

    转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先 ...

  8. (转)利用Spring AOP自定义注解解决日志和签名校验

    一.需解决的问题 部分API有签名参数(signature),Passport首先对签名进行校验,校验通过才会执行实现方法. 第一种实现方式(Origin):在需要签名校验的接口里写校验的代码,例如: ...

  9. 化繁就简,如何利用Spring AOP快速实现系统日志

    1.引言 有关Spring AOP的概念就不细讲了,网上这样的文章一大堆,要讲我也不会比别人讲得更好,所以就不啰嗦了. 为什么要用Spring AOP呢?少写代码.专注自身业务逻辑实现(关注本身的业务 ...

随机推荐

  1. 394. Decode String

    [题目] Total Accepted: 10087 Total Submissions: 25510 Difficulty: Medium Contributors: Admin Given an ...

  2. 美化iTerm2

    一.下载iTerm2,启动 二.安装oh-my-zsh curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/insta ...

  3. 使用keytool生产jks证书

    使用JDK中的keytool生成服务器证书 1.创建服务器KeyStorekeytool -genkey -alias server_jks_cennavi -keyalg RSA  -keystor ...

  4. The connection to adb is down, and a severe error has occured.问题解决方法小结

    遇到了几次这个问题:The connection to adb is down, and a severe error has occured. You must restart adb and Ec ...

  5. Buffer、Channel示例

      a.txt 孔雀向西飞,今朝更好看.孔雀向西飞,今朝更好看.孔雀向西飞,今朝更好看.孔雀向西飞,今朝更好看. 示例一. package com.test; import java.io.FileI ...

  6. IE 文档模式

    <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv= ...

  7. Visual Studio 不生成.vshost.exe和.pdb文件的方法【转】

    Visual Studio 不生成.vshost.exe和.pdb文件的方法[转] 使用Visual Studio编译工程时,默认设置下,即使选择了「Release」时也会生成扩展名为「.vshost ...

  8. 设置DIV块元素在浏览器页面中垂直居中

    任务目标 实践HTML/CSS布局方式 深入了解position等CSS属性 任务描述 实现如 示例图(点击打开) 的效果 灰色元素水平垂直居中,有两个四分之一圆位于其左上角和右下角. 任务注意事项 ...

  9. android densityDpi 的由来

    ---恢复内容开始--- 今天做屏幕适配的时候,发现一个奇怪的现象: HTC D820u/ 红米Note/HONOR H30-L02 /Coolpad 8297-T01 4款手机的分辨率均为 1280 ...

  10. swift 构建类

    参开 http://blog.csdn.net/chelongfei/article/details/49784633 在 Swift 中, 类的初始化有两种方式, 分别是 Designated In ...