1. 程式人生 > >理一理ehcache-spring-annotations快取的Key生成

理一理ehcache-spring-annotations快取的Key生成

pom.xml

        <!-- ehcache -->
        <dependency>
            <groupId>com.googlecode.ehcache-spring-annotations</groupId>
            <artifactId>ehcache-spring-annotations</artifactId>
            <version>1.2.0</version>
        </dependency>
        <dependency
>
<groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>${ehcache.version}</version> <type>pom</type> </dependency> <dependency> <groupId>
net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>${ehcache.version}</version> </dependency>

說明:
本文說的不是Spring Cache, ehcache-spring-annotations需要配合ehcache使用. 在程式碼中只是加註解(@Cacheable)而已

    @Cacheable(cacheName="myCache"
) public ForumPermissions getPermissions(GlobalPermissionEnum permissonType){ }

@Cacheable的全名: com.googlecode.ehcache.annotations.Cacheable,不要跟Spring Cache的同名註解(org.springframework.cache.annotation.Cacheable)混為一體

@Cacheable

@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {

    public String cacheName();

    public boolean cacheNull() default true;

    public DecoratedCacheType decoratedCacheType() default DecoratedCacheType.NONE;

    @Deprecated
    public boolean selfPopulating() default false;

    public int selfPopulatingTimeout() default 0;

    public long refreshInterval() default 60000L;

    public String keyGeneratorName() default "";

    public String exceptionCacheName() default "";

    public KeyGenerator keyGenerator() default @KeyGenerator(name = "");

    public String cacheableInteceptorName() default "";

    public String resolverFactoryName() default "";

    public ResolverFactory resolverFactory() default @ResolverFactory(name = "");
}

說明:
keyGeneratorName和keyGenerator在下一篇中詳細說說.refreshInterval預設是一分鐘(60000L).一般只需要為cacheName設定一個值

快取的Key
記憶體快取大多都是key-value鍵值儲存,註解中的keyGeneratorName和keyGenerator是可以指定一個自定義key生成策略.不指定預設使用的是:com.googlecode.ehcache.annotations.key.HashCodeCacheKeyGenerator

package com.googlecode.ehcache.annotations.key;
/**
 * @author Eric Dalquist
 * @version $Revision: 656 $
 */
public class HashCodeCacheKeyGenerator extends AbstractHashingCacheKeyGenerator<HashCodeCacheKeyGenerator.LongGenerator, Long> {}

這裡都不一一列出類的層次結構了.有興趣的可以翻牆去下原始碼.下面是具體作生成的方法

package com.googlecode.ehcache.annotations.key;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.IdentityHashMap;
import java.util.Map;
import org.aopalliance.intercept.MethodInvocation;

/**
 * Base class for cache key generators. Handles common logic for including/excluding the method
 * signature from key generation. Also provides support for avoiding circular references for key
 * generators that traverse the argument object graph via the {@link #register(Object)} and
 * {@link #unregister(Object)} APIs 
 * 
 * @author Eric Dalquist
 * @version $Revision: 656 $
 */
public abstract class AbstractCacheKeyGenerator<T extends Serializable> implements CacheKeyGenerator<T> {
     //ETC

     /* (non-Javadoc)
     * @see com.googlecode.ehcache.annotations.key.CacheKeyGenerator#generateKey(org.aopalliance.intercept.MethodInvocation)
     */
    public T generateKey(MethodInvocation methodInvocation) {
        final Object[] arguments = methodInvocation.getArguments();

        if (this.includeMethod) {
            final Method method = methodInvocation.getMethod();

            final Class<?> declaringClass = method.getDeclaringClass();
            final String name = method.getName();
            final Class<?> returnType = method.getReturnType();

            if (this.includeParameterTypes) {
                final Class<?>[] parameterTypes = method.getParameterTypes();
                return this.generateKey(declaringClass, name, returnType, parameterTypes, arguments);
            }

            return this.generateKey(declaringClass, name, returnType, arguments);
        }

        try {
            return this.generateKey(arguments);
        }
        finally {
            if (this.checkforCycles) {
                //Cleanup our thread local data
                REGISTRY.remove();
            }
        }
    }   
}

說明:

MethodInvocation 在aopalliance-1.0.jar中的org.aopalliance.intercept包下,MethodInvocation 繼承了Invocation可以拿到方法的引數值(getArguments),其它的都是反射包下的Method可以拿到的

method.getName()返回方法的名字
method.getReturnType()返回方法的返回值型別
method.getParameterTypes()返回方法的引數型別

在程式碼中定位一個方法的元素都有了:類全名 + 方法的簽名(返回值 + 方法名 + 引數列表 )

引數的值不同也可以生成唯一的key

package com.googlecode.ehcache.annotations.interceptor;
/**
 * Intercepter that handles invocations on methods annotated with {@link Cacheable} or {@link TriggersRemove}.
 * 
 * @author Eric Dalquist
 * @version $Revision: 681 $
 */
public class EhCacheInterceptor implements MethodInterceptor {
    //ETC

    /**
     * Creates a {@link Serializable} cache key from the {@link MethodInvocation} and configuration attributes.
     * 
     * @param methodInvocation Invocation to build the key for
     * @param methodAttribute Configuration for the invoked method
     * @return Generated cache key, must not return null.
     */
    protected Serializable generateCacheKey(MethodInvocation methodInvocation, final MethodAttribute methodAttribute) {
        final CacheKeyGenerator<? extends Serializable> cacheKeyGenerator = methodAttribute.getCacheKeyGenerator();

        final ParameterMask parameterMask = methodAttribute.getCacheKeyParameterMask();
        if (parameterMask.shouldMask()) {
            methodInvocation = new ParameterFilteringMethodInvocation(methodInvocation, parameterMask);
        }

        final Serializable cacheKey = cacheKeyGenerator.generateKey(methodInvocation);
        this.logger.debug("Generated key '{}' for invocation: {}", cacheKey, methodInvocation);
        return cacheKey;
    }
}

debug日誌

Generated key '-27052115907236245' for invocation: ReflectiveMethodInvocation: 
public net.htage.forum.entity.ForumPermissions net.htage.forum.impl.service.spring.EhcacheCacheBox.getPermissions(net.htage.forum.entity.define.GlobalPermissionEnum); 
target is of class [net.htage.forum.impl.service.spring.EhcacheCacheBox]

Generated key '-27052113702228882' for invocation: ReflectiveMethodInvocation: 
public net.htage.forum.entity.ForumPermissions net.htage.forum.impl.service.spring.EhcacheCacheBox.getPermissions(net.htage.forum.entity.define.GlobalPermissionEnum); 
target is of class [net.htage.forum.impl.service.spring.EhcacheCacheBox]

ReflectiveMethodInvocation :
可見性修飾符(public) +
返回值型別(net.htage.forum.entity.ForumPermissions) +
方法全名(net.htage.forum.impl.service.spring.EhcacheCacheBox.getPermissions) +
( +
引數型別列表(net.htage.forum.entity.define.GlobalPermissionEnum) +
);

也說明如果一個方法沒有引數,第一次呼叫生成key並快取,以後的N次呼叫都是同一份快取. 在debug日誌中只能搜到一份記錄

Generated key '-937817555943' for invocation: ReflectiveMethodInvocation: 
public java.util.List net.htage.forum.impl.service.spring.EhcacheCacheBox.getAllLevel(); 
target is of class [net.htage.forum.impl.service.spring.EhcacheCacheBox]

對於全域性的配置來說沒問題, N次呼叫後都是一樣的結果. 但是?具有時間性的方法(同時它沒有引數)則不然。第二次呼叫到第100呼叫之間資料肯定有變化的.哪這個預設的key生成策略還能用嗎?