Jason
Jason

Reputation: 11363

Are annotations applicable to subclasses?

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

Upvotes: 3

Views: 2132

Answers (2)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279970

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.

Upvotes: 3

SJuan76
SJuan76

Reputation: 24885

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.

Upvotes: 6

Related Questions