首页 > 解决方案 > 在 Jersey 2.27 中注册自定义 ValueParamProvider

问题描述

我意识到这些是内部 API,但如果它们在内部可用,为什么不让特权较低的大众使用它们,它们也非常有用。即使这些 API 在 Jersey 2.25 中是内部的,它们也可以使用,而且我想升级我的 Jersey 版本而不破坏我的自定义 Jersey 扩展。

当然可以ValueParamProvider在 Jersey 2.27 中进行扩展,但我不再看到注册该 Provider 及其触发注释的方法。查看 Jersey 如何为其自己的实现执行此操作,它现在使用 a BoostrapConfigurator,这似乎已被内部化到外部实现无法使用相同方法的程度。

也许我错了,如果有人清楚地描述了如何,那就太好了。否则,有人知道做同样事情的方法吗?

曾经工作...

ResourceConfig resourcceConfig = ...

resourceConfig.register(new AbstractBinder() {

    @Override
    protected void configure (){ 
      bind(MyParamValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
      bind(MyParamInjectionResolver.class).to(new TypeLiteral<InjectionResolver<EntityParam>>() {

      }).in(Singleton.class);
    }
  }
});

通过适当的实现AbstractValueFactoryProviderParamInjectionResolver

现在看起来你需要实现ValueParamProvider,这很容易,但我不知道如何在 Jersey 框架中正确注册它。任何帮助表示赞赏。

标签: javajerseyjersey-2.0

解决方案


你不需要使用任何BootstrapConfigurator. 您所需要做的就是将服务添加到注入器,它们稍后将被添加到价值提供者列表中。

要配置它,您仍然可以使用AbstractBinder,但不要使用 HK2 ,而是使用Jersey 1ValueParamProvider仍然可以以相同的方式绑定,但是对于,InjectionResolver您应该确保实现的不是 HK2 解析器,而是Jersey one。然后不是绑定到TypeLiteral,而是绑定到GenericType

我只想补充一点,人们在尝试实现参数注入时存在的一个误解是,我们还需要InjectResolver为方法参数使用自定义注释。不是这种情况。方法参数注释只是我们应该在ValueParamProvider#getValueProvider()方法内部检查的标记注释。AnInjectResolver仅用于非方法参数注入,例如字段和构造函数注入。如果你不需要它,那么你不需要InjectionResolver.

下面是一个使用Jersey Test Framework的完整示例。我没有使用InjectionResolver,只是为了表明它不是必需的。

import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Parameter;
import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.core.Response;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.function.Function;

import static org.assertj.core.api.Assertions.assertThat;


public class ParamInjectTest extends JerseyTest {

    @Target({ElementType.PARAMETER, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Auth {
    }

    private static class User {
        private String username;
        public User(String username) {
            this.username = username;
        }
        public String getUsername() {
            return this.username;
        }
    }

    public static class AuthValueParamProvider implements ValueParamProvider {

        @Override
        public Function<ContainerRequest, ?> getValueProvider(Parameter parameter) {
            if (parameter.getRawType().equals(User.class)
                    && parameter.isAnnotationPresent(Auth.class)) {
                return new UserParamProvider();
            }
            return null;
        }

        private class UserParamProvider implements Function<ContainerRequest, User> {
            @Override
            public User apply(ContainerRequest containerRequest) {
                return new User("Peeskillet");
            }
        }

        @Override
        public PriorityType getPriority() {
            return Priority.HIGH;
        }
    }

    public static class AuthFeature implements Feature {

        @Override
        public boolean configure(FeatureContext context) {
            context.register(new AbstractBinder() {
                @Override
                protected void configure() {
                    bind(AuthValueParamProvider.class)
                            .to(ValueParamProvider.class)
                            .in(Singleton.class);
                }
            });

            return true;
        }
    }

    @Path("test")
    @Consumes("text/plain")
    public static class TestResource {
        @POST
        @Produces("text/plain")
        public Response post(String text, @Auth User user) {
            return Response.ok(user.getUsername() + ":" + text).build();
        }
    }


    @Override
    public ResourceConfig configure() {
        return new ResourceConfig()
                .register(TestResource.class)
                .register(AuthFeature.class);
    }

    @Test
    public void testIt() {
        final Response response  = target("test")
                .request()
                .post(Entity.text("Test"));

        assertThat(response.getStatus()).isEqualTo(200);
        assertThat(response.readEntity(String.class)).isEqualTo("Peeskillet:Test");
    }
}

我要提到的另一件事是,在您扩展AbstractValueFactoryProvider和实现 a的先前版本中ParamInjectionResolver,大多数人这样做是为了遵循 Jersey 如何实现参数注入,同时仍然允许其他注入点(字段和构造函数)。如果您仍想使用此模式,则可以。

下面是AuthFeature从上面的测试重构

public static class AuthFeature implements Feature {

    @Override
    public boolean configure(FeatureContext context) {
        InjectionManager im = InjectionManagerProvider.getInjectionManager(context);

        AuthValueParamProvider authProvider = new AuthValueParamProvider();

        im.register(Bindings.service(authProvider).to(ValueParamProvider.class));

        Provider<ContainerRequest> request = () -> {
            RequestProcessingContextReference reference = im.getInstance(RequestProcessingContextReference.class);
            return reference.get().request();
        };

        im.register(Bindings.injectionResolver(new ParamInjectionResolver<>(authProvider, Auth.class, request)));

        return true;
    }
}

我只是从源头中挖掘出这些东西。我在ValueParamProviderConfigurator. 您不需要实现自己的ParamInjectionResolver. Jersey 已经有一个具体的类,我们可以直接使用,就像上面的特性一样。

如果您TestResource将按字段更改为注入,它现在应该可以工作

@Path("test")
@Consumes("text/plain")
public static class TestResource {

    @Auth User user;

    @POST
    @Produces("text/plain")
    public Response post(String text) {
        return Response.ok(user.getUsername() + ":" + text).build();
    }
}

推荐阅读