This is an issue when a framework annotation or metadata marker is used alongside another annotation that already includes it through annotation inheritance or composition mechanisms.

Why is this an issue?

Some frameworks use composed annotations or attribute inheritance to build functionality. A composed annotation is one that is declared to include another annotation’s behavior. When you use an annotation that composes other annotations, you automatically get all the functionality of those included annotations.

For example, a framework might provide a combined controller annotation that is composed from a basic controller marker and a response formatting marker. This means when you add the combined annotation to a class, the framework automatically treats it as if it also has both the basic controller marker and the response formatting marker.

When developers explicitly add both annotations, the parent annotation becomes redundant. It has no effect on how the framework processes the class, but it creates several problems:

The most common cases involve framework stereotype annotations. A base component marker is often the foundation, with the framework providing specialized versions like service markers, data access markers, controller markers, and configuration markers. Each of these specialized annotations is composed from the base component marker, so adding the base marker explicitly is redundant.

Similar redundancies occur with combined controller annotations (which include both basic controller and response formatting markers), application bootstrap annotations (which include configuration, auto-configuration, and component scanning markers), and various framework test annotations.

In Spring Framework, this specifically applies to meta-annotations. Common examples include:

What is the potential impact?

This impacts:

How to fix it in Spring

Remove the redundant parent annotation. Keep only the most specific annotation that provides the functionality you need.

For stereotype annotations (@Service, @Repository, @Controller, @Configuration), remove @Component since it’s already included in these specialized annotations.

Code examples

Noncompliant code example

@Component  // Noncompliant
@Service
public class UserService {
    public User findById(Long id) {
        // service logic
    }
}

Compliant solution

@Service
public class UserService {
    public User findById(Long id) {
        // service logic
    }
}

For REST controllers, remove @Controller and class-level @ResponseBody since @RestController already includes both. Also remove method-level @ResponseBody within @RestController classes since it’s already applied to all methods.

Noncompliant code example

@Controller  // Noncompliant
@RestController
public class UserController {

    @ResponseBody  // Noncompliant
    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.findAll();
    }
}

Compliant solution

@RestController
public class UserController {

    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.findAll();
    }
}

How to fix it in Spring Boot

For Spring Boot applications, remove @Configuration, @SpringBootConfiguration, @EnableAutoConfiguration, and @ComponentScan (when used without custom attributes) since @SpringBootApplication already includes all of these.

Code examples

Noncompliant code example

@Configuration  // Noncompliant
@EnableAutoConfiguration  // Noncompliant
@ComponentScan  // Noncompliant
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

Compliant solution

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

How to fix it in Spring Test

For Spring test classes, remove @ExtendWith(SpringExtension.class) when using specialized test annotations like @SpringBootTest, @WebMvcTest, @DataJpaTest, or @WebFluxTest since they already include this extension. Also remove @Transactional when using @DataJpaTest since it’s already included.

Code examples

Noncompliant code example

@ExtendWith(SpringExtension.class)  // Noncompliant
@SpringBootTest
class UserServiceIntegrationTest {

    @Test
    void testUserCreation() {
        // test logic
    }
}

Compliant solution

@SpringBootTest
class UserServiceIntegrationTest {

    @Test
    void testUserCreation() {
        // test logic
    }
}

Resources

Documentation