2012-10-30 7 views
11

Napisałem usługę odpoczynku przy użyciu koszulki 1.13 i sprężyny 3.1.1, która działa na tomcat 6. W tomcat używam domeny, która wykona uwierzytelnienie. W mojej aplikacji potrzebuję bieżącego użytkownika, ale nie chcę uzyskać dostępu do SecurityContext z koszulki w każdym zasobie. Chcę wstrzyknąć obiekt ApplicationConfig o zasięgu żądania w moich zasobach odpoczynku, które będą zawierały bieżącego użytkownika. Później mogę rozszerzyć tę klasę, aby zawierała więcej parametrów konfiguracyjnych na poziomie żądania. Wydaje mi się to dobrą abstrakcją.Wypełnianie wniosku wiosennego z zakresem bean w ContainerRequestFilter

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

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

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

Utworzono aplikację ApplicationConfigManager, aby zapewnić dostęp do konfiguracji.

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

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

Menedżer konfiguracja aplikacji jest zdefiniowany jako singleton (domyślnie), ale ApplicationConfig powinien być zakres żądania, stąd adnotacji @Scope.

Używam (jersey) ContainterRequestFilter do ustawienia użytkownika na obiekcie konfiguracji aplikacji.

@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; 
    } 
} 

Aby zarejestrować ten filtr stworzyłem 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; 
    } 
} 

I uaktywniony przez ustawienie fabryczne to do 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> 

I dodał także te słuchacze bootstrap sprężyna kontekst i aby uzyskać zakres żądań działający

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

To, co umieścić w kontekście aplikacji, aby uzyskać funkcję skanowania pracy oraz określenie przedmiotu aplikacja config (myślę, że to nie jest nawet konieczne, ponieważ wiosną znajdzie je automagicznie)

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

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

a teraz moje problem. Kiedy zacznę, sprężyna aplikacji będzie bootstrapowana i wstrzyknie obiekt ApplicationConfig do ApplicationConfigManager, który jest wstrzykiwany do ApplicationConfigFilter.

W tym momencie rzuca wyjątek:

. 
. 
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 

Wyjątek ten jest dość oczywiste i myślę, że to oznacza, że ​​prośba o zakresie ApplicationConfig nie można wstrzykiwać ponieważ żadna prośba została jeszcze wysłana.

Więc I spring powinien utworzyć obiekt ApplicationConfig TYLKO podczas wysyłania żądania, a nie podczas uruchamiania aplikacji. Szukałem rozwiązań i odkryłem, że wstrzykiwanie komponentu o żądanym zakresie w pojedynczą fasolę nie jest zbyt logiczne. Zawsze i tak dostałbym ten sam przedmiot. Rozwiązaniem jest użycie serwera proxy więc zmieniłem adnotacji @Scope od klasy ApplicationConfig do tego

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

to powinno dać mi nowy obiekt ApplicationConfig dla każdego żądania.

Aplikacja uruchamia się teraz poprawnie (nie wydaje się tworzyć instancji ApplicationConfig), ale kiedy wysyłam żądanie do mojej usługi odpoczynku, znalazłem, używając debugowania, że ​​otrzymuję różne obiekty ApplicationConfig zamiast tego samego dla prośba.

Co więc robię źle?

Odpowiedz

7

Po zabawie z tym stwierdziłem, że ustawienie proxyMode na adnotacji @Scope i tak wykonało tę sztuczkę. To jest rozwiązanie. Serwer proxy będzie dbać o to, aby za każdym razem tworzyć nową instancję i upewniał się, że jest to żądanie o zakresie.

Powiązane problemy