【问题标题】:@Inject not working in MethodInterceptor managed by HK2@Inject 在 HK2 管理的 MethodInterceptor 中不起作用
【发布时间】:2016-04-26 08:41:30
【问题描述】:

我目前正在重构一个 Jersey Web App,并希望将一些横切关注点捆绑在他们自己的类中,并使用注释来拦截方法。例如,有很多方法我需要检查用户是否是他想要更改的实体的所有者(在我的情况下这是一个项目)。因此,在拦截器中,我需要进行数据库调用,尽管注入适当的 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:

我使用的是 Jersey 2.17

【问题讨论】:

    标签: java jersey jersey-2.0 interceptor hk2


    【解决方案1】:

    问题是拦截器永远不会经历 DI 生命周期,因为 if 不是容器中的服务。您正在自己实例化它。当您在 DI 框架中执行此操作时,大多数情况下您都可以期待这个结果。

    可以做的是使用 HK2 容器 ServiceLocator 自己显式注入它。您将定位器注入InterceptionService,然后调用locator.inject(interceptor)。此方法是显式注入 any 任意对象的通用方法。所以你可能会改变它像

    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);
    

    另请参阅: Injecting Request Scoped Objects into Singleton Scoped Object with HK2 and Jersey

    下面是使用Jersey Test Framework的完整示例。

    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"));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-10-11
      • 1970-01-01
      • 2014-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多