Java/Jersey - creating own injection resolver with ParamInjectionResolver - strange behavior

南笙酒味 提交于 2019-12-06 06:12:37

On startup, Jersey builds an internal model of all the resources. Jersey uses this model to process requests. Part of that model consists of all the resource methods and all of its parameters. To take it even further, Jersey will also validate the model to make sure it is a valid model. Something invalid in the model may cause Jersey not to be able to process that model during runtime. So this validation is there to protect us.

That being said, part of the validation process is to validate the method parameters. There are rules that govern what we can have as parameters. For example, a @QueryParam parameters must meet one of the requirements mentioned here in the javadoc:

  1. Be a primitive type
  2. Have a constructor that accepts a single String argument
  3. Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String))
  4. Have a registered implementation of ParamConverterProvider JAX-RS extension SPI that returns a ParamConverter instance capable of a "from string" conversion for the type.
  5. Be List<T>, Set<T> or SortedSet<T>, where T satisfies 2, 3 or 4 above. The resulting collection is read-only.

Here's something you can try out. Add a @QueryParam using the following arbitrary class

public class Dummy {
  public String value;
}

@GET
public Response get(@QueryParam("dummy") Dummy dummy) {}

Notice that the Dummy class doesn't meet any of the requirements listed above. When you run the application, you should get an exception on startup, causing the application to fail. The exception will be something like

ModelValidationException: No injection source for parameter ...

This means that the validation of the model failed because Jersey has no idea how to create the Dummy instance from the query param, as it doesn't follow the rules of what is allowed.

Ok. so how is this all related to your question? Well, all parameter injection requires a ValueFactoryProvider to be able to provide a value for it. If there isn't one, then the parameter will not be able to be created at runtime. So Jersey validates the parameters by checking for the existence of a ValueFactoryProvider that returns a Factory. The method that Jersey calls to obtain the Factory at runtime, is the one you mentioned: createValueFactory.

Now keep in mind that when we implement the createValueFactory, we can either return a Factory or we can return null. How we should implement it, is the check to Parameter argument to see if we can handle that parameter. For instance

protected Factory<?> createValueFactory(Parameter parameter) {
   if (parameter.getRawType() == Dummy.class
       && parameter.isAnnotationPresent(MyAnnoation.class)) {
     return new MyFactory();
   }
   return null;
}

So here we are telling Jersey what this ValueFactoryProvider can handle. In this case we can handle parameters of type Dummy and if the parameter is annotated with @MyAnnotation.

So what happens during startup validation, for each parameter, Jersey will traverse each ValueFactoryProvider registered to see if there is one that can handle that parameter. The only way it can know is if it calls the createValueFactory method. If there is one that returns a Factory, then it is a success. If all the ValueFactoryProviders are traversed and they all return null, then the model is not valid and we will get the model validation exception. It should be noted that there are a bunch of internal ValueFactoryProviders for parameter annotated with annotations like @QueryParam, @PathParam, etc.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!