<p></p> <p>백엔드 작업 하면서 내부 변동성 없는 데이터를 DB를 통해 아주 자주~ 자주 호출 하는 구분이 생겼다.</p> <p>캐싱 어노테이션을 이용해 보려 했지만 그낙 내 맘에 들지는 않았다.</p> 일정 시간이 지나면 원하는 것만 캐시 내용이 제거 됬으면 하는 내 소망이였다. 그래서 만들었다. <p><br></p> <p><br></p> <p></p> <table style="border-color:#000000;border-style:solid;border-width:1px;border-collapse:collapse;width:100%;" cellspacing="0" cellpadding="1"><tbody><tr><td style="border-style:solid;border-width:1px;border-color:#000000;">import java.util.Map;<br>import java.util.concurrent.Callable;<br>import java.util.concurrent.ConcurrentHashMap;<br>import java.util.concurrent.Executors;<br>import java.util.concurrent.Future;<br>import java.util.concurrent.ScheduledExecutorService;<br>import java.util.concurrent.TimeUnit;<br><br>/**<br> * 일정 시간 지나면 항목 제거 Map<P><br> * 항목을 추가하면 내부에 runnable이 생성되고 interval 시간에 삭제 처리가 이뤄 진다.<br> * @param <K><br> * @param <V><br> * @see {@link RemoveEventListener} <br> */<br><b><span style="color:#ff0000;">public class </span></b>CacheIntervalMap<K, V> <b><span style="color:#cc0000;">extends </span></b>ConcurrentHashMap<K, V>{<br><br> private static final long serialVersionUID = -3116825266165827645L;<br> <br> /** 스케줄러 서비스 */<br> private final ScheduledExecutorService scheduleExe;<br> <br> /** 지속 시간 */<br> private final long interval;<br> <br> /** 지속 시간 단위 */<br> private final TimeUnit unit;<br> <br> /** 자동 삭제하기 전 문의하기 이벤트 리스너 */<br> private final RemoveEventListener<K, V> listener;<br><br> /** 중간 취소 처리 위한 관리 객체 */<br> private final Map<K, Future<K>> callManage;<br> <br> /**<br> * 일정 시간이 지나면 제거<br> * @param <K><br> * @param <V><br> */<br> private static class RemoverCashIntervalMap<K, V> implements Callable<K>{<br> <br> private final CacheIntervalMap<K, V> owner;<br> <br> private final K key;<br> <br> public RemoverCashIntervalMap(CacheIntervalMap<K, V> owner, K key) {<br> this.owner = owner;<br> this.key = key;<br> }<br> <br> @Override<br> public K call() throws Exception {<br><br> System.out.println("자동 삭제 " + this.key );<br><br> if( ! this.owner.containsKey(this.key) )<br> return this.key;<br> <br> // 일단 지우고 나서 리스너로 문의해 본다.<br> final V value = this.owner.remove(this.key);<br> <br> if( this.owner.listener == null )<br> return this.key;<br> <br> if( ! this.owner.listener.isRemove(this.key, value) ) {<br> // 이미 삭제된거 어쩔 수 없다. 다시 추가~!<br> this.owner.put(this.key, value);<br> }<br> <br> return this.key;<br> }<br> }<br> <br> /**<br> * 삭제하기<br> * @param interval 일정시간<br> * @param unit 시간 단위<br> */<br> public CacheIntervalMap(long interval, TimeUnit unit) {<br> this(interval, unit, null);<br> }<br> <br> /**<br> * 삭제하기<br> * @param interval 일정시간<br> * @param unit 시간 단위<br> * @param listener 삭제할지 말지 결정.<br> */<br> public CacheIntervalMap(long interval, TimeUnit unit, RemoveEventListener<K, V> listener) {<br> super();<br> this.interval = interval;<br> this.unit = unit;<br> this.listener = listener;<br> <br> this.scheduleExe = Executors.newSingleThreadScheduledExecutor();<br> this.callManage = new ConcurrentHashMap<K, Future<K>>();<br> }<br> <br> @Override<br> public V remove(Object key) {<br> this.callManage.remove( key ).cancel(false);<br> return super.remove(key);<br> }<br> <br> @Override<br> public V put(K key, V value) {<br> <br> this.callManage.put(key, this.scheduleExe.schedule( new RemoverCashIntervalMap<K,V>(this, key), interval, unit) );<br> <br> return super.put(key, value);<br> }<br> <br> @Override<br> public void putAll(Map<? extends K, ? extends V> m) {<br> <br> for( K key : m.keySet() ) {<br> this.callManage.put(key, this.scheduleExe.schedule( new RemoverCashIntervalMap<K,V>(this, key), interval, unit) );<br> }<br> <br> super.putAll(m);<br> }<br> <br> @Override<br> public void clear() {<br> <br> for( Future<K> k : this.callManage.values() ) {<br> k.cancel(true);<br> }<br> <br> this.callManage.clear();<br> <br> super.clear();<br> }<br> <br>}</td> </tr></tbody></table><br><p>이벤트 클래스</p> <p></p> <table style="border-color:#000000;border-style:solid;border-width:1px;border-collapse:collapse;width:100%;" cellspacing="0" cellpadding="1"><tbody><tr><td style="border-style:solid;border-width:1px;border-color:#000000;"> /**<br> * 삭제 되기 전 발생하는 이벤트<br> * @param <K><br> * @param <V><br> * @see {@link CacheIntervalMap} <br> */<br><b><span style="color:#ff0000;">public interface</span></b> RemoveEventListener<K, V> {<br><br> /**<br> * 삭제 되기전 문의 하기~ <br> * @param key<br> * @param value<br> * @return true: 삭제해라. false:다음에 삭제할까 생각해 볼께. <br> */<br> public boolean isRemove(K key, V value);<br>}</td> </tr></tbody></table><p><br></p> <div>사용 예제...</div> <div> <table style="border-color:#000000;border-style:solid;border-width:1px;border-collapse:collapse;width:100%;" cellspacing="0" cellpadding="1"><tbody><tr><td style="border-style:solid;border-width:1px;border-color:#000000;"> <br>final Map<String, Integer> testExe = new <b><span style="color:#cc0000;">CacheIntervalMap</span></b><String, Integer>(<b><span style="color:#ff0000;">5</span></b>, <b><span style="color:#ff0000;">TimeUnit.SECONDS</span></b>, new <b>RemoveEventListener</b><String, Integer>() {<br> <br> @Override<br> public boolean isRemove(String key, Integer value) {<br> // BB는 삭제하지 않는다.<br> return !"BB".equals(key);<br> }<br> <br> });<br> <br> testExe.put("AA", 100);<br> testExe.put("BB", 200);<br> testExe.put("CC", 300);<br></td> </tr></tbody></table></div> <p><br></p> <p>결과</p> <p></p> <table style="border-color:#000000;border-style:solid;border-width:1px;border-collapse:collapse;width:100%;" cellspacing="0" cellpadding="1"><tbody><tr><td style="border-style:solid;border-width:1px;border-color:#000000;">자동 삭제 AA<br>자동 삭제 BB<br>자동 삭제 CC<br>자동 삭제 BB<br>자동 삭제 BB<br>자동 삭제 BB<br></td> </tr></tbody></table><br><div>5초마다 계속 삭제 시도는 하지만 BB는 지워지지 않는다. <br></div> <div><br></div> <div>뭐 ... 아주 많은 데이터를 구겨 넣으면 문제가 되겠지만 몇 백개의 작은 객체는 충분히 버틸만 하다.</div> <div> </div>
댓글 분란 또는 분쟁 때문에 전체 댓글이 블라인드 처리되었습니다.