Java Cache 상세 정보 및 간단한 구현
개요:
최근에 스프링 프로젝트를 하고 있습니다. 캐시를 만들고 데이터베이스에 접근하고 정기적으로 데이터 업데이트를 하려고 합니다.
두 가지 기능 구현
http를 통해 캐시를 갱신해야 하기 때문에 첫 번째 생각은 캐시를 컨트롤러로 만드는 것이다
Controller 구현
Controller의 가장 큰 장점은 Spring의 설정을 통해 많은 의존을 주입할 수 있다는 것이다. 예를 들어 서비스에 대한 의존, 데이터베이스에 대한 의존 등이다.
대량의 데이터베이스와 서비스층에 접근하는 코드는 모두 복용할 수 있다
Cache 인터페이스를 다음과 같이 정의합니다.
public interface Cache {
/**
* Refresh the cache. If succeed, return true, else return false;
*
* @return
*/
boolean refresh();
/**
* How much time it will refresh the cache.
*
* @return
*/
long interval();
}
그러나 여기에 문제가 생겼다. 자신이 쓴 컨트롤러는 주입하는 방식으로 Http 서비스와 서비스 층, 데이터베이스 층을 쉽게 연결할 수 있지만 만약에 Cache 컨트롤러가 Cache 인터페이스를 실현한다면 인터벌 함수를 호출하여 간격을 찾기 어렵다는 것을 발견할 수 있다.Cache Controller도 Bean이기 때문에 스프링을 통해 이 bean을 찾아서 호출해야 합니다.Bean을 찾을 수 없으면 Interval을 호출할 수 없고, 다른 라인을 통해 캐시 리셋을 제어할 수 없습니다.이 Bean을 얻기 위해 모든 Cache Controller를 하나의 Cache Manager Controller에 Autowired할 수 있습니다.
@Controller
public class CacheManagerController {
@Autowired
private CacheController cache;
private static ScheduledExecutorService executor = Executors
.newScheduledThreadPool(1);
public CacheManagerController() {
executor.scheduleAtFixedRate(() -> cache.refresh(), 0, cache.interval(),
TimeUnit.MILLISECONDS);
}
}
이렇게 하려고 했는데 문제가 하나 생겼어요. 이렇게 하면 Cache Manager Controller가 초기화될 때, 즉 Bean을 구성할 때 각종 Cache가 Cache Controller에 주입되지 않고 구조 함수에 방법을 넣지 않으면 Cache Manager Controller가 자동으로 스케줄링 서비스를 호출할 수 없어요.수동으로 호출해야 합니다.그러나 프로그램의 입구가 반드시 어느 컨트롤러에서 들어오는 것은 아니다. 만약에 차단기를 쓰면 번거롭고 호출할 때마다 실행된다.이때 Cache Service를 통해 이 문제를 해결합니다.
public class CacheService {
public static final long ONE_MINUTE = 60 * 1000;
private static ScheduledExecutorService executor = Executors
.newScheduledThreadPool(1);
public static void register(Cache cache) {
executor.scheduleAtFixedRate(() -> cache.refresh(), 0, cache.interval(),
TimeUnit.MILLISECONDS);
}
}
@Controller
public class CacheController implements Cache {
// autowire service, repo
@Autowired
private Service service;
public CacheController() {
CacheService.register(this);
}
// cache interface
@Override
public long interval() {
return 1000;
}
@Override
public boolean refresh() {
return true;
}
}
구체적인 Cache Controller는 반사 구조를 통해 Bean을 Spring에서 관리하기 때문에 직접 무참구조 함수를 통해 등록할 수 있습니다. 그러면 문제가 없습니다. Spring이 Cache Controller를 불러올 때 Cache Service의 등록 방법을 직접 호출하여 캐시 서비스에 정의된 스레드 탱크에 캐시를 등록하고 바로 리셋 방법을 실행합니다.또한 시간 간격에 따라 자동으로 리셋됩니다.지정된 Cache를 가져오는 것은 더 간단합니다. Cache 자체가 Controller이기 때문에 Autowire를 통해 사용할 다른 Controller에 자동으로 등록할 수 있습니다.
물론, 현재 이렇게 쓰는 것은 아무런 문제가 없지만, refresh가 즉시 호출될 때, Autowired가 주입한 서비스를 받을 수 없습니다.스프링은 모든 실례화를 통일한 다음에 불러오는 것이기 때문에,refresh 함수에서 서비스가 호출된다면, 분명히 프로그램은 빈 바늘에 이상을 알릴 것입니다.이것도 Controller를 사용하여 Cache를 하는 문제입니다.스프링이 불러온 모든 실례를 얻으려면 구조 함수를 수정해서 실례를 통일된 집합에 주입해야 한다. 그러면 앞에서 언급한 문제와 같다. 즉, 빈을 얻는 것이다.빈을 얻을 수 있다면 실례적인 방법을 사용할 수 있을 것이다. 이렇게 많은 일도 없을 것이다.
총결산
Controller 의 특징은 다음과 같습니다.
Listener의 장점 중 하나는 PreloadListener를 써서 ServletContextListener를 실현하면 Tomcat을 이용하여 웹을 불러올 수 있다는 것이다.xml 때 코드를 미리 초기화했습니다.
Listener 구현
public class PreloadListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
CacheFactory.init();
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
다음은 웹입니다.xml 코드
// web.xml
<listener>
<listener-class>com.sapphire.listener.PreloadListener</listener-class>
</listener>
물론 장점이 있으면 열세가 있을 것이다. Listener 방식을 사용하여 미리 불러오기 때문에 웹의 성명 주기 때문에 문제가 생길 수 있다.Tomcat이 웹을 로드하고 있습니다.xml 때 Listener의 초기화는 스프링 용기가 시작되기 전에 문제가 발생합니다.PreloadListener에서 호출할 수 있는 코드는 틀림없이 Autowire에서 어떤 Bean에도 호출할 수 없습니다.이것이 바로 컨트롤러가 부딪힌 커다란 열세를 비교하는 것이다. 스스로 그 서비스를 다시 써야 한다.
이 외에도 지정한 캐시를 새로 고치기 위해 컨트롤러를 따로 써야 합니다.
public class CacheFactory {
private static ConcurrentHashMap<String, Cache> caches = new ConcurrentHashMap<>();
private static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private static void register(Cache cache) {
caches.put(cache.category(), cache);
}
private static void registerAll() {
register(new StockCache());
}
public static void init() {
registerAll();
for (Cache cache : caches.values()) {
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
cache.refresh();
}
}, 0, cache.interval(), TimeUnit.MILLISECONDS);
}
}
public static Cache getCache(String key) {
if (caches.contains(key)) {
return caches.get(key);
}
return null;
}
}
// cache interval refresh , category Cache
public interface Cache {
/**
* Refresh the cache. If succeed, return true, else return false;
*
* @return
*/
boolean refresh();
/**
* How much time it will refresh the cache.
*
* @return
*/
long interval();
/**
* Cache's category. Each cache has distinct category.
*
* @return
*/
String category();
}
이렇게 완성된 CacheFactory는 PreloadListener에서 init 방법을 호출하여 모든 Cache를 초기화하여 Cache의 시작을 완성할 수 있습니다.이를 통해 알 수 있듯이 모든 CacheFactory 중의 방법은 정적 방법으로 컨트롤러 층에서 마음대로 호출할 수 있다.그 다음에 서로 다른 Cache는 단독으로 init 방법을 써서 각자의 실현된 Refresh 방법에 넣어야 한다.데이터베이스와의 링크 등은 모두 구축해야 한다.서로 다른 Cache는 각자의 초기화 방법을 다시 써야 하고, 파일 설정을 읽는 것을 써서 데이터베이스의 설정 정보를 읽어야 한다.아무튼 귀찮아요.
총결산
Listener를 통해 더욱 유연하게 용기가 시작되기 전에 필요한 정보를 메모리에 불러올 수 있지만 많은 업무 코드를 다시 써야 한다. 데이터베이스 링크, 해석Property, 유연하게 새로 고침된 Cache Controller.
읽어주셔서 감사합니다. 여러분에게 도움이 되었으면 좋겠습니다. 본 사이트에 대한 지지에 감사드립니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.