February 12, 2022

Spring @ComponentScan Annotation

Spring framework can automatically detect the classes annotated with @Component and other stereotype annotations like @Repository, @Service, @Controller. You need some way to tell Spring where to look for classes annotated with one of these annotations. For that you can use Spring @ComponentScan annotation, you can also specify the base package to scan. Spring will scan the base package as well as all the sub-packages for the components and register them as beans.

@ComponentScan is used along with @Configuration annotation to specify the packages to scan. Since bean are automatically registered so there is no need for having methods marked with @Bean annotation with in the configuration class. With @ComponentScan annotation for automatically registering the bean and @Autowired annotation for automatically injecting the dependencies there is no need for explicit configuration.

@ComponentScan annotation is counterpart of <context:component-scan> element used in Spring XML configuration.

@ComponentScan with no specific packages

If specific packages are not defined along with @ComponentScan, scanning will occur from the package of the class that declares this annotation.

For example if you have your Service classes with in the package com.knpcode.springexample.service and DAO classes in com.knpcode.springexample.dao package and AppConfig class in com.knpcode.springexample package then the sub packages will be scanned even if you don’t specify any base package with @ComponentScan.

Spring @ComponentScan example

UserService interface
import java.util.List;
import com.knpcode.springexample.dto.User;

public interface UserService {
  public List<User> getUsers();
}
UserServiceImpl class
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.knpcode.springexample.dao.UserDAO;
import com.knpcode.springexample.dto.User;

@Service("userService")
public class UserServiceImpl implements UserService{
  @Autowired
  UserDAO userDAO;

  public List<User> getUsers() {
    return userDAO.getUsers();
  }
}
UserDAO interface
import java.util.List;
import com.knpcode.springexample.dto.User;

public interface UserDAO {
  public List<User> getUsers();
}
UserDAOImpl class
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.knpcode.springexample.dto.User;

@Repository
public class UserDAOImpl implements UserDAO {
  public List<User> getUsers() {
    System.out.println("In getUsers method, connect to DB and get data");
    List<User> userList = new ArrayList<User>();
    // Creating User instance locally
    User user = new User();
    user.setFirstName("John");
    user.setLastName("Wick");
    user.setAge(35);
    userList.add(user);
    return userList;
  }
}
DTO Class (User.java)
public class User {
  private String firstName;
  private String lastName;
  private int age;
  public String getFirstName() {
    return firstName;
  }
  public String getLastName() {
    return lastName;
  }
  public int getAge() {
    return age;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public void setAge(int age) {
    this.age = age;
  }	
}
Configuration class
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class AppConfig {

}

You can see that no base package is specified with the @ComponentScan so scanning will start from the package where AppConfig class resides, all the sub-packages will also be scanned. To run this example use the following class-

public class App {
  public static void main(String[] args) {
    AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

    UserService userService = context.getBean("userService", UserService.class);
    List<User> users = userService.getUsers();
    for(User user : users) {
      System.out.println("First Name- " + user.getFirstName());
      System.out.println("Last Name- " + user.getLastName());
      System.out.println("Age- " + user.getAge());
    }
    context.close();
  }
}
Output
In getUsers method, connect to DB and get data
First Name- John
Last Name- Wick
Age- 35

Specifying argument with @ComponentScan

You can specify base packages with basePackages() (or its alias value())

You can also use basePackageClasses() to specify classes. The package of each class specified will be scanned.

1. Specifying multiple packages with @ComponentScan

@Configuration
@ComponentScan(basePackages = {"com.knpcode.springexample", 
			       "com.knpcode.springexample.dao",
			       "com.knpcode.springexample.service"})
public class AppConfig {

}

2. Though in the scenario as shown above specifying just the parent package suffices, sub-packages will also be scanned recursively.

@Configuration
@ComponentScan(basePackages = "com.knpcode.springexample")
public class AppConfig {

}

3. If no other attributes are needed, you don’t even need to specify "basePackages" explicitly.

@Configuration
@ComponentScan("com.knpcode.springexample")
public class AppConfig {

}

4. You can also specify classes with basePackageClasses(), package that class belongs to is scanned.

@Configuration
@ComponentScan(basePackages = {"com.knpcode.springexample", 
                  "com.knpcode.springexample.dao"}, 
                  basePackageClasses = UserDAO.class)
public class AppConfig {

}

@ComponentScan with filters

You can also specify exclude filters and include filters with @ComponentScan.

Using excludeFilters you can specify which types are not eligible for component scanning.

Using includeFilters you can specify which types are eligible for component scanning.

There are five filter types available for @ComponentScan.Filter. It is defined as an Enum org.springframework.context.annotation.FilterType

  • ANNOTATION- Filter candidates marked with a given annotation.
  • ASPECTJ- Filter candidates matching a given AspectJ type pattern expression.
  • ASSIGNABLE_TYPE- Filter candidates assignable to a given type.
  • CUSTOM- Filter candidates using a given custom TypeFilter implementation.
  • REGEX- Filter candidates matching a given regex pattern.

Here is an example of excluding filters with filter type as REGEX.

@Configuration
@ComponentScan(basePackages = {"com.knpcode.springexample", 
              "com.knpcode.springexample.dao"}, 
              basePackageClasses = UserDAO.class, 
              excludeFilters = @ComponentScan.Filter(type=FilterType.REGEX,
              pattern="com.knpcode.springexample.dto..*"))
public class AppConfig {

}

Another example with FilterType.ASSIGNABLE_TYPE

@Configuration
@ComponentScan(excludeFilters = @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,
			   classes=UserService.class))
public class AppConfig {

}

This will exclude any class implementing UserService interface.

That's all for the topic Spring @ComponentScan Annotation. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment