我正在寻找一个优雅的解决方案来解决在应用程序启动时(具有无限生命周期)加载和缓存静态,共享数据的旧问题。
我的老方法是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();
}
}
cacher的
@Singleton
public class RoomsCached {
@EJB
Rooms rooms;
private List<Room> cachedRooms; // GETTER
@PostConstruct
public void initCache(){
this.cachedRooms = Collections.unmodifiableList(rooms.findAll());
}
}
你能看到这个例子中的大问题,概念错误或什么吗?
我主要担心的是
如果两者都是@Singleton
(mehh),我可以在cacher bean上添加@DependsOn("Rooms")
,以确保在使用之前已经加载了Rooms,但使用@Singleton
和@Stateless
我无法在CDI将@Stateless
注入@Singleton
之前将@Singleton
bean加载?
@Stateless
调用@Singleton
似乎很奇怪(我见过相反的例子);我应该通过将@Stateless
实例放在@PostConstruct
EJB中来改变设计吗?
在{{1}}方法中加载和缓存是否正确?
答案 0 :(得分:1)
好吧,我做了一些测试,我也尝试了@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的同步问题。
它运作良好。