3

I've wondered this for a while, but haven't been able to find anything on the topic.

Let's say I'm using Spring MVC and defining a series of Controllers.

@RestController public abstract class AbstractController { @Resource private Environment env; ... ... } 

For the following, is a @RestController annotation necessary for UserController to register?

@RequestMapping(value = "/user") public class UserController extends AbstractController { @RequestMapping("value = "", method = RequestMethod.GET) public ResponseEntity<User> getUser() { ... return new ResponseEntity<>(user, HttpStatus.OK); } } 

2 Answers 2

6

If you decide the annotation to be @Inherited, yes. In this example, @RestController is not defined with the @Inherited meta-annotation, so it is not applied to subclasess of AbstractController.

Sign up to request clarification or add additional context in comments.

2 Comments

Suggested enhancement to answer: Specifying @RestController (or @Controller or @Component) will likely cause an exception, because Spring will try to instantiate the abstract class.
@Andreas You have to give Spring some credit. It doesn't do such things.
3

There are two questions here. SJuan76 has answered the first, concerning inheritance of type annotations.

The second question

is a @RestController annotation necessary for UserController to register?

is a little more involved.

Spring has a concept of meta-annotations:

Many of the annotations provided by Spring can be used as meta-annotations in your own code. A meta-annotation is simply an annotation that can be applied to another annotation.

[...]

Meta-annotations can also be combined to create composed annotations. For example, the @RestController annotation from Spring MVC is composed of @Controller and @ResponseBody.

However, the component scanning process will not generate a bean definition for abstract classes. What's more, it won't discover these meta-annotations on subtypes that aren't themselves annotated. In other words, Spring will skip over your UserController type and won't register a bean definition for it. (I'm sure it could, it just doesn't as of 4.1.7.RELEASE.)

Now, if you did annotate your type UserController with @Component to force Spring to generate a bean definition, the Spring MVC process that handles @ResponseBody would detect the @RestController annotations on the AbstractController supertype.

This is done in RequestResponseBodyMethodProcessor which uses AnnotationUtils.findAnnotation to find an annotation on the type that contains your handler method. Its javadoc states

Find a single Annotation of annotationType on the supplied Class, traversing its interfaces, annotations, and superclasses if the annotation is not present on the given class itself.

This method explicitly handles class-level annotations which are not declared as inherited as well as meta-annotations and annotations on interfaces.

Therefore, if you had

@Component @RequestMapping(value = "/user") public class UserController extends AbstractController { 

Spring would detect and generate a bean for UserController. When handling a request to /user with your getUser handler method, Spring MVC would operate as if your class was actually annotated with @RestController (or @ResponseBody).

However, again in your case, your handler method is returning a ResponseEntity. That's not handled by the HandlerMethodReturnValueHandler for @ResponseBody, it's handled by HttpEntityMethodProcessor.


A word of caution with abstract classes meant to be supertypes of @Controller types: if you have many subtypes also meant to be beans and the abstract supertype contains @RequestMapping annotated methods, Spring MVC will fail to register your handler methods because it will consider that supertype method to be duplicated across them.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.