簡體   English   中英

@Inject 在 HK2 管理的 MethodInterceptor 中不起作用

[英]@Inject not working in MethodInterceptor managed by HK2

我目前正在重構一個 Jersey 網絡應用程序,並希望在他們自己的類中捆綁一些橫切關注點並使用注釋來攔截方法。 例如,有很多方法我需要檢查用戶是否是他想要更改的實體的所有者(在我的情況下這是一個項目)。 因此,在攔截器中,我需要進行數據庫調用,盡管注入適當的 DAO 將是最好的方法。

目前我的攔截器看起來像這樣:

 public class ProjectOwnerCheckInterceptor implements MethodInterceptor {

        @Inject
        private EntityManager em;

        @Inject
        private UserProvider userProvider;

        @Inject
        private RMUserDAO rmUserDAO;

        @Inject
        private ProjectDAO projectDAO;

        public ProjectOwnerCheckInterceptor() {
        // TODO Auto-generated constructor stub
        }

        @Override
        public Object invoke(MethodInvocation arg0) throws Throwable {

            // First of all let's get the annotation
            ProjectOwnerCheck check = arg0.getMethod().getAnnotation(ProjectOwnerCheck.class);

            // if there is no check, then just proceed!
            if (check == null)
                arg0.proceed();

            long projectId = (long) arg0.getArguments()         [check.projectIdIndex()];

            // Handling ownership!!
            Project project = getProjectOrThrow(projectId);

            return arg0.proceed();

        }
    }

自定義注釋是直截了當的。 我需要添加一些小信息,方法中的 entityId 必須檢查哪些參數位置,因為參數和類型的數量會有所不同:

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface ProjectOwnerCheck {

    int projectIdIndex() default -1;

}

為了讓 Jersey/HK2 知道如何處理攔截器,我創建了一個實現 InterceptionService 的過濾器:

public class HK2InterceptorFilter implements InterceptionService {

    private final static MethodInterceptor PROJECT_CHECK_METHOD_INTERCEPTOR = new ProjectOwnerCheckInterceptor();
    private final static List<MethodInterceptor> PROJECT_CHECK_METHOD_LIST = Collections
            .singletonList(PROJECT_CHECK_METHOD_INTERCEPTOR);

    public HK2InterceptorFilter() {
        // TODO Auto-generated constructor stub
    }

    @Override
    public Filter getDescriptorFilter() {

        return BuilderHelper.allFilter();

    }

    @Override
    public List<MethodInterceptor> getMethodInterceptors(Method method) {

        if (method.isAnnotationPresent(ProjectOwnerCheck.class))
            return PROJECT_CHECK_METHOD_LIST;

        return null;

    }

    @Override
    public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> constructor) {
        // TODO Auto-generated method stub
        return null;
    }

}

我在 JerseyApplication 類中綁定該過濾器:

register(new AbstractBinder() {
            @Override
            protected void configure() {
                try {

                    bind(HK2InterceptorFilter.class).to(InterceptionService.class).in(Singleton.class);
                    bind(getPasswordStorage()).to(PasswordStorage.class);
                    bind(getDocumentService()).to(DocumentService.class);
                    bind(UserManagementAccessor.getUserProvider()).to(UserProvider.class);
                } catch (Exception e) {
                    throw new InternalServerErrorException(e);
                }
            }
        });

在我的攔截器中設置斷點時,我可以看到它被正確實例化並且方法被調用。 但我完全懷念的是我需要檢查的所有@Inject 字段。 我是不是遺漏了什么,或者這在 HK2 中是不可能的。 我曾經與 Guice 一起工作並且它正在工作(我 - 由於該應用程序的代碼庫非常大,但時間有限 - 綁定到 HK2 :))。

提前感謝您的所有幫助!

PS:

我正在使用澤西島 2.17

問題是攔截器永遠不會經歷 DI 生命周期,因為 if 不是容器中的服務。 您正在自己實例化它。 當您在 DI 框架中執行此操作時,大多數情況下您可以預期此結果。

可以做的是使用 HK2 容器ServiceLocator自己顯式注入它。 您將定位器注入InterceptionService ,然后調用locator.inject(interceptor) 此方法是顯式注入任何任意對象的通用方法。 所以你可能會改變它像

private final List<MethodInterceptor> PROJECT_CHECK_METHOD_LIST;

@Inject
public HK2InterceptorFilter(ServiceLocator locator) {
    final MethodIntercator i = new ProjectOwnerCheckInterceptor();
    locator.inject(i)
    PROJECT_CHECK_METHOD_LIST = Collections.singletonList(i);
}

您將面臨的另一個問題是攔截器是一個單例,但是您嘗試注入的所有服務看起來都像是請求范圍的。 這是一個問題,因為它們需要根據每個請求進行更改。 為此,我們可以做的是讓它們成為代理。 我們可以簡單地通過在綁定中鏈接幾個方法來做到這一點

bind(getPasswordStorage())
    .to(PasswordStorage.class)
    .proxy(true)
    .proxyForSameScope(false)
    .in(RequestScoped.class);

另請參閱: 使用 HK2 和 Jersey 將請求范圍的對象注入到單例范圍的對象中

下面是一個使用Jersey 測試框架的完整示例。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;

import org.aopalliance.intercept.ConstructorInterceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.glassfish.hk2.api.Filter;
import org.glassfish.hk2.api.InterceptionService;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.BuilderHelper;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

/**
 * Stack Overflow question http://stackoverflow.com/q/36859669/2587435
 * 
 * Run this like any other JUnit test. One one required test dependency:
 * 
 * <dependency>
 *     <groupId>org.glassfish.jersey.test-framework.providers</groupId>
 *     <artifactId>jersey-test-framework-provider-inmemory</artifactId>
 *     <version>${jersey2.version}</version>
 * </dependency>
 *
 * @author Paul Samsotha
 */
public class InterceptionTest extends JerseyTest {

    public static interface HeaderProvider {
        String getXCustomHeader();
    }

    public static class HeaderProviderImpl implements HeaderProvider {
        @Context
        private HttpHeaders headers;

        @Override
        public String getXCustomHeader() {
            return headers.getHeaderString("X-Custom-Header");
        }
    }

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface Intercept {
    }

    public static class MyMethodInterceptor implements MethodInterceptor {

        @Inject
        private HeaderProvider provider;

        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            return provider.getClass().getName() + ":" + provider.getXCustomHeader();
        }

    }

    public static class InterceptionHandler implements InterceptionService {

        private final List<MethodInterceptor> interceptors;

        @Inject
        public InterceptionHandler(ServiceLocator locator) {
            final MethodInterceptor interceptor = new MyMethodInterceptor();
            locator.inject(interceptor);
            interceptors = Collections.singletonList(interceptor);
        }

        @Override
        public Filter getDescriptorFilter() {
            return BuilderHelper.allFilter();
        }

        @Override
        public List<MethodInterceptor> getMethodInterceptors(Method method) {
            if (method.isAnnotationPresent(Intercept.class)) {
                return interceptors;
            }
            return null;
        }

        @Override
        public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> c) {
            return null;
        }
    }

    public static class Binder extends AbstractBinder {
        @Override
        protected void configure() {
            bind(InterceptionHandler.class)
                    .to(InterceptionService.class)
                    .in(Singleton.class);
            bind(HeaderProviderImpl.class)
                    .to(HeaderProvider.class)
                    .proxy(true)
                    .proxyForSameScope(false)
                    .in(RequestScoped.class);
        }
    }

    @Path("intercept")
    public static class TestResource {

        @GET
        @Intercept
        public String get() {
            return null;
        }
    }

    @Override
    public ResourceConfig configure() {
        return new ResourceConfig(TestResource.class)
                .register(new Binder())
                .register(new LoggingFilter(Logger.getAnonymousLogger(), true));          
    }

    @Test
    public void shouldReturnHeaderAndProxyClass() {
        Response response = target("intercept").request()
                .header("X-Custom-Header", "Value1")
                .get();
        assertThat(response.getStatus(), is(200));
        String entity = response.readEntity(String.class);
        response.close();
        assertThat(entity, containsString("Value1"));
        assertThat(entity, containsString("Proxy"));

        // Change header to make sure we aren't getting the same HttpHeaders instance
        response = target("intercept").request()
                .header("X-Custom-Header", "Value2")
                .get();
        assertThat(response.getStatus(), is(200));
        entity = response.readEntity(String.class);
        response.close();
        assertThat(entity, containsString("Value2"));
        assertThat(entity, containsString("Proxy"));
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM