我使用球衣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注释。
我正在使用(球衣)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>
我还添加了这些侦听器,以引导弹簧上下文并使请求范围正常工作
<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"/>
现在是我的问题。当我启动应用程序时,spring将被引导,它将把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注入到singleton bean中不是很合逻辑。无论如何,我总是会得到相同的对象。解决方案是使用代理,因此我将ApplicationConfig类上的@Scope注释更改为此
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
这应该为我的每个请求提供一个新的ApplicationConfig对象。
该应用程序现在可以正常启动(它似乎没有实例化ApplicationConfig),但是当我将请求发送到我的Rest服务时,发现使用调试发现我得到了不同的ApplicationConfig对象,而不是相同的请求对象。
所以我在做什么错?
[玩弄了之后,我发现@Scope批注上的proxyMode设置还是成功了。这就是解决方案。代理每次都会小心创建一个新实例,并确保它是请求范围的。
您可以使用@RequestScope
注释执行相同的操作。它只是以下各项的缩写:
@RequestScope