在 Jersey 2.27 中注册自定义 ValueParamProvider [英] Registering a custom ValueParamProvider in Jersey 2.27

查看:21
本文介绍了在 Jersey 2.27 中注册自定义 ValueParamProvider的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I realize that these are internal APIs, but if they're available internally why not make them usable by the less privileged masses, and they're also extremely useful. Even though these APIs were internal in Jersey 2.25 they could be used, and I'd like to upgrade my Jersey version without breaking my custom Jersey extensions.

当然可以扩展 ValueParamProvider 在 Jersey 2.27 中,但我不再看到注册该 Provider 及其触发注释的方法.看看 Jersey 如何为自己的实现做到这一点,它现在使用 BoostrapConfigurator,它似乎被内部化到外部实现无法使用相同方法的程度.

It's certainly possible to extend ValueParamProvider in Jersey 2.27, but I no longer see a way to register that Provider along with it's triggering annotation. Looking at how Jersey does this for its own implementations, it now uses a BoostrapConfigurator, which seems to be internalized to such an extent that external implementations can't use the same methodology.

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

Maybe I'm wrong about that, and if someone has a clear description of how, that would be great. Otherwise, does anyone know of a method for doing the same thing?

这个工作了……

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 的适当实现.

With appropriate implementations of AbstractValueFactoryProvider and ParamInjectionResolver.

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

Now it looks like you need to implement ValueParamProvider, which is easy enough, but I'm not sure how to register that properly with the Jersey framework anymore. Any help appreciated.

推荐答案

您不需要使用任何 BootstrapConfigurator.您只需将服务添加到注入器和 它们将在稍后添加到值提供者列表中.

You don't need to use any BootstrapConfigurator. All you need to is add the services to the injector and they will be added later to the list of value providers.

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

To configure it, you can still use the AbstractBinder, but instead of the HK2 one, use the Jersey one. The ValueParamProvider can still be bound the same way, but for the InjectionResolver, you should make sure to implement not the HK2 resolver, but the Jersey one. Then instead of binding to TypeLiteral, bind to GenericType.

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

I just want to add that a misconception that people have when trying to implement parameter injection is that we also need an InjectResolver to use a custom annotation for the method parameter. This is not the case. The method parameter annotation is just a marker annotation that we should check inside ValueParamProvider#getValueProvider() method. An InjectResolver is only needed for non-method-parameter injections, for instance field and constructor injection. If you don't need that, then you don't need the InjectionResolver.

以下是使用 Jersey 的完整示例测试框架.我没有使用 InjectionResolver,只是为了表明它不需要.

Below is a complete example using Jersey Test Framework. I didn't use an InjectionResolver, just to show that it's not needed.

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 并实现 ParamInjectionResolver 的先前版本中,大多数人这样做是为了遵循 Jersey 如何实现参数注入,同时仍然允许其他注入点(字段和构造函数).如果你仍然想使用这个模式,你可以.


Another thing I'll mention is that in previous versions where you extended AbstractValueFactoryProvider and implemented a ParamInjectionResolver, most people did this to follow how Jersey implemented parameter injection while still allowing for other injection points (field and constructor). If you still want to use this pattern, you can.

下面是上述测试重构的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 已经有一个具体的类,我们可以直接使用,就像上面的特性一样.

I figured this stuff out just digging through the source. All this configuration I saw in the ValueParamProviderConfigurator. You don't need to implement your own ParamInjectionResolver. Jersey has a concrete class already that we can just use, as done in the feature above.

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

If you change the TestResource to inject by field, it should work now

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

这篇关于在 Jersey 2.27 中注册自定义 ValueParamProvider的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆