在ContainerRequestFilter中填充spring请求范围的bean

时间:2012-10-30 10:14:52

标签: spring jersey

我使用jersey 1.13和spring 3.1.1编写了一个休息服务,它运行在tomcat 6上。 在tomcat我正在使用一个将进行身份验证的领域。 在我的应用程序中,我需要当前用户,但我不想在每个资源中从泽西访问SecurityContext。我想在我的其余资源中注入一个请求范围的ApplicationConfig对象,该资源将包含当前用户。稍后我可以扩展此类以包含更多请求级别配置参数。这对我来说似乎是一个很好的抽象。

@Component
@Scope(value = "request")
public class ApplicationConfig
{
    private String userCode;

    public String getUserCode()
    {
        return this.userCode;
    }

    public void setUserCode(String userCode)
    {
        this.userCode = userCode;
    }
}

我创建了一个ApplicationConfigManager来提供对配置的访问。

@Component
public class ApplicationConfigManager
{
    @Autowired
    public ApplicationConfig applicationConfig;

    public ApplicationConfig getApplicationConfig()
    {
        return this.applicationConfig;
    }
}

应用程序配置管理器被定义为单例(默认),但ApplicationConfig应该是请求范围,因此是@Scope注释。

我正在使用(jersey)ContainterRequestFilter在应用程序配置对象上设置用户。

@Component
@Provider
public class ApplicationConfigFilter implements ResourceFilter, ContainerRequestFilter
{
    @Autowired
    private ApplicationConfigManager applicationConfigManager;

    @Override
    public ContainerRequest filter(ContainerRequest request)
    {
        this.applicationConfigManager.getApplicationConfig().setUserCode(
            request.getSecurityContext().getUserPrincipal().getName()
        );
        return request;
    }

    @Override
    public ContainerRequestFilter getRequestFilter()
    {
        return this;
    }

    @Override
    public ContainerResponseFilter getResponseFilter()
    {
        return null;
    }
}

要注册此过滤器,我创建了一个ResourceFilterFactory

@Component
@Provider
public class ResourceFilterFactory extends RolesAllowedResourceFilterFactory
{
    @Autowired
    private ApplicationConfigFilter applicationConfigFilter;

    @Override
    public List<ResourceFilter> create(AbstractMethod am)
    {
        // get filters from RolesAllowedResourceFilterFactory Factory!
        List<ResourceFilter> rolesFilters = super.create(am);
        if (null == rolesFilters) {
            rolesFilters = new ArrayList<ResourceFilter>();
        }

        // Convert into mutable List, so as to add more filters that we need
        // (RolesAllowedResourceFilterFactory generates immutable list of filters)
        List<ResourceFilter> filters = new ArrayList<ResourceFilter>(rolesFilters);

        filters.add(this.applicationConfigFilter);

        return filters;
    }
}

我通过将此工具设置为web.xml

来激活此工厂
<servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
        <param-name>com.sun.jersey.config.property.packages</param-name>
        <param-value>com.mypackage</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
        <param-value>com.mypackage.ResourceFilterFactory</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

我还添加了这些侦听器来引导spring上下文并使请求范围正常工作

<listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

这是我在我的应用程序上下文中放置扫描功能并定义应用程序配置对象(我想这甚至不是必需的,因为spring会自动找到它)

<context:annotation-config/>
<context:component-scan base-package="com.mypackage" />

<bean id="applicationConfig" class="com.mypackage.ApplicationConfig" scope="request"/>

现在我的问题。当我启动应用程序时,弹簧将被引导,它会将ApplicationConfig对象注入ApplicationConfigManager,并注入到ApplicationConfigFilter中。

此时它会抛出异常:

.
.
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 o
perating 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) ~[spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
        at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:40) ~[spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:328) ~[spring-beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]
        ... 57 common frames omitted

此异常非常清楚,我认为这意味着无法注入请求范围的ApplicationConfig,因为尚未发送任何请求。

所以我春天应该只在发送请求时实例化ApplicationConfig对象,而不是在应用程序启动期间。我搜索了解决方案,发现在单例bean中注入请求范围的bean不是很合乎逻辑。无论如何,我总会得到同样的物体。解决方案是使用代理,因此我将ApplicationConfig类的@Scope注释更改为此

@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)

这应该为每个请求提供一个新的ApplicationConfig对象。

应用程序现在启动就好了(它似乎没有实例化ApplicationConfig)但是当我向我的休息服务发送请求时,我发现,使用调试,我获得了不同的ApplicationConfig对象而不是相同的请求。

那么我做错了什么?

2 个答案:

答案 0 :(得分:8)

在玩完这个之后,我发现@Scope注释上的proxyMode设置无论如何都能解决问题。这就是解决方案。代理将每次都创建一个新实例,并确保它是请求范围。

答案 1 :(得分:0)

您可以使用@RequestScope注释来完成此操作。 它只是以下内容的缩写:

@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)