当前位置: 首页 > 知识库问答 >
问题:

使用内置注入的Jersey自定义方法参数注入

穆单鹗
2023-03-14
@java.lang.annotation.Target(ElementType.PARAMETER)
@java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
public @interface TenantParam {
}
public class TenantFactory implements Factory<Tenant> {

    private final HttpServletRequest request;
    private final ApiConfiguration apiConfiguration;

    @Inject
    public TenantFactory(HttpServletRequest request, @Named(ApiConfiguration.NAMED_BINDING) ApiConfiguration apiConfiguration) {
        this.request = request;
        this.apiConfiguration = apiConfiguration;
    }

    @Override
    public Tenant provide() {
        return null;
    }

    @Override
    public void dispose(Tenant tenant) {

    }
}
public class TenantParamResolver implements InjectionResolver<TenantParam> {

    @Inject
    @Named(InjectionResolver.SYSTEM_RESOLVER_NAME)
    private InjectionResolver<Inject> systemInjectionResolver;

    @Override
    public Object resolve(Injectee injectee, ServiceHandle<?> serviceHandle) {
        if(Tenant.class == injectee.getRequiredType()) {
            return systemInjectionResolver.resolve(injectee, serviceHandle);
        }
        return null;
    }

    @Override
    public boolean isConstructorParameterIndicator() {
        return false;
    }

    @Override
    public boolean isMethodParameterIndicator() {
        return true;
    }
}
@POST
@Timed
public ApiResponse create(User user, @TenantParam Tenant tenant) {
    System.out.println("resource method invoked. calling service method");
    System.out.println("service class" + this.service.getClass().toString());
    //DatabaseResult<User> result = this.service.insert(user, tenant);
    //return ApiResponse.buildWithPayload(new Payload<User>().addObjects(result.getResults()));
    return null;
}

下面是如何配置应用程序的

@Override
public void run(Configuration configuration, Environment environment) throws Exception {
    // bind auth and token param annotations
    environment.jersey().register(new AbstractBinder() {
        @Override
        protected void configure() {
            bindFactory(TenantFactory.class).to(Tenant.class);
            bind(TenantParamResolver.class)
                .to(new TypeLiteral<InjectionResolver<TenantParam>>() {})
                .in(Singleton.class);
        }
    });
}

问题是在应用程序启动期间,我得到以下错误

WARNING: No injection source found for a parameter of type public void com.proretention.commons.auth.resources.Users.create(com.proretention.commons.api.core.Tenant,com.proretention.commons.auth.model.User) at index 0.

并且有很长的堆栈错误堆栈和描述

    null

我刚试过用两个自定义方法param注入,那也不起作用

@POST
@Path("/login")
@Timed
public void validateUser(@AuthParam AuthToken token, @TenantParam Tenant tenant) {


}

共有1个答案

爱茂勋
2023-03-14

对于注入,方法参数的处理方式略有不同。为此我们需要实现的组件是ValueFactoryProvider。实现后,还需要在abstractbinder中绑定它。

Jersey在实现ValueFactoryProvider时遵循一种模式。这是用于处理@PathParam@QueryParam等参数的模式。Jersey有一个valuefactoryprovider用于其中的每一个,以及其他。

模式如下:

public static class TenantValueProvider extends AbstractValueFactoryProvider {

    @Inject
    public TenantValueProvider(MultivaluedParameterExtractorProvider mpep,
                           ServiceLocator locator) {
        super(mpep, locator, Parameter.Source.UNKNOWN);
    }

    @Override
    protected Factory<?> createValueFactory(Parameter parameter) {
        if (!parameter.isAnnotationPresent(TenantParam.class) 
                || !Tenant.class.equals(parameter.getRawType())) {
            return null;
        }
        return new Factory<Tenant>() {

            @Override
            public Tenant provide() {
                ...
            }
        };
    }
public static class TenantParamInjectionResolver 
        extends ParamInjectionResolver<TenantParam> {

    public TenantParamInjectionResolver() {
        super(TenantValueProvider.class);
    }
} 

这是真的。然后将两个组件绑定

public static class Binder extends AbstractBinder {
    @Override
    public void configure() {
        bind(TenantParamInjectionResolver.class)
                .to(new TypeLiteral<InjectionResolver<TenantParam>>(){})
                .in(Singleton.class);
        bind(TenantValueProvider.class)
                .to(ValueFactoryProvider.class)
                .in(Singleton.class);
    }
}

下面是使用Jersey测试框架的完整测试。javadoc注释中列出了所需的依赖项。您可以像运行任何其他JUnit测试一样运行该测试

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.logging.Logger;
import javax.inject.Inject;
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.Response;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;
import org.glassfish.jersey.server.model.Parameter;
import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

/**
 * Stack Overflow https://stackoverflow.com/q/29145807/2587435
 * 
 * Run this like any other JUnit test. Dependencies required are as the following
 * 
 *  <dependency>
 *      <groupId>org.glassfish.jersey.test-framework.providers</groupId>
 *      <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
 *      <version>2.22</version>
 *      <scope>test</scope>
 *  </dependency>
 *  <dependency>
 *      <groupId>org.glassfish.jersey.media</groupId>
 *      <artifactId>jersey-media-json-jackson</artifactId>
 *      <version>2.22</version>
 *      <scope>test</scope>
 *  </dependency>
 * 
 * @author Paul Samsotha
 */
public class TenantInjectTest extends JerseyTest {

    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface TenantParam {
    }

    public static class User {
        public String name;
    }

    public static class Tenant {
        public String name;
        public Tenant(String name) {
            this.name = name;
        }
    }

    public static class TenantValueProvider extends AbstractValueFactoryProvider {

        @Inject
        public TenantValueProvider(MultivaluedParameterExtractorProvider mpep,
                                   ServiceLocator locator) {
            super(mpep, locator, Parameter.Source.UNKNOWN);
        }

        @Override
        protected Factory<?> createValueFactory(Parameter parameter) {
            if (!parameter.isAnnotationPresent(TenantParam.class) 
                    || !Tenant.class.equals(parameter.getRawType())) {
                return null;
            }
            return new AbstractContainerRequestValueFactory<Tenant>() {
                // You can @Inject things here if needed. Jersey will inject it.
                // for example @Context HttpServletRequest

                @Override
                public Tenant provide() {
                    final ContainerRequest request = getContainerRequest();
                    final String name 
                            = request.getUriInfo().getQueryParameters().getFirst("tenent");
                    return new Tenant(name);
                }
            };
        }

        public static class TenantParamInjectionResolver 
                extends ParamInjectionResolver<TenantParam> {

            public TenantParamInjectionResolver() {
                super(TenantValueProvider.class);
            }
        } 

        public static class Binder extends AbstractBinder {
            @Override
            public void configure() {
                bind(TenantParamInjectionResolver.class)
                        .to(new TypeLiteral<InjectionResolver<TenantParam>>(){})
                        .in(Singleton.class);
                bind(TenantValueProvider.class)
                        .to(ValueFactoryProvider.class)
                        .in(Singleton.class);
            }
        }
    }


    @Path("test")
    @Produces("text/plain")
    @Consumes("application/json")
    public static class TestResource {
        @POST
        public String post(User user, @TenantParam Tenant tenent) {
            return user.name + ":" + tenent.name;
        }
    }

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

    @Test
    public void shouldReturnTenantAndUserName() {
        final User user = new User();
        user.name = "peeskillet";
        final Response response = target("test")
                .queryParam("tenent", "testing")
                .request()
                .post(Entity.json(user));

        assertEquals(200, response.getStatus());
        assertEquals("peeskillet:testing", response.readEntity(String.class));
    }
}

另见:

    null
 类似资料:
  • 问题内容: 我正在从DropWizard 0.7.1迁移到0.8.1的过程中。这包括从Jersey 1.x迁移到2.x。在使用Jersey 1.18.1的实现中,我实现了(为简单起见,更改了所有类名)。此类将创建包含自定义注入批注的对象。包含传递并由读取的各种属性。最后,在该类中,我注册的新实例,如下所示。 我已经进行了一些研究,似乎无法完全围绕如何在Jersey 2.x中创建(或替换)这样的se

  • 问题内容: 如果要以非安全方式访问某些方法,我想限制它们。我正在创建一个@Secure批注,该批注检查请求是否通过安全通道发送。但是,我无法创建一个可注入的方法来捕获请求的HttpContext。 我使用的是Dropwizard框架,因此提供程序的初始化应该像下面这样简单: 用法: 在这一点上,我假设HttpContext Injectable在某种方法上不起作用,但是我对可以用来实现此注释的其他

  • 目前,我将辅助注射与命名参数一起使用,如下所示: 这很棒。但是我认为使用字符串作为参数的标识符有点难看。我想做的是以下内容: 所以本质上我想要自定义辅助注释。有办法做到这一点吗?

  • 我有一个方法 我可能更容易获得查询参数的映射。

  • 我尝试使用类似于< code > org . spring framework . cache . annotation . cache able 的东西: 自定义注释: 方面: 服务: 我的IDE (IntelliJ)看不出< code>key = "#id"用法有什么特别之处,相比之下,< code>Cacheable的类似用法用不同于纯文本的颜色显示。我提到IDE部分只是作为一个提示,以防它