我正在寻找一个优雅的解决方案来解决在应用程序启动时加载和缓存静态、共享数据(具有无限生命周期)的老问题。
我以前的方法是使用 Spring Singleton Bean,但我现在正在尝试使用 JAVA EE 6(JPA2、EJB3.1、CDI)来实现它。
我有一个 @Entity
和一个 @Stateless
EJB 从数据库加载实体。我的想法是添加一个 @Singleton
EJB 来缓存数据;我还决定将原始 EJB 分开,以防止违反 SRP (并且因为将来它可能会被其他参与者绕过缓存使用)。
请看一下这个简化的概念验证:
实体
@NamedQuery(name="Room.findAll", query="SELECT r FROM Room r")
@Entity
public class Room {
@Id
private Integer id; // GETTER, SETTER
private String description; // GETTER, SETTER
}
加载程序
@Stateless
public class Rooms {
@PersistenceContext
EntityManager em;
public List<Room> findAll() {
return em.createNamedQuery("Room.findAll",Room.class).getResultList();
}
}
缓存器
@Singleton
public class RoomsCached {
@EJB
Rooms rooms;
private List<Room> cachedRooms; // GETTER
@PostConstruct
public void initCache(){
this.cachedRooms = Collections.unmodifiableList(rooms.findAll());
}
}
你能看出这个例子中的大问题、概念错误或其他什么吗?
我主要担心的是
如果两者都是
@Singleton
(mehh),我可以在缓存 bean 上添加@DependsOn("Rooms")
,以确保 Rooms 已经在使用之前加载,但是使用@Singleton
和@Stateless
我不能...将@Stateless
bean 总是在 CDI 注入(inject)之前加载它变成了@Singleton
?@Singleton
调用@Stateless
似乎很奇怪(我见过相反的例子);我应该通过将@Singleton
实例放入@Stateless
EJB 来更改设计吗?在
@PostConstruct
方法中加载缓存是否正确?
最佳答案
嗯,我做了一些测试,我也尝试了@Decorator
方式。这似乎仍然是最好的。
@Entity bean 和@Stateless bean 是相同的问题,而我按如下方式更改了@Singleton bean,还添加了经典的定时缓存:
@Singleton
public class RoomsCached {
@Inject
Rooms rooms;
private List<Room> cachedRooms;
private long timeout = 86400000L; // reload once a day
private long lastUpdate;
public List<Room> getCachedRooms() {
initCache();
return cachedRooms;
}
public void initCache() {
if (cachedRooms == null || expired()) {
cachedRooms = Collections.unmodifiableList(rooms.findAll());
lastUpdate = System.currentTimeMillis();
}
}
private boolean expired() {
return System.currentTimeMillis() > lastUpdate + timeout;
}
}
不需要@PostConstruct,也不需要@EJB,与底层的@inject-ed @Stateless bean 没有同步问题。
效果很好。
关于java - 使用@Singleton 和@Stateless 加载和缓存应用程序范围的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26733141/