|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<cache:annotation-driven /><context:component-scan base-package='org.bk.samples.cachexml'></context:component-scan><bean id='cacheManager' class='org.springframework.cache.support.SimpleCacheManager'> <property name='caches'> <set> <ref bean='defaultCache'/> </set> </property></bean><bean name='defaultCache' class='org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean'> <property name='name' value='default'/></bean> |
Заводской компонент ConcurrentMapCacheFactoryBean — это компонент, который, в свою очередь, отвечает за создание компонента Cache.
Моя первая попытка перевести эту настройку в стиль @Configuration была следующей:
|
01
02
03
04
05
06
07
08
09
10
|
@Beanpublic SimpleCacheManager cacheManager(){ SimpleCacheManager cacheManager = new SimpleCacheManager(); List<Cache> caches = new ArrayList<Cache>(); ConcurrentMapCacheFactoryBean cacheFactoryBean = new ConcurrentMapCacheFactoryBean(); cacheFactoryBean.setName('default'); caches.add(cacheFactoryBean.getObject()); cacheManager.setCaches(caches ); return cacheManager;} |
Однако это не сработало, причина в том, что здесь я обошел некоторые механизмы жизненного цикла Spring bean. Оказывается, что ConcurrentMapCacheFactoryBean также реализует интерфейс InitializingBean и активно инициализирует кэш в методе InitializingBean ‘afterPropertiesSet’. Теперь, напрямую вызывая factoryBean.getObject (), я полностью обошел метод afterPropertiesSet.
Есть два возможных решения:
1. Определите FactoryBean так же, как он определен в XML:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
@Beanpublic SimpleCacheManager cacheManager(){ SimpleCacheManager cacheManager = new SimpleCacheManager(); List<Cache> caches = new ArrayList<Cache>(); caches.add(cacheBean().getObject()); cacheManager.setCaches(caches ); return cacheManager;}@Beanpublic ConcurrentMapCacheFactoryBean cacheBean(){ ConcurrentMapCacheFactoryBean cacheFactoryBean = new ConcurrentMapCacheFactoryBean(); cacheFactoryBean.setName('default'); return cacheFactoryBean;} |
В этом случае явный FactoryBean возвращается из метода @Bean, и Spring позаботится о вызове методов жизненного цикла этого компонента.
2. Реплицируйте поведение в соответствующих методах жизненного цикла, в этом конкретном случае я знаю, что FactoryBean создает экземпляр ConcurrentMapCache в методе afterPropertiesSet, я могу реплицировать это поведение непосредственно следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@Beanpublic SimpleCacheManager cacheManager(){ SimpleCacheManager cacheManager = new SimpleCacheManager(); List<Cache> caches = new ArrayList<Cache>(); caches.add(cacheBean()); cacheManager.setCaches(caches ); return cacheManager;}@Beanpublic Cache cacheBean(){ Cache cache = new ConcurrentMapCache('default'); return cache;} |
О чем следует помнить при переводе FactoryBean из xml в @Configuration.
Замечания:
Рабочий одностраничный тест в виде гистеты доступен здесь:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
package org.bk.samples.cache;import static org.hamcrest.MatcherAssert.assertThat;import static org.hamcrest.Matchers.equalTo;import java.util.ArrayList;import java.util.List;import java.util.Random;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.Cache;import org.springframework.cache.annotation.Cacheable;import org.springframework.cache.annotation.EnableCaching;import org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean;import org.springframework.cache.support.SimpleCacheManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.stereotype.Component;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes={TestSpringCache.TestConfiguration.class})public class TestSpringCache { @Autowired TestService testService; @Test public void testCache() { String response1 = testService.cachedMethod('param1', 'param2'); String response2 = testService.cachedMethod('param1', 'param2'); assertThat(response2, equalTo(response1)); } @Configuration @EnableCaching @ComponentScan('org.bk.samples.cache') public static class TestConfiguration{ @Bean public SimpleCacheManager cacheManager(){ SimpleCacheManager cacheManager = new SimpleCacheManager(); List<Cache> caches = new ArrayList<Cache>(); caches.add(cacheBean().getObject()); cacheManager.setCaches(caches ); return cacheManager; } @Bean public ConcurrentMapCacheFactoryBean cacheBean(){ ConcurrentMapCacheFactoryBean cacheFactoryBean = new ConcurrentMapCacheFactoryBean(); cacheFactoryBean.setName('default'); return cacheFactoryBean; } }}interface TestService{ String cachedMethod(String param1,String param2);}@Componentclass TestServiceImpl implements TestService{ @Cacheable(value='default', key='#p0.concat('-').concat(#p1)') public String cachedMethod(String param1, String param2){ return 'response ' + new Random().nextInt(); }} |
Ссылка: Spring @Configuration и FactoryBean от нашего партнера по JCG Биджу Кунджуммен в блоге « все и вся» .