Regularly I find myself in a situation in which I'd like to use a class name in an annotation. Most recently I ran into that situation in connection with the @RequestAttribute annotation.
Often when I need to write a request filter I end up with a code similar to the following:
package org.exmaple;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class ExampleFilter implements Filter {
public static final String ATTRIBUTE_NAME =
ExampleFilter.class.getName() + '.' + "EXAMPLE_ATTRIBUTE";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String someValue;
...
request.setAttribute(ATTRIBUTE_NAME, someValue);
chain.doFilter(request, response);
}
}
Note that the value of ATTRIBUTE_NAME is a constant string with the class name prepended to it to avoid attribute name conflicts. This practice is not uncommon for request attribute names.
Now, when I want to use the attribute set by the filter in a handler, I'd like to be able to do so as follows:
package org.exmaple;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/example")
public class ExportController {
@GetMapping("/test")
public ResponseEntity<Object> test(@RequestAttribute(ExampleFilter.ATTRIBUTE_NAME) String attributeValue, ...) {
Object responseData;
...
return new ResponseEntity<>(responseData, HttpStatus.OK);
}
}
But java doesn't let me do that, complaining that: Attribute value must be constant.
This is unfortunate as it forces me to hardcode the class name in the attribute name:
public static final String ATTRIBUTE_NAME = "org.exmaple.ExampleFilter.EXAMPLE_ATTRIBUTE";
While it is not possible to use class names in annotations it is possible to use class constants. Therefore, to improve the current situation, I propose adding a new field to the @RequestAttribute for specifying the class "owning" the attribute. If specified, the name of the class would be prepended to the value used currenlty (either specified or inferred). If left unspecified, nothing would be prepended, so the proposed change is backwards compatible.
Here is a tentative implementation:
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestAttribute {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the request attribute to bind to.
* <p>The default name is inferred from the method parameter name.
* If {@link #owningClass} is specified, its name is prepended to the value of this field.
*/
@AliasFor("value")
String name() default "";
/**
* The class owning the attribute. If specified, its name is prepended to {@link #name}
* (both explicitly specified or inferred).
*/
Class<?> owningClass() default None.class;
/**
* Whether the request attribute is required.
* <p>Defaults to {@code true}, leading to an exception being thrown if
* the attribute is missing. Switch this to {@code false} if you prefer
* a {@code null} or Java 8 {@code java.util.Optional} if the attribute
* doesn't exist.
*/
boolean required() default true;
class None {
}
}
Comment From: rstoyanchev
Thanks for the suggestion. It is an interesting idea, and a clever solution to a problem once you've built the mental model for it.
Looking at such an annotation in reverse however without any prior knowledge I can't say that @RequestAttribute(owningClass=ExampleFilter.class) is very intuitive. It's not clear what an owning class is, and you'd have to read about it.
Is the alternative of just creating a unique attribute so bad after all? A very fully qualified classname is not required to make an attribute unique. Some simplification can be made, given the limitation, without compromising uniqueness.
Comment From: mruzicka
What's intuitive about annotations in general? With their use and the implementation of the respective functionality so disconnected. Fortunately with modern IDEs and proper documentation one is never farther than a click away from obtaining the required information.
Would @RequestAttribute itself look intuitive to someone who hasn't been exposed to the javax.servlet API before?
My point is: Yes, you'll need to read up on that when you first see it, but that is the case with everything else too. The concept is hopefully not too obscure and on top of that we could perhaps come up with a more self explanatory name.
As to the point of using other strategies for making the names unique: Yes, there are alternatives but using the class name as the attribute name prefix has its advantages: It's a direct pointer to the class where one can learn more about the purpose of the attribute, it feels like a reasonably safe way to prevent the mentioned name conflicts and it's readily available obviating the need to invent another reasonably unique prefix whenever the need arises. So unsurprisingly IMHO the adoption of this strategy is more of a norm than an edge case - for example out of 16 attributes Spring Web sets on requests in my setup 12 use this strategy [1] and two more are not far away [2].
With all that said having a dedicated support for this strategy would seem IMHO appropriate.
[1] https://github.com/spring-projects/spring-framework/blob/v5.2.4.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java#L212-L262, https://github.com/spring-projects/spring-framework/blob/v5.2.4.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java#L58-L130
[2] https://github.com/spring-projects/spring-framework/blob/v5.2.4.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProviderExposingInterceptor.java#L38, https://github.com/spring-projects/spring-framework/blob/v5.2.4.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/ConversionServiceExposingInterceptor.java#L58
Comment From: rstoyanchev
If you feel strongly about a convention based approach, it's quite straight forward to create your own custom argument resolver. You could probably make it even shorter (e.g. @Attribute(class=..., name=...) or the alternative is quite long. Follow the pattern of how RequestAttributeMethodArgumentResolver is implemented.
Comment From: mruzicka
Thanks for the suggestion, it did cross my mind but I turned it down as going down that road I'd have another annotation which would essentially be equivalent to the @RequestAttribute save for the proposed functionality. Hence this proposal. :-)