14

Tomcat 6 で実行される jersey 1.13 と spring 3.1.1 を使用してレスト サービスを作成しました。Tomcat では、認証を行うレルムを使用しています。私のアプリケーションでは現在のユーザーが必要ですが、すべてのリソースで jersey から 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"/>

そして今、私の問題。アプリケーション スプリングを起動すると、ブートストラップされ、ApplicationConfig オブジェクトが ApplicationConfigFilter に注入される ApplicationConfigManager に注入されます。

この時点で、例外がスローされます。

.
.
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 オブジェクトを取得することがわかりました。

それで、私は何を間違っていますか?

4

2 に答える 2

12

これで遊んだ後、 @Scope アノテーションの proxyMode 設定がとにかくうまくいくことがわかりました。それが解決策です。プロキシは毎回新しいインスタンスを作成することに注意を払い、リクエスト スコープであることを確認します。

于 2012-11-05T07:44:02.393 に答える