Use spring AOP and annotation implementation methods to check the cache-How far can we go series (46), aopcache-
Subject:
This code is a common code used in development. It queries the data of a master table in a database. To improve performance, a cache is performed. cache data is obtained for each call, and data is directly returned, no data can be queried from the database to reduce the pressure on the database.
Public Merchant loadCachedMerchant (String merchantId) {String key = this. createCacheKey (merchantId); Merchant merchant = (Merchant) this. memCachedClient. get (key); // first check the cache if (merchant = null) {merchant = this. merchantDao. searchMerchantByMerchantId1 (merchantId); // query the database if (merchant! = Null) {merchant = extraCacheMerchant (merchant); this. memCachedClient. put (key, merchant, 5*60);} return merchant ;}
However, during the initial or development of the project, I cannot plan the services that need to be cached, however, we found that the logic of first caching and then database is common, so we thought of cross-section development to solve this problem.
We can imagine that we can use annotations to provide a separate annotation for those methods that require this caching logic, so that we can find these aspects, that is, the method with this specific annotation, for example @ ServiceCache
The parameters required for this @ ServiceCache annotation include the composition of the cache key and the validity period.
@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() { } }}
Here, a sync parameter is added to control synchronous execution. What problems does this synchronization parameter solve? We all understand that the cache is used to solve the problem of frequent database calls. These concurrent calls may cause pressure on the database. In our logic, we should first check the cache, when there are no concurrent requests, the database will be searched. However, in an extreme case, when a large number of requests are concurrently sent, the cache of the judgment logic check will not be found at the same time, that is, if the database data has not been cached, The concurrency will be searched by the database, and the database still has the risk of concurrent calls.
@ Aspectpublic class ServiceCacheAnnotationAspect {private static Logger log = LoggerFactory. getLogger (ServiceCacheAnnotationAspect. class); private ICache serviceCache; private Object syncLock = new Object (); public ServiceCacheAnnotationAspect () {}/*** @ Service or @ Repository is used for class aspect matching and the custom @ 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 ("This method interface cannot enable the cache function: {}", 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 (); // get the defined expiration time 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) {// synchronize rval = this. cacheInvoke (SC, jp, cacheKey, expire); // here we implement our core logic} else {rval = this. cacheInvoke (SC, jp, cacheKey, expire);} return rval insta Nceof 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) {// The cache does not exist, you need to get it from the database. After the cache is completed, the log.info ("Miss from cache, load backend for key: {} ", cacheKey); rval = jp. proceed (); // execution target method 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 (){}}}
The actual example is as follows:
@ServiceCache(expire = 600, includeKeys = { "name" }) public CarProvinceEntity selectProvinceByName(String name) { return commonDao.mapper(CarProvinceEntity.class).source(Source.SLAVE) .sql("selectByName").session().selectOne(name); }
In this way, it is more convenient for developers. Using aop combined with annotations, you can do some aspect work in the project, which has become a part of the underlying framework of many projects, such as the time-consuming computing method, logging, internationalization, and so on.
Links to related knowledge points:
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