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
|
@Bean public 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
|
@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; } |
В этом случае явный FactoryBean возвращается из метода @Bean, и Spring позаботится о вызове методов жизненного цикла этого компонента.
2. Реплицируйте поведение в соответствующих методах жизненного цикла, в этом конкретном случае я знаю, что FactoryBean создает экземпляр ConcurrentMapCache в методе afterPropertiesSet, я могу реплицировать это поведение непосредственно следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@Bean public SimpleCacheManager cacheManager(){ SimpleCacheManager cacheManager = new SimpleCacheManager(); List<Cache> caches = new ArrayList<Cache>(); caches.add(cacheBean()); cacheManager.setCaches(caches ); return cacheManager; } @Bean public 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); } @Component class 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 Биджу Кунджуммен в блоге « все и вся» .