gpt4 book ai didi

当 session 在 websphere 中失效时,Spring View 范围不会被破坏

转载 作者:行者123 更新时间:2023-12-04 11:33:22 25 4
gpt4 key购买 nike

我正在使用 Springboot 1.5.9.RELEASE , JSF 2.2.20(玻璃鱼) , Primefaces 8 运行于 WebSphere 8.5.5.13
,并且我已经根据以下链接定义了自定义 View 范围:
https://github.com/jneat/spring-jsf

public class ViewScope implements Scope, HttpSessionBindingListener {

private static final long serialVersionUID = 1L;

private static final Log logger = LogFactory.getLog(ViewScope.class);

private final WeakHashMap<HttpSession, Set<ViewScopeViewMapListener>> sessionToListeners = new WeakHashMap<>();

@Override
public Object get(String name, ObjectFactory objectFactory) {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
//noinspection SynchronizationOnLocalVariableOrMethodParameter
if (viewMap.containsKey(name)) {
return viewMap.get(name);
} else {
synchronized (viewMap) {
if (viewMap.containsKey(name)) {
return viewMap.get(name);
} else {
logger.trace("Creating bean " + name);
Object object = objectFactory.getObject();
viewMap.put(name, object);
return object;
}
}
}
}

@Override
public String getConversationId() {
return null;
}

/**
* Removing bean from the scope and unregister it destruction callback without executing them.
*
* @see Scope for more details
*/
@Override
public Object remove(String name) {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
if (viewMap.containsKey(name)) {
Object removed;
synchronized (viewMap) {
if (viewMap.containsKey(name)) {
removed = FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
} else {
return null;
}
}

HttpSession httpSession = (HttpSession)FacesContext.getCurrentInstance().getExternalContext().getSession(true);
Set<ViewScopeViewMapListener> sessionListeners;
sessionListeners = sessionToListeners.get(httpSession);
if (sessionListeners != null) {
Set<ViewScopeViewMapListener> toRemove = new HashSet<>();
for (ViewScopeViewMapListener listener : sessionListeners) {
if (listener.getName().equals(name)) {
toRemove.add(listener);
FacesContext.getCurrentInstance().getViewRoot().unsubscribeFromViewEvent(PreDestroyViewMapEvent.class, listener);
}
}
synchronized (sessionListeners) {
sessionListeners.removeAll(toRemove);
}
}

return removed;
}
return null;
}

/**
* Register callback to be executed only on the whole scope destroying (not single object).
*
* @see Scope for more details
*/
@Override
public void registerDestructionCallback(String name, Runnable callback) {
logger.trace("registerDestructionCallback for bean " + name);

UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
ViewScopeViewMapListener listener = new ViewScopeViewMapListener(viewRoot, name, callback, this);

viewRoot.subscribeToViewEvent(PreDestroyViewMapEvent.class, listener);

HttpSession httpSession = (HttpSession)FacesContext.getCurrentInstance().getExternalContext().getSession(true);

final Set<ViewScopeViewMapListener> sessionListeners;

if (sessionToListeners.containsKey(httpSession)) {
sessionListeners = sessionToListeners.get(httpSession);
} else {
synchronized (sessionToListeners) {
if (sessionToListeners.containsKey(httpSession)) {
sessionListeners = sessionToListeners.get(httpSession);
} else {
sessionListeners = new HashSet<>();
sessionToListeners.put(httpSession, sessionListeners);
}
}
}

synchronized (sessionListeners) {
sessionListeners.add(listener);
}

if (!FacesContext.getCurrentInstance().getExternalContext().getSessionMap().containsKey("sessionBindingListener")) {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("sessionBindingListener", this);
}

}

@Override
public Object resolveContextualObject(String key) {
return null;
}

@Override
public void valueBound(HttpSessionBindingEvent event) {
logger.trace("Session event bound " + event.getName());
}

/**
* Seems like it called after our listeners were unbounded from HTTP session.
* Looks like view scope is destroyed. But should we call callback or not is a big question.
*
* @see HttpSessionBindingListener for more details
*/
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
logger.trace("Session event unbound " + event.getName());
final Set<ViewScopeViewMapListener> listeners;
synchronized (sessionToListeners) {
if (sessionToListeners.containsKey(event.getSession())) {
listeners = sessionToListeners.get(event.getSession());
sessionToListeners.remove(event.getSession());
} else {
listeners = null;
}
}
if (listeners != null) {
// I just hope that JSF context already done this job
for (ViewScopeViewMapListener listener : listeners) {
// As long as our callbacks can run only once - this is not such big deal
listener.doCallback();
}
}
}

/**
* Will remove the listener from the session set and unregister it from UIViewRoot.
*/
public void unregisterListener(ViewScopeViewMapListener listener) {
logger.debug("Removing listener from map");
HttpSession httpSession = (HttpSession)FacesContext.getCurrentInstance().getExternalContext().getSession(false);
FacesContext.getCurrentInstance().getViewRoot().unsubscribeFromViewEvent(PreDestroyViewMapEvent.class, listener);
if (httpSession != null) {
synchronized (sessionToListeners) {
if (sessionToListeners.containsKey(httpSession)) {
sessionToListeners.get(httpSession).remove(listener);
}
}
}
}
}
- ViewScopeViewMapListener :
public class ViewScopeViewMapListener implements ViewMapListener {

private static final Log logger = LogFactory.getLog(ViewScope.class);

private final String name;

private final Runnable callback;

private boolean callbackCalled = false;

private final WeakReference<UIViewRoot> uiViewRootWeakReference;

private final ViewScope viewScope;

public ViewScopeViewMapListener(UIViewRoot root, String name, Runnable callback, ViewScope viewScope) {
this.name = name;
this.callback = callback;
this.uiViewRootWeakReference = new WeakReference<>(root);
this.viewScope = viewScope;
}

public synchronized void doCallback() {
logger.trace("Going call callback for bean " + name);
if (!callbackCalled) {
try {
callback.run();
} finally {
callbackCalled = true;
}
}
}

public String getName() {
return name;
}

@Override
public boolean isListenerForSource(Object source) {
return (source == uiViewRootWeakReference.get());
}

@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
if (event instanceof PreDestroyViewMapEvent) {
logger.trace("Going call callback for bean " + name);
doCallback();
viewScope.unregisterListener(this);
}
}

}
- SpringScopeView 注释:
@Qualifier
@Scope("view")
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SpringScopeView {
}
- 定义自定义范围:
    @Configuration
public class MyViewScope extends CustomScopeConfigurer {

public InitViewScope() {
log.info("Init ViewScope");
Map<String, Object> map = new HashMap<>();
map.put("view", new ViewScope());
super.setScopes(map);
}
}
- 查看范围 bean 示例:
@Component("employeeRequest")
@SpringScopeView
public class EmployeeRequestBean implements Serializable {

private static final long serialVersionUID = 6609775672949354713L;



@Autowired
private CurrentUserBean currentUserBean;

@Autowired
private RequestRepository requestRepository;

@Autowired
private ActionRepository actionRepository;


@Autowired
private SMSServiceClient smsServiceClient;

private List<AttachmentDTO> uploadedFilesList = new ArrayList<AttachmentDTO>();

private Request request;

private Action action;


@PostConstruct
public void init() {
try {
String selectedRequestId = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()
.get("selectedRequestId");
if (StringUtils.isBlank(selectedRequestId))
return;
request = requestRepository.getOne(Long.parseLong(selectedRequestId));
showRequest();
} catch (Exception e) {
log.error("Exception in init EmployeeRequestBean: ", e);
throw new RuntimeException(e);
}
}

@PreDestroy
public void preDestroy() {
System.err.println("####### EmployeeRequestBean DESTROYED #########");
}

}
问题:执行注销时,所有 session 作用域 bean 都会被破坏并调用 @PreDestroy执行,而 View 范围 bean @PreDestroy没有被调用!
此问题仅在 WebSphere 8.5.5.13 上发生,但在 tomcat 9 上运行相同的应用程序时,一切都按预期工作。

最佳答案

@PreDestroy很棘手。你看到了吗this问题?他们说最终应该在某个时候触发它。我想,这对你来说是不行的。
该怎么办?
避免 @PreDestroy ,我尽量不使用它们。我讨厌这样的问题 - 有些东西,有时会被触发,即使它今天有效,升级也会破坏它......
通常,我所做的是跟踪已启动和正在运行的 session 。其他一切都可以关闭 - 通过调用方法。这样我就可以进行测试、集成测试,还可以跟踪活跃用户的数量。像往常一样 - 更多的工作,更好的控制。
对我来说,分别是 JSF 和 Spring。那个@PreDestroy是 Spring 功能。我的猜测是有些东西保持对对象的引用,这就是它没有被销毁的原因。那个 jneat 与那个是分开的。这里证明,对于纯 Spring bean,它是有效的......

关于当 session 在 websphere 中失效时,Spring View 范围不会被破坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67885285/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com