티스토리 뷰

Spring Frameworks

Spring boot - Ehcache, cache 예제

까오기 까오기 2019. 2. 26. 09:23

Overviews

예제를 만드는 것은 간단합니다. 먼저 Spring Starter Project로 기본 프로젝트를 생성합니다. cache 관련 dependencies를 추가해주고 설정 Configuration 추가해 줍니다. ehcache.xml 파일을 클래스패스 아래 생성합니다. 

Main class에 캐시를 활성 애노테이션을 추가해주고 샘플을 만들어서 테스트 합니다.


개발 환경 

  • Spring boot 2.x
  • jdk 1.8 
  • Gradle 4.x

Dependencies 추가 

1
2
3
4
5
6
7
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    implementation 'org.springframework.boot:spring-boot-starter-cache'
    implementation 'net.sf.ehcache:ehcache-core:2.6.11'
}
cs

Configuration 추가

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class CustomCacheConfiguration extends CachingConfigurerSupport {
    
    @Bean
    public EhCacheManagerFactoryBean getEhCacheFactory(){
        EhCacheManagerFactoryBean factoryBean = new EhCacheManagerFactoryBean();
        factoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        factoryBean.setShared(true);
        return factoryBean;
    }
 
}
cs

ehcache.xml 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false"
    monitoring="autodetect" dynamicConfig="true">
 
    <diskStore path="java.io.tmpdir" />
 
    <cache name="userCache" maxEntriesLocalHeap="2000"
        maxEntriesLocalDisk="2000" eternal="false" diskSpoolBufferSizeMB="20"
        timeToIdleSeconds="1200" timeToLiveSeconds="1800"
        memoryStoreEvictionPolicy="LFU" transactionalMode="off">
        <persistence strategy="localTempSwap" />
    </cache>
 
</ehcache
cs


캐시 활성화 

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableCaching
public class CacheExampleApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(CacheExampleApplication.class, args);
    }
 
}
cs


예제 

User.java

1
2
3
4
5
6
7
8
9
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
public class User implements Serializable{
 
    private static final long serialVersionUID = 1L;
 
    private String userId;
    private String name;
    private String authType;
    private Date timestamp;
    private boolean isUpdate = false;
    
    public User() {
        super();
    }
    
    public User(String userId, String name, String authType) {
        super();
        this.userId = userId;
        this.name = name;
        this.authType = authType;
    }
 
    public String getUserId() {
        return userId;
    }
 
    public void setUserId(String userId) {
        this.userId = userId;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getAuthType() {
        return authType;
    }
 
    public void setAuthType(String authType) {
        this.authType = authType;
    }
 
    public Date getTimestamp() {
        return timestamp;
    }
 
    public void setTimestamp(Date timestamp) {
        this.timestamp = timestamp;
    }
 
    public boolean getIsUpdate() {
        return isUpdate;
    }
 
    public void setIsUpdate(boolean isUpdate) {
        this.isUpdate = isUpdate;
    }
 
    @Override
    public String toString() {
        return "User [userId=" + userId + ", name=" + name + ", authType=" + authType
                + ", timestamp=" + timestamp + ", isUpdate=" + isUpdate + "]";
    }
 
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((authType == null) ? 0 : authType.hashCode());
        result = prime * result + (isUpdate ? 1231 : 1237);
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + ((timestamp == null) ? 0 : timestamp.hashCode());
        result = prime * result + ((userId == null) ? 0 : userId.hashCode());
        return result;
    }
 
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (authType == null) {
            if (other.authType != null)
                return false;
        } else if (!authType.equals(other.authType))
            return false;
        if (isUpdate != other.isUpdate)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (timestamp == null) {
            if (other.timestamp != null)
                return false;
        } else if (!timestamp.equals(other.timestamp))
            return false;
        if (userId == null) {
            if (other.userId != null)
                return false;
        } else if (!userId.equals(other.userId))
            return false;
        return true;
    }
}
cs

캐시하고자 하는 객체는 기본적으로 Serializable 인터페이스를 implements해야 합니다.  

Cache에서 Key를 지정하지 않는 경우에는 Key를 생성하기 위해서는 hashCode()를 오버라이딩해야 합니다. 


UserService 생성

1
2
3
4
5
6
7
8
9
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
@Service
public class UserService {
 
    @Cacheable(cacheNames="userCache")
    public User __getUser(final User user) {
        return cloneUser(user);
    }
 
    @Cacheable(cacheNames="userCache", key="#user.userId", condition = "#user.isUpdate != true")
    public User getUser(User user) {
        return cloneUser(user);
    }
 
    @CachePut(cacheNames="userCache", key="#user.userId")
    public User addUser(User user) {
        return cloneUser(user);
    }
 
    @CachePut(cacheNames="userCache", key="#user.userId")
    public User modifyUser(User user) {
        return cloneUser(user);
    }
 
    @CacheEvict(cacheNames="userCache", key="#user.userId")
    public void removeUser(User user) {
        cloneUser(user);
    }
    
    private User cloneUser(User user) {
        User rUser = SerializationUtils.clone(user);
        rUser.setTimestamp(new Date());
        return rUser;
    }
}
cs

각 메소드에서는 결과값으로 날짜를 생성해서 캐시 여부를 체크합니다. 


@CachePut : 캐시 생성 

@Cacheable : 조회해서 없으면 캐시 생성하고 결과 반환, 캐시된 값이 있는 경우 해당 값 반환. key 정의 및 조건 정의 가능. 

@CacheEvict : 캐시 삭제, allEntries를 이용해서 전체 삭제 가능. 


여러개의 캐시 삭제 예제)

1
2
3
4
5
6
7
8
    @Caching(evict = {
            @CacheEvict(cacheNames="userCache", key="#param.userId"),
            @CacheEvict(cacheNames="authCache", key="#param.roleId")
        })
    public int removeUserAuth(UserAuth param) {
        ...
        ...
    }
cs


컨트롤러 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@RestController
public class UserTestController {
 
    @Autowired 
    private UserService userService; 
    
    @GetMapping("/users/{userId}")
    public User getUser(User pUser) {
        return userService.getUser(pUser);
    }
 
    @PostMapping("/users")
    public User addUser(User pUser) {
        userService.addUser(pUser);
        return pUser;
    }
 
    @PutMapping("/users/{userId}")
    public User modifyUser(User pUser) {
        userService.modifyUser(pUser);
        return pUser;
    }
 
    @DeleteMapping("/users/{userId}")
    public User removeUser(User pUser) {
        userService.removeUser(pUser);
        return pUser;
    }
    
}
cs


Test

실행하고 브라우져에서 확인해 봅니다. 

http://localhost:8080/users/1234

1
{"userId":"1234","name":null,"authType":null,"timestamp":"2019-02-26T01:06:05.408+0000","isUpdate":false}
cs


캐시가 적용되었다면 timestamp의 값이 변하지 않는 걸 확인 할 수 있습니다. 


Test Controller

1
2
3
4
5
6
7
8
9
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
76
77
78
79
80
81
82
83
84
85
86
87
88
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTestControllerTest {
 
    private User pUser;
    
    @Before
    public void setUp() {
        this.pUser = new User("test""테스터""web");
    }
    
    @SuppressWarnings("unused")
    private static <extends Serializable> T clone(final T object) { 
        return SerializationUtils.clone(object);
    }
 
    @Autowired
    protected WebApplicationContext context;
 
    protected MockMvc mockMvc;
    
    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
    }
 
    private User getUser(MockHttpServletRequestBuilder req) throws Exception {
        // 값을 가져와서 처리하기 
        MvcResult result = this.mockMvc.perform(req)
        .andExpect(status().isOk())
//        .andExpect(jsonPath("$.userId").value("test"))
//        .andDo(print())
        .andReturn();
        
        // expected : {"userId":"test","userPwd":null,"name":null,"authType":null,"timestamp":"2019-02-22T07:24:33.784+0000","isUpdate":false}
        String jsonInString = result.getResponse().getContentAsString();
        // Json String을 Map에 담기 
        final ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(jsonInString, User.class);
    }
 
    @Test
    public void getUserTest() throws Exception {
        User param = clone(pUser);
        MockHttpServletRequestBuilder req = get("/users/"+param.getUserId());
        User rUser1 = getUser(req);
        Date ts1 = rUser1.getTimestamp();
 
        User rUser2 = getUser(req);
        Date ts2 = rUser2.getTimestamp();
        
        assertTrue(ts1.compareTo(ts2) == 0);
    }
 
    @Test
    public void getUserUpdateTest() throws Exception {
        User param = clone(pUser);
        MockHttpServletRequestBuilder req = get("/users/"+param.getUserId());
        User rUser1 = getUser(req);
        Date ts1 = rUser1.getTimestamp();
        
        req.param("isUpdate""true");
        User rUser2 = getUser(req);
        Date ts2 = rUser2.getTimestamp();
        // 두개의 결과가 달라야만 한다.  
        assertTrue(ts1.compareTo(ts2) != 0);
    }
    
    @Test
    public void addUserTest() throws Exception {
        User param = clone(pUser);
        param.setUserId("1234");
        MockHttpServletRequestBuilder postReq = post("/users");
        postReq.param("userId", param.getUserId());
        postReq.param("name", param.getName());
        postReq.param("authType", param.getAuthType());
        
        User rUser1 = getUser(postReq);
        Date ts1 = rUser1.getTimestamp();
        
        MockHttpServletRequestBuilder req = get("/users/"+param.getUserId());
        User rUser2 = getUser(req);
        Date ts2 = rUser2.getTimestamp();
        // 두개의 결과가 달라야만 한다.  
        assertTrue(ts1.compareTo(ts2) == 0);
    }
    
}
cs


Test service

1
2
3
4
5
6
7
8
9
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
 
    @Autowired
    private UserService userService;
    
    private User pUser;
    
    @Before
    public void setUp() {
        this.pUser = new User("test""테스터""web");
    }
    
    @SuppressWarnings("unused")
    private static <extends Serializable> T clone(final T object) { 
        return SerializationUtils.clone(object);
    }
 
    @Test
    public void getUser() {
        User param = clone(pUser);
        User rUser1 = userService.getUser(param);
        Date ts1 = rUser1.getTimestamp();
 
        User rUser2 = userService.getUser(param);
        Date ts2 = rUser2.getTimestamp();
        
        assertTrue(ts1.compareTo(ts2) == 0);
    }
 
    @Test
    public void getUserUpdate() {
        User param = clone(pUser);
        User rUser1 = userService.getUser(param);
        Date ts1 = rUser1.getTimestamp();
 
        // update 조건을 주면 캐시를 타지 않는다. 
        param.setIsUpdate(true);
        User rUser2 = userService.getUser(param);
        Date ts2 = rUser2.getTimestamp();
        // 두개의 결과가 달라야만 한다.  
        assertTrue(ts1.compareTo(ts2) != 0);
    }
 
    @Test
    public void addUser() {
        User param = clone(pUser);
        param.setUserId("1234");
        // 사용자 추가 
        User rUser1 = userService.addUser(param);
        Date ts1 = rUser1.getTimestamp();
 
        // 사용자 조회 
        User rUser2 = userService.getUser(param);
        Date ts2 = rUser2.getTimestamp();
        
        assertTrue(ts1.compareTo(ts2) == 0);
    }
 
    @Test
    public void modifyUser() {
        User param = clone(pUser);
        // 사용자 조회 
        User rUser1 = userService.getUser(param);
        Date ts1 = rUser1.getTimestamp();
        
        // 사용자 수정 
        param.setAuthType("facebook");
        User rUser2 = userService.modifyUser(param);
        Date ts2 = rUser2.getTimestamp();
        
        assertTrue(ts1.compareTo(ts2) != 0);
    }
 
    @Test
    public void deleteUser() {
        User param = clone(pUser);
        // 사용자 조회 
        User rUser1 = userService.getUser(param);
        Date ts1 = rUser1.getTimestamp();
        
        // 사용자 삭제 
        userService.removeUser(param);
 
        // 사용자 조회 
        User rUser2 = userService.getUser(param);
        Date ts2 = rUser2.getTimestamp();
 
        assertTrue(ts1.compareTo(ts2) != 0);
    }
    
 
}
cs


Github 링크

https://github.com/kkaok/examples

댓글
댓글쓰기 폼