在ContainerRequestFilter中填充Spring Request范围的Bean [英] Populating a spring request scoped bean in a ContainerRequestFilter

查看:150
本文介绍了在ContainerRequestFilter中填充Spring Request范围的Bean的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I wrote a rest service using jersey 1.13 and spring 3.1.1 which runs on tomcat 6. In tomcat I'm using a realm which will do the authentication. In my application I need the current user but I don't want to access the SecurityContext from jersey in every resource. I want to inject a request scoped ApplicationConfig object in my rest resources which will contain the current user. Later I can extend this class to contain more request level configuration parameteters. This seems a nice abstraction to me.

@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来提供对配置的访问.

I created a ApplicationConfigManager to provide access to the configuration.

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

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

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

The application config manager is defined as a singleton (default) but the ApplicationConfig should be request scope, hence the @Scope annotation.

我正在使用(球衣)ContainterRequestFilter在应用程序配置对象上设置用户.

I'm using a (jersey) ContainterRequestFilter to set the user on the application config object.

@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

To register this filter I created a 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来激活了该工厂

I activated this factory by setting this into the 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上下文并确保请求范围正常工作

I also added these listeners to bootstrap the spring context and to get the request scope working

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

这就是我放入应用程序上下文中以使扫描功能正常工作并定义应用配置对象的原因(我想这甚至没有必要,因为spring会自动找到它)

This is what I put in my application context to get the scanning feature working and to define the aplication config object (I guess this is not even necessary as spring will find it automagically)

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

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

现在是我的问题.当我启动应用程序时,spring将被引导,并将ApplicationConfig对象注入到ApplicationConfigManager中,该ApplicationConfigManager被注入到ApplicationConfigFilter中.

And now my problem. When I start the application spring will be bootstrapped and it will inject the ApplicationConfig object onto the ApplicationConfigManager which is injected onto the 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.

This exception is pretty clear and I think it means the request scoped ApplicationConfig cannot be injected because no request was sent yet.

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

So I spring should instantiate the ApplicationConfig object ONLY when a request is sent and not during application startup. I searched for solutions and found that injecting a request scoped bean in a singleton bean is not very logical. I would always get the same object anyway. The solution is to use a proxy so I changed the @Scope annotation on the ApplicationConfig class to this

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

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

This should give me a new ApplicationConfig object for every request.

应用程序现在可以正常启动了(它似乎没有实例化ApplicationConfig),但是当我向我的Rest服务发送请求时,我发现通过调试发现我得到了不同的ApplicationConfig对象,而不是相同的对象.请求.

The app now starts up just fine (it doesn't seem to instantiate the ApplicationConfig) but when I send a request to my rest service I found, using debugging, that I get different ApplicationConfig objects instead of the same one for the request.

那我在做什么错了?

推荐答案

玩了这个之后,我发现@Scope批注上的proxyMode设置还是可以解决问题的.所以这就是解决方案.代理会在每次创建新实例时保持谨慎,并确保它是请求范围的.

After playing around with this I found that the proxyMode setting on the @Scope annotation did the trick anyway. So that is the solution. The proxy will take care in creating a new instance every time and will make sure it is request scoped.

这篇关于在ContainerRequestFilter中填充Spring Request范围的Bean的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆