`
jinnianshilongnian
  • 浏览: 21505210 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2418798
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:3008954
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5639578
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:259965
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1597402
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:250249
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5859076
Group-logo
跟我学Nginx+Lua开...
浏览量:702309
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:785274
社区版块
存档分类
最新评论

Spring Cache抽象详解

阅读更多

缓存简介

缓存,我的理解是:让数据更接近于使用者;工作机制是:先从缓存中读取数据,如果没有再从慢速设备上读取实际数据(数据也会存入缓存);缓存什么:那些经常读取且不经常修改的数据/那些昂贵(CPU/IO)的且对于相同的请求有相同的计算结果的数据。如CPU--L1/L2--内存--磁盘就是一个典型的例子,CPU需要数据时先从L1/L2中读取,如果没有到内存中找,如果还没有会到磁盘上找。还有如用过Maven的朋友都应该知道,我们找依赖的时候,先从本机仓库找,再从本地服务器仓库找,最后到远程仓库服务器找;还有如京东的物流为什么那么快?他们在各个地都有分仓库,如果该仓库有货物那么送货的速度是非常快的。

 

缓存命中率

即从缓存中读取数据的次数 与 总读取次数的比率,命中率越高越好:

命中率 = 从缓存中读取次数 / (总读取次数[从缓存中读取次数 + 从慢速设备上读取的次数])

Miss率 = 没有从缓存中读取的次数 / (总读取次数[从缓存中读取次数 + 从慢速设备上读取的次数])

 

这是一个非常重要的监控指标,如果做缓存一定要健康这个指标来看缓存是否工作良好;

 

缓存策略

Eviction policy

移除策略,即如果缓存满了,从缓存中移除数据的策略;常见的有LFU、LRU、FIFO:

FIFO(First In First Out):先进先出算法,即先放入缓存的先被移除;

LRU(Least Recently Used):最久未使用算法,使用时间距离现在最久的那个被移除;

LFU(Least Frequently Used):最近最少使用算法,一定时间段内使用次数(频率)最少的那个被移除;

 

TTL(Time To Live )

存活期,即从缓存中创建时间点开始直到它到期的一个时间段(不管在这个时间段内有没有访问都将过期)

 

TTI(Time To Idle)

空闲期,即一个数据多久没被访问将从缓存中移除的时间。

 

 

到此,基本了解了缓存的知识,在Java中,我们一般对调用方法进行缓存控制,比如我调用"findUserById(Long id)",那么我应该在调用这个方法之前先从缓存中查找有没有,如果没有再掉该方法如从数据库加载用户,然后添加到缓存中,下次调用时将会从缓存中获取到数据。

 

自Spring 3.1起,提供了类似于@Transactional注解事务的注解Cache支持,且提供了Cache抽象;在此之前一般通过AOP实现;使用Spring Cache的好处:

提供基本的Cache抽象,方便切换各种底层Cache;

通过注解Cache可以实现类似于事务一样,缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成;

提供事务回滚时也自动回滚缓存;

支持比较复杂的缓存逻辑;

 

对于Spring Cache抽象,主要从以下几个方面学习:

  • Cache API及默认提供的实现
  • Cache注解
  • 实现复杂的Cache逻辑

 

Cache API及默认提供的实现

Spring提供的核心Cache接口: 

package org.springframework.cache;

public interface Cache {
	String getName();  //缓存的名字
	Object getNativeCache(); //得到底层使用的缓存,如Ehcache
	ValueWrapper get(Object key); //根据key得到一个ValueWrapper,然后调用其get方法获取值
	<T> T get(Object key, Class<T> type);//根据key,和value的类型直接获取value
	void put(Object key, Object value);//往缓存放数据
	void evict(Object key);//从缓存中移除key对应的缓存
	void clear(); //清空缓存

	interface ValueWrapper { //缓存值的Wrapper
		Object get(); //得到真实的value
        }
}

提供了缓存操作的读取/写入/移除方法;

 

 

默认提供了如下实现:

ConcurrentMapCache:使用java.util.concurrent.ConcurrentHashMap实现的Cache;

GuavaCache:对Guava com.google.common.cache.Cache进行的Wrapper,需要Google Guava 12.0或更高版本,@since spring 4;

EhCacheCache:使用Ehcache实现

JCacheCache:对javax.cache.Cache进行的wrapper,@since spring 3.2;spring4将此类更新到JCache 0.11版本;

 

另外,因为我们在应用中并不是使用一个Cache,而是多个,因此Spring还提供了CacheManager抽象,用于缓存的管理: 

package org.springframework.cache;
import java.util.Collection;
public interface CacheManager {
	Cache getCache(String name); //根据Cache名字获取Cache 
	Collection<String> getCacheNames(); //得到所有Cache的名字
}

 

默认提供的实现: 

ConcurrentMapCacheManager/ConcurrentMapCacheFactoryBean:管理ConcurrentMapCache;

GuavaCacheManager;

EhCacheCacheManager/EhCacheManagerFactoryBean;

JCacheCacheManager/JCacheManagerFactoryBean;

 

另外还提供了CompositeCacheManager用于组合CacheManager,即可以从多个CacheManager中轮询得到相应的Cache,如

<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
    <property name="cacheManagers">
        <list>
            <ref bean="ehcacheManager"/>
            <ref bean="jcacheManager"/>
        </list>
    </property>
    <property name="fallbackToNoOpCache" value="true"/>
</bean>

当我们调用cacheManager.getCache(cacheName) 时,会先从第一个cacheManager中查找有没有cacheName的cache,如果没有接着查找第二个,如果最后找不到,因为fallbackToNoOpCache=true,那么将返回一个NOP的Cache否则返回null。

 

 

除了GuavaCacheManager之外,其他Cache都支持Spring事务的,即如果事务回滚了,Cache的数据也会移除掉。

 

Spring不进行Cache的缓存策略的维护,这些都是由底层Cache自己实现,Spring只是提供了一个Wrapper,提供一套对外一致的API。

 

示例

需要添加Ehcache依赖,具体依赖轻参考pom.xml

 

SpringCacheTest.java

    @Test
    public void test() throws IOException {
        //创建底层Cache
        net.sf.ehcache.CacheManager ehcacheManager
                = new net.sf.ehcache.CacheManager(new ClassPathResource("ehcache.xml").getInputStream());

        //创建Spring的CacheManager
        EhCacheCacheManager cacheCacheManager = new EhCacheCacheManager();
        //设置底层的CacheManager
        cacheCacheManager.setCacheManager(ehcacheManager);

        Long id = 1L;
        User user = new User(id, "zhang", "zhang@gmail.com");

        //根据缓存名字获取Cache
        Cache cache = cacheCacheManager.getCache("user");
        //往缓存写数据
        cache.put(id, user);
        //从缓存读数据
        Assert.assertNotNull(cache.get(id, User.class));
    }

此处直接使用Spring提供的API进行操作;我们也可以通过xml/注解方式配置到spring容器;

 

xml风格的(spring-cache.xml):

    <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml"/>
    </bean>

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehcacheManager"/>
        <property name="transactionAware" value="true"/>
    </bean>

spring提供EhCacheManagerFactoryBean来简化ehcache cacheManager的创建,这样注入configLocation,会自动根据路径从classpath下找,比编码方式简单多了,然后就可以从spring容器获取cacheManager进行操作了。此处的transactionAware表示是否事务环绕的,如果true,则如果事务回滚,缓存也回滚,默认false。

 

 

注解风格的(AppConfig.java):

    @Bean
    public CacheManager cacheManager() {

        try {
            net.sf.ehcache.CacheManager ehcacheCacheManager
                    = new net.sf.ehcache.CacheManager(new ClassPathResource("ehcache.xml").getInputStream());

            EhCacheCacheManager cacheCacheManager = new EhCacheCacheManager(ehcacheCacheManager);
            return cacheCacheManager;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

和编程方式差不多就不多介绍了。

另外,除了这些默认的Cache之外,我们可以写自己的Cache实现;而且即使不用之后的Spring Cache注解,我们也尽量使用Spring Cache API进行Cache的操作,如果要替换底层Cache也是非常方便的。到此基本的Cache API就介绍完了,接下来我们来看看使用Spring Cache注解来简化Cache的操作。

 

Cache注解

启用Cache注解

XML风格的(spring-cache.xml):

<cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>

另外还可以指定一个 key-generator,即默认的key生成策略,后边讨论;

 

 

注解风格的(AppConfig.java):

@Configuration
@ComponentScan(basePackages = "com.sishuok.spring.service")
@EnableCaching(proxyTargetClass = true)
public class AppConfig implements CachingConfigurer {
    @Bean
    @Override
    public CacheManager cacheManager() {

        try {
            net.sf.ehcache.CacheManager ehcacheCacheManager
                    = new net.sf.ehcache.CacheManager(new ClassPathResource("ehcache.xml").getInputStream());

            EhCacheCacheManager cacheCacheManager = new EhCacheCacheManager(ehcacheCacheManager);
            return cacheCacheManager;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return new SimpleKeyGenerator();
    }
}

1、使用@EnableCaching启用Cache注解支持;

2、实现CachingConfigurer,然后注入需要的cacheManager和keyGenerator;从spring4开始默认的keyGenerator是SimpleKeyGenerator;

 

@CachePut 

应用到写数据的方法上,如新增/修改方法,调用方法时会自动把相应的数据放入缓存: 

    @CachePut(value = "user", key = "#user.id")
    public User save(User user) {
        users.add(user);
        return user;
    }

即调用该方法时,会把user.id作为key,返回值作为value放入缓存;

 

@CachePut注解:

public @interface CachePut {
	String[] value();              //缓存的名字,可以把数据写到多个缓存
	String key() default "";       //缓存key,如果不指定将使用默认的KeyGenerator生成,后边介绍
	String condition() default ""; //满足缓存条件的数据才会放入缓存,condition在调用方法之前和之后都会判断
	String unless() default "";    //用于否决缓存更新的,不像condition,该表达只在方法执行之后判断,此时可以拿到返回值result进行判断了
}

 

@CacheEvict 

即应用到移除数据的方法上,如删除方法,调用方法时会从缓存中移除相应的数据:

    @CacheEvict(value = "user", key = "#user.id") //移除指定key的数据
    public User delete(User user) {
        users.remove(user);
        return user;
    }
    @CacheEvict(value = "user", allEntries = true) //移除所有数据
    public void deleteAll() {
        users.clear();
    }

 

@CacheEvict注解:

public @interface CacheEvict {
	String[] value();                        //请参考@CachePut
	String key() default "";                 //请参考@CachePut
	String condition() default "";           //请参考@CachePut
	boolean allEntries() default false;      //是否移除所有数据
	boolean beforeInvocation() default false;//是调用方法之前移除/还是调用之后移除

 

@Cacheable

应用到读取数据的方法上,即可缓存的方法,如查找方法:先从缓存中读取,如果没有再调用方法获取数据,然后把数据添加到缓存中:

   @Cacheable(value = "user", key = "#id")
    public User findById(final Long id) {
        System.out.println("cache miss, invoke find by id, id:" + id);
        for (User user : users) {
            if (user.getId().equals(id)) {
                return user;
            }
        }
        return null;
    }

 

@Cacheable注解:

public @interface Cacheable {
	String[] value();             //请参考@CachePut
	String key() default "";      //请参考@CachePut
	String condition() default "";//请参考@CachePut
	String unless() default "";   //请参考@CachePut  

 

运行流程

1、首先执行@CacheEvict(如果beforeInvocation=true且condition 通过),如果allEntries=true,则清空所有
2、接着收集@Cacheable(如果condition 通过,且key对应的数据不在缓存),放入cachePutRequests(也就是说如果cachePutRequests为空,则数据在缓存中)
3、如果cachePutRequests为空且没有@CachePut操作,那么将查找@Cacheable的缓存,否则result=缓存数据(也就是说只要当没有cache put请求时才会查找缓存)
4、如果没有找到缓存,那么调用实际的API,把结果放入result
5、如果有@CachePut操作(如果condition 通过),那么放入cachePutRequests
6、执行cachePutRequests,将数据写入缓存(unless为空或者unless解析结果为false);
7、执行@CacheEvict(如果beforeInvocation=false 且 condition 通过),如果allEntries=true,则清空所有

流程中需要注意的就是2/3/4步:

如果有@CachePut操作,即使有@Cacheable也不会从缓存中读取;问题很明显,如果要混合多个注解使用,不能组合使用@CachePut和@Cacheable;官方说应该避免这样使用(解释是如果带条件的注解相互排除的场景);不过个人感觉还是不要考虑这个好,让用户来决定如何使用,否则一会介绍的场景不能满足。

提供的SpEL上下文数据

Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:

名字 位置 描述 示例

methodName

root对象

当前被调用的方法名

#root.methodName

method

root对象

当前被调用的方法

#root.method.name

target

root对象

当前被调用的目标对象

#root.target

targetClass

root对象

当前被调用的目标对象类

#root.targetClass

args

root对象

当前被调用的方法的参数列表

#root.args[0]

caches

root对象

当前方法调用使用的缓存列表(如@Cacheable(value={"cache1", "cache2"})),则有两个cache

#root.caches[0].name

argument name

执行上下文

当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数

#user.id

result

执行上下文

方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,'cache evict'的beforeInvocation=false)

#result

通过这些数据我们可能实现比较复杂的缓存逻辑了,后边再来介绍。

 

Key生成器

如果在Cache注解上没有指定key的话@CachePut(value = "user"),会使用KeyGenerator进行生成一个key: 

public interface KeyGenerator {
	Object generate(Object target, Method method, Object... params);
}

默认提供了DefaultKeyGenerator生成器(Spring 4之后使用SimpleKeyGenerator): 

	@Override
	public Object generate(Object target, Method method, Object... params) {
		if (params.length == 0) {
			return SimpleKey.EMPTY;
		}
		if (params.length == 1 && params[0] != null) {
			return params[0];
		}
		return new SimpleKey(params);
	}

即如果只有一个参数,就使用参数作为key,否则使用SimpleKey作为key。

 

我们也可以自定义自己的key生成器,然后通过xml风格的<cache:annotation-driven key-generator=""/>或注解风格的CachingConfigurer中指定keyGenerator。 

 

条件缓存

根据运行流程,如下@Cacheable将在执行方法之前( #result还拿不到返回值)判断condition,如果返回true,则查缓存; 

@Cacheable(value = "user", key = "#id", condition = "#id lt 10")
public User conditionFindById(final Long id)

 

根据运行流程,如下@CachePut将在执行完方法后(#result就能拿到返回值了)判断condition,如果返回true,则放入缓存; 

@CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")
public User conditionSave(final User user) 

  

根据运行流程,如下@CachePut将在执行完方法后(#result就能拿到返回值了)判断unless,如果返回false,则放入缓存;(即跟condition相反)

@CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")
    public User conditionSave2(final User user) 
 
根据运行流程,如下@CacheEvict, beforeInvocation=false表示在方法执行之后调用(#result能拿到返回值了);且判断condition,如果返回true,则移除缓存;
    @CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")
    public User conditionDelete(final User user) 
 

@Caching

有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id-->user;username--->user;email--->user的缓存;此时就需要@Caching组合多个注解标签了。

 

如用户新增成功后,添加id-->user;username--->user;email--->user到缓存; 

    @Caching(
            put = {
                    @CachePut(value = "user", key = "#user.id"),
                    @CachePut(value = "user", key = "#user.username"),
                    @CachePut(value = "user", key = "#user.email")
            }
    )
    public User save(User user) {

  

@Caching定义如下: 

public @interface Caching {
	Cacheable[] cacheable() default {}; //声明多个@Cacheable
	CachePut[] put() default {};        //声明多个@CachePut
	CacheEvict[] evict() default {};    //声明多个@CacheEvict
}

 

自定义缓存注解

比如之前的那个@Caching组合,会让方法上的注解显得整个代码比较乱,此时可以使用自定义注解把这些注解组合到一个注解中,如: 

@Caching(
        put = {
                @CachePut(value = "user", key = "#user.id"),
                @CachePut(value = "user", key = "#user.username"),
                @CachePut(value = "user", key = "#user.email")
        }
)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UserSaveCache {
}
 

这样我们在方法上使用如下代码即可,整个代码显得比较干净。 

@UserSaveCache
public User save(User user)
  

示例

新增/修改数据时往缓存中写 

    @Caching(
            put = {
                    @CachePut(value = "user", key = "#user.id"),
                    @CachePut(value = "user", key = "#user.username"),
                    @CachePut(value = "user", key = "#user.email")
            }
    )
    public User save(User user)

 

    @Caching(
            put = {
                    @CachePut(value = "user", key = "#user.id"),
                    @CachePut(value = "user", key = "#user.username"),
                    @CachePut(value = "user", key = "#user.email")
            }
    )
    public User update(User user) 
删除数据时从缓存中移除
    @Caching(
            evict = {
                    @CacheEvict(value = "user", key = "#user.id"),
                    @CacheEvict(value = "user", key = "#user.username"),
                    @CacheEvict(value = "user", key = "#user.email")
            }
    )
    public User delete(User user) 
   @CacheEvict(value = "user", allEntries = true)
    public void deleteAll()
 
查找时从缓存中读
    @Caching(
            cacheable = {
                    @Cacheable(value = "user", key = "#id")
            }
    )
    public User findById(final Long id) 

 

   @Caching(
            cacheable = {
                    @Cacheable(value = "user", key = "#username")
            }
    )
    public User findByUsername(final String username) 

 

  @Caching(
            cacheable = {
                    @Cacheable(value = "user", key = "#email")
            }
    )
    public User findByEmail(final String email)
  

 

问题及解决方案

一、比如findByUsername时,不应该只放username-->user,应该连同id--->user和email--->user一起放入;这样下次如果按照id查找直接从缓存中就命中了;这需要根据之前的运行流程改造CacheAspectSupport: 

// We only attempt to get a cached result if there are no put requests
if (cachePutRequests.isEmpty() && contexts.get(CachePutOperation.class).isEmpty()) {
    result = findCachedResult(contexts.get(CacheableOperation.class));
}
改为:
Collection<CacheOperationContext> cacheOperationContexts = contexts.get(CacheableOperation.class);
if (!cacheOperationContexts.isEmpty()) {
    result = findCachedResult(cacheOperationContexts);
}

然后就可以通过如下代码完成想要的功能: 

    @Caching(
            cacheable = {
                    @Cacheable(value = "user", key = "#username")
            },
            put = {
                    @CachePut(value = "user", key = "#result.id", condition = "#result != null"),
                    @CachePut(value = "user", key = "#result.email", condition = "#result != null")
            }
    )
    public User findByUsername(final String username) {
        System.out.println("cache miss, invoke find by username, username:" + username);
        for (User user : users) {
            if (user.getUsername().equals(username)) {
                return user;
            }
        }
        return null;
    }
  

 

二、缓存注解会让代码看上去比较乱;应该使用自定义注解把缓存注解提取出去;

 

三、往缓存放数据/移除数据是有条件的,而且条件可能很复杂,考虑使用SpEL表达式:

    @CacheEvict(value = "user", key = "#user.id", condition = "#root.target.canCache() and #root.caches[0].get(#user.id).get().username ne #user.username", beforeInvocation = true)
    public void conditionUpdate(User user)
 或更复杂的直接调用目标对象的方法进行操作(如只有修改了某个数据才从缓存中清除,比如菜单数据的缓存,只有修改了关键数据时才清空菜单对应的权限数据) 
    @Caching(
            evict = {
                    @CacheEvict(value = "user", key = "#user.id", condition = "#root.target.canEvict(#root.caches[0], #user.id, #user.username)", beforeInvocation = true)
            }
    )
    public void conditionUpdate(User user) 
    public boolean canEvict(Cache userCache, Long id, String username) {
        User cacheUser = userCache.get(id, User.class);
        if (cacheUser == null) {
            return false;
        }
        return !cacheUser.getUsername().equals(username);
    }
如上方式唯一不太好的就是缓存条件判断方法也需要暴露出去;而且缓存代码和业务代码混合在一起,不优雅;因此把canEvict方法移到一个Helper静态类中就可以解决这个问题了:
@CacheEvict(value = "user", key = "#user.id", condition = "T(com.sishuok.spring.service.UserCacheHelper).canEvict(#root.caches[0], #user.id, #user.username)", beforeInvocation = true)
public void conditionUpdate(User user)

 

四、其实对于:id--->user;username---->user;email--->user;更好的方式可能是:id--->user;username--->id;email--->id;保证user只存一份;如:

@CachePut(value="cacheName", key="#user.username", cacheValue="#user.username")
public void save(User user) 
@Cacheable(value="cacheName", ley="#user.username", cacheValue="#caches[0].get(#caches[0].get(#username).get())")
public User findByUsername(String username)

 

五、使用Spring3.1注解 缓存 模糊匹配Evict的问题 

缓存都是key-value风格的,模糊匹配本来就不应该是Cache要做的;而是通过自己的缓存代码实现;

 

六、spring cache的缺陷:例如有一个缓存存放 list<User>,现在你执行了一个 update(user)的方法,你一定不希望清除整个缓存而想替换掉update的元素

这个在现有的抽象上没有很好的方案,可以考虑通过condition在之前的Helper方法中解决;当然不是很优雅。

 

也就是说Spring Cache注解还不是很完美,我认为可以这样设计:

@Cacheable(cacheName = "缓存名称",key="缓存key/SpEL", value="缓存值/SpEL/不填默认返回值",  beforeCondition="方法执行之前的条件/SpEL", afterCondition="方法执行后的条件/SpEL", afterCache="缓存之后执行的逻辑/SpEL")

value也是一个SpEL,这样可以定制要缓存的数据;afterCache定制自己的缓存成功后的其他逻辑。

 

 

当然Spring Cache注解对于大多数场景够用了,如果场景复杂还是考虑使用AOP吧;如果自己实现请考虑使用Spring Cache API进行缓存抽象。

 

欢迎加入spring群134755960进行交流。

 

示例请参考我的github:

https://github.com/zhangkaitao/spring4-showcase/tree/master/spring-cache

34
13
分享到:
评论
24 楼 zfcejb 2014-01-09  
23 楼 zfcejb 2014-01-09  
涛 哥,最近用spring cache + redis实现了一个缓存,其实redis主要是封装了下java的常用类型操作,还在完善中,不过已经能跑起来了,欢迎大家来讨论
22 楼 greemranqq 2014-01-09  
涛 哥,谢谢啦,发现源码包 里面的文档挺全的,原来一直看API ,没找到实例- -尴尬
21 楼 jinnianshilongnian 2014-01-09  
steafler 写道
龙哥,你的分析总是那么入木三分

  有用就好了 没白浪费时间写
20 楼 steafler 2014-01-09  
龙哥,你的分析总是那么入木三分
19 楼 jinnianshilongnian 2014-01-09  
greemranqq 写道
tao哥,我们项目用的3.0,然后我看很多教程是3.1 的。而我自己的项目是3.2的,感觉好多都不一样的,我看了下版本更新,缓存这块3个版本 一直在更新,能告诉一下从哪儿可以看到 更新后,或者对应版本的ehcache 的配置吗?spring网站 有点多,英文不是很好,比较乱了,或者你写个教程什么的,几个版本 比较一下,给小弟们 介绍下晒。感谢la

1、看官方文档(官方文档会介绍对应的版本)
2、看javadoc(会详细的告诉你需要哪个版本)
18 楼 greemranqq 2014-01-08  
tao哥,我们项目用的3.0,然后我看很多教程是3.1 的。而我自己的项目是3.2的,感觉好多都不一样的,我看了下版本更新,缓存这块3个版本 一直在更新,能告诉一下从哪儿可以看到 更新后,或者对应版本的ehcache 的配置吗?spring网站 有点多,英文不是很好,比较乱了,或者你写个教程什么的,几个版本 比较一下,给小弟们 介绍下晒。感谢la
17 楼 jinnianshilongnian 2014-01-08  
Shen.Yiyang 写道
看了这个接口,感觉是一个集成接口大过一个抽象接口,接口定义的行为都很简单,显式地写,显示地回收。其它的自动加载策略、回收策略还是都丢给底层缓存做了。

嗯,我还是更喜欢叫它是对缓存接口的抽象;其实像hibernate之类的也是这样的;之前工作的时候也是自己写这种抽象的;这样的好处是屏蔽了底层实现,好切换实现; 另一个方面就是提供了缓存切面;对于一些场景使用下还是可以减少代码量的
16 楼 Shen.Yiyang 2014-01-08  
看了这个接口,感觉是一个集成接口大过一个抽象接口,接口定义的行为都很简单,显式地写,显示地回收。其它的自动加载策略、回收策略还是都丢给底层缓存做了。
15 楼 lwclover 2014-01-08  
jinnianshilongnian 写道
lwclover 写道
你好,看了 ConcurrentMapCache源码,里面并没有实现LRU等算法,这样的缓存能用吗

这个要看底层缓存框架了;尽量考虑如ehcache等这种框架;spring只是包装了一层,而不是实现缓存;

3ks
14 楼 jinnianshilongnian 2014-01-08  
lwclover 写道
你好,看了 ConcurrentMapCache源码,里面并没有实现LRU等算法,这样的缓存能用吗

这个要看底层缓存框架了;尽量考虑如ehcache等这种框架;spring只是包装了一层,而不是实现缓存;
13 楼 lwclover 2014-01-08  
你好,看了 ConcurrentMapCache源码,里面并没有实现LRU等算法,这样的缓存能用吗
12 楼 jinnianshilongnian 2014-01-08  
在世界的中心呼喚愛 写道
jinnianshilongnian 写道
在世界的中心呼喚愛 写道
没用过,基本上是用ecahce

额;这个是缓存抽象;更高层的API;


类似于hibernate那种思想?先查询缓存,然后再查询数据库或者硬盘的?
我看最上面的介绍是这样的

缓存的思想都是类似的;只是目的不一样; Spring Cache提供了两个方面的东西:抽象Cache API 和 提供Cache切面+注解方便Cache逻辑的实现,类似于事务;
11 楼 在世界的中心呼喚愛 2014-01-08  
jinnianshilongnian 写道
在世界的中心呼喚愛 写道
没用过,基本上是用ecahce

额;这个是缓存抽象;更高层的API;


类似于hibernate那种思想?先查询缓存,然后再查询数据库或者硬盘的?
我看最上面的介绍是这样的
10 楼 jinnianshilongnian 2014-01-08  
dieslrae 写道
我一直觉得这一层的缓存在业务复杂的系统中没什么用,命中率太低了

比如用户/积分等这种缓存是有必要的,命中率也会很高的;还有如权限/菜单等这块
9 楼 jinnianshilongnian 2014-01-08  
zhangping2056 写道
    

8 楼 jinnianshilongnian 2014-01-08  
在世界的中心呼喚愛 写道
没用过,基本上是用ecahce

额;这个是缓存抽象;更高层的API;
7 楼 jinnianshilongnian 2014-01-08  
peihexian 写道
用mybatis框架的话直接写个cache标签就完了,比这个省事。

做的事情不一样;但思想类似
6 楼 jinnianshilongnian 2014-01-08  
aiyoaiyo0330 写道
请问 这个和用在hibernate的二级缓存有什么不同 是不是就是把ehcache用在了spring层

二级缓存是对如jpa/hibernate的实体/集合/关系进行缓存;可能应用中不用jpa/hibernate;可能应用中有更复杂的需求;如之上我描述的;还是需要自己设计缓存层的(最典型的如权限);
5 楼 zhangping2056 2014-01-08  
    

相关推荐

    Spring Cache的基本使用与实现原理详解

    Spring Cache 是Spring框架提供的一种缓存抽象,从Spring 3.1版本开始引入,目的是为了简化应用程序中的缓存管理,实现缓存透明化。通过在方法上添加特定注解,如@Cacheable、@CacheEvict等,可以轻松地启用缓存功能...

    (SSM框架)memcached整合Spring基于Cache注解.

    以上就是SSM框架下整合Memcached并基于Spring Cache注解的知识点详解。通过这样的集成,可以显著提高系统的响应速度,降低数据库压力,提升整体性能。在实际开发中,根据项目需求进行适当的调整和优化,可以使缓存...

    spring +hibernate 详解与配置(附图)

    ### Spring + Hibernate 详解与配置 #### Spring框架概述 Spring框架是一个开源的、轻量级的应用框架,旨在简化企业级应用开发。它提供了一个全面的编程模型,支持从Web到数据访问等各种应用需求。Spring的核心...

    Spring 缓存抽象示例详解

    Spring 缓存抽象是Spring框架从3.1版本开始引入的一种功能,旨在提供一种统一的方式来管理和集成各种缓存实现,如EhCache、Hazelcast、Infinispan和Redis等。这一抽象使得开发者能够在不关注具体缓存实现的情况下,...

    Springboot 2.X中Spring-cache与redis整合

    **Spring Boot 2.X 中 Spring Cache 与 Redis 整合详解** 在现代的 Web 应用开发中,缓存机制是提升系统性能的关键技术之一。Spring Cache 是 Spring 框架提供的一种统一的缓存抽象,它允许开发者通过简单的注解来...

    Spring Redis缓存实例

    ### 一、Spring Cache抽象 Spring Cache是一个通用的缓存抽象,它允许开发者在不修改代码的情况下启用缓存,通过简单的注解就能实现。它支持多种缓存提供商,包括Redis、EhCache、Hazelcast等。主要涉及的注解有`@...

    Java Cache详解及简单实现

    在Java中,我们可以使用多种库来实现缓存,例如Ehcache、Guava Cache或者Spring框架提供的缓存抽象。 在给出的描述中,我们看到一个基于Spring的项目中,开发者希望创建一个能够通过HTTP请求刷新的缓存,并且能够...

    Spring Boot整合EhCache的步骤详解

    这将激活Spring的缓存抽象,并允许我们在服务方法上使用`@Cacheable`、`@CacheEvict`等注解来控制缓存行为。 ```java import org.springframework.cache.annotation.EnableCaching; import org.springframework....

    Spring整合memcached完整项目代码

    Spring Cache是一个抽象层,它允许我们在不依赖特定缓存实现的情况下使用缓存。通过使用`@Cacheable`、`@CacheEvict`和`@CachePut`等注解,我们可以轻松地在方法级别启用、禁用或更新缓存。 1. **集成步骤** - ...

    spring boot cache 整合 redis demo (redis windows 安装包,和redis desktop管理工具)

    **Spring Boot Cache** 是Spring Boot对缓存功能的抽象,它支持多种缓存机制,包括Redis。通过简单的注解,如`@Cacheable`、`@CacheEvict`和`@CachePut`,我们可以轻松地在代码中加入缓存逻辑,提高应用程序性能。 ...

    spring缓存代码详解

    它提供了一个抽象接口,可以统一处理不同的缓存解决方案,使得代码的改动最小。 缓存的主要作用在于,当带有特定参数的方法首次被调用时,Spring会存储返回值,之后的相同请求将直接从缓存中获取结果,避免了昂贵的...

    Spring 3.x 中文开发手册.pdf

    ### Spring 3.x 新特性详解 #### 一、Spring 3.x 概述 Spring 3.x 是一个重大版本更新,引入了许多新功能和改进,旨在简化开发过程并提高应用程序性能。Spring 框架的核心优势在于其轻量级、模块化以及易于集成第三...

    spring-framework-4.1.4.release-schema

    《Spring Framework 4.1.4.RELEASE Schema详解》 Spring Framework是Java开发中的一个核心框架,它为创建高效、灵活且可测试的Java应用程序提供了强大的支持。在4.1.4.RELEASE版本中,Spring引入了对XML配置的...

    Spring+EhCache缓存实例

    **Spring+EhCache缓存实例详解** 在现代的Java企业级应用中,缓存技术扮演着至关重要的角色,它能够显著提升系统性能,减少数据库负载。Spring框架与EhCache的结合,为开发者提供了一种高效、易用的缓存解决方案。...

    spring-framework-reference.pdf

    - **Cache Abstraction**:Spring 3.1引入了缓存抽象,使得开发人员可以轻松地使用不同的缓存解决方案。 - **Bean Definition Profiles**:Spring 3.1支持定义Bean的配置文件,可以根据不同的环境选择不同的配置。 -...

    spring-framework-4.1.6.RELEASE-schema.zip

    《Spring Framework 4.1.6.RELEASE Schema详解》 Spring Framework是Java开发中的核心框架,它为构建高质量、可维护、可扩展的应用程序提供了全面的解决方案。在4.1.6.RELEASE版本中,Spring引入了对XML Schema的...

    详解spring applicationContext.xml 配置文件

    7. `&lt;cache&gt;`:Spring的缓存抽象允许通过注解或XML配置来管理缓存。例如,`&lt;cache:annotation-driven&gt;`可以启用基于注解的缓存管理。 此外,`xsi:schemaLocation`定义了每个命名空间对应的schema位置,确保解析器...

    官方原版源码 spring-framework-4.3.29.RELEASE.zip

    `Cache`包则提供了缓存抽象,如基于` Ehcache`和`Redis`的实现。 综上所述,Spring Framework 4.3.29.RELEASE的源码深度涵盖了IoC、AOP、MVC、数据访问以及任务调度等多个方面,是学习和研究Java企业级开发的宝贵...

    Spring整合EhCache详细教程(史上最全)

    在开始Spring整合EhCache之前,首先需要理解Spring缓存的核心概念及其抽象机制。Spring框架本身并不提供具体的缓存实现,但它提供了一套统一的缓存管理API,能够轻松地与各种第三方缓存系统集成。这一设计思路使得...

    详解Spring整合Ehcache管理缓存

    # Spring 整合 Ehcache 管理缓存详解 Ehcache 是一个高性能、轻量级的缓存框架,适用于 Java 应用程序。它可以极大地提高数据访问速度,减轻数据库负载。Spring 框架则提供了对缓存功能的抽象,允许开发者选择不同...

Global site tag (gtag.js) - Google Analytics