我目前正在重构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();
}
}
自定义注释很简单。我需要添加一些小信息,因为参数和类型的数量各不相同,因此该方法必须检查哪个objectId在方法中的位置:
@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;
}
}
我将那个Filter绑定到我的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
最佳答案
问题在于,拦截器永远不会经历DI生命周期,因为如果不是容器中的服务,该拦截器就不会通过。您正在自己实例化它。当您在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);
另请参见: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"));
}
}
关于java - @Inject在HK2管理的MethodInterceptor中不起作用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36859669/