在会话销毁时从HttpSessionEventPublisher访问会话范围的bean

时间:2017-11-07 13:40:39

标签: java spring session dependency-injection

我正在使用Spring框架开发Java webapp。 我需要在用户会话结束时调用webservice:

  • 当用户正式退出应用程序时
  • 会话在没有用户操作的情况下过期

我实现了一个HttpSessionEventPublisher来通过提供的客户端调用服务:

public class SessionDestroyedListener extends HttpSessionEventPublisher {

    /**
     * {@inheritDoc}
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent event) {

        // Retrieve managed beans from SessionEvent
        HttpSession session = event.getSession();
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());

        // This is the session scoped bean : I can access it from the HttpSessionEventPublisher
        SessionBean sessionBean = (SessionBean) session.getAttribute("scopedTarget.sessionBean");

        // This is the service client : I can access it from the HttpSessionEventPublisher
        ServiceClient serviceClient = (ServiceClient) ctx.getBean("serviceClient");

        // This is the call that raises an error
        serviceClient.callService();

        super.sessionDestroyed(event);
    }
}

在服务客户端(我无法按照自己的意愿修改它,它由另一个团队提供),会注入会话范围的bean来检索用户身份以进行身份​​验证:

public class ServiceClientImpl implements ServiceClient {

    // This is the injected session bean
    @Autowired
    private SessionBean sessionBean;

     /**
     * {@inheritDoc}
     */
    @Override
    public void callService() {
        // The service is called using data from the injected session bean and raises an error
    }

}

当用户正确注销时,一切正常,因为我们处于请求上下文中。 但是当会话在会话超时后到期时,我收到以下错误:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.sessionBean': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:341)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:192)
    at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.getTarget(Cglib2AopProxy.java:653)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:604)
    at com.mycompany.myapp.bean.SessionBean$$EnhancerByCGLIB$$c324aab0.getUserId()
    at com.mycompany.myapp.client.ServiceClientImpl.callService(ServiceClientImpl.java:165)
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
    at org.springframework.web.context.request.SessionScope.get(SessionScope.java:90)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:327)
    ... 18 more

是否有解决方法/黑客使用提供的客户端在请求范围之外调用该服务?

修改

马库斯在这个问题中解释了类似的问题:
Autowire session-scoped bean into thread (Spring)

他的最后评论是:

  

看起来也是在线程外部的submissionDao中手动设置UserInfoBean对象,一切正常。我会接受这个答案,因为它给了我解决问题所需的一切。感谢

出于测试目的,我将SessionBean的setter添加到服务客户端:

public class ServiceClientImpl implements ServiceClient {

    // This is the injected session bean
    @Autowired
    private SessionBean sessionBean;

     /**
     * {@inheritDoc}
     */
    @Override
    public void callService() {
        // The service is called using data from the injected session bean and raises an error
    }

     /**
     * {@inheritDoc}
     */
    @Override
    public void setSessionBean(SessionBean sessionBean) {
        this.sessionBean = sessionBean;
    }

}

在引发异常的方法之前调用了setter:

public class SessionDestroyedListener extends HttpSessionEventPublisher {

    /**
     * {@inheritDoc}
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent event) {

        // Retrieve managed beans from SessionEvent
        HttpSession session = event.getSession();
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());

        // This is the session scoped bean : I can access it from session attributes
        SessionBean sessionBean = (SessionBean) session.getAttribute("scopedTarget.sessionBean");

        // This is the service client : I can access it from spring context
        ServiceClient serviceClient = (ServiceClient) ctx.getBean("serviceClient");

        // After overriding autowired bean in the service client, no more error
        serviceClient.setSessionBean(sessionBean);
        serviceClient.callService();

        super.sessionDestroyed(event);
    }
}

除了工作之外,这一切都不优雅......现在的问题是:

  • 由于ServiceClient是一个单独的bean,它是否打破了autowire macanism?
  • 如果来自另一个会话的另一个用户调用ServiceClient方法,SessionBean将是会话范围的bean代理还是我在HttpSessionEventPublisher中设置的最后一个bean?

0 个答案:

没有答案