Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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);
    }          
}
like image 656
Jason Avatar asked Feb 22 '26 09:02

Jason


2 Answers

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.

like image 173
SJuan76 Avatar answered Feb 23 '26 22:02

SJuan76


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.

like image 25
Sotirios Delimanolis Avatar answered Feb 23 '26 21:02

Sotirios Delimanolis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!