Custom Package Scanning

About

In Spring Boot, annotations like @ComponentScan, @EnableJpaRepositories, and @EntityScan are used to automatically detect and register beans, repositories, and entities during application startup.

By default, these annotations scan the packages from the location of the @SpringBootApplication class and below. In modular or layered applications, especially when using external modules or JARs, it is often necessary to explicitly specify additional packages to scan, especially when we cannot modify the base project.

Why Custom Package Scanning Is Needed

We may need custom scanning when:

  • We add a new module with additional repositories or entities.

  • The main application class (in a base JAR) does not scan our current module.

  • We want to keep modules loosely coupled and modular.

  • We have split our domain, persistence, and business logic into separate packages or modules.

If we don’t configure it correctly:

  • Spring will not detect our new @Entity classes.

  • @Repository interfaces in our module will not be instantiated.

  • Beans from our new packages will not be registered.

Common Scenarios

  • We are working on an extension or feature module that includes:

    • New @Entity classes (e.g., com.my.module.entity)

    • New @Repository interfaces (e.g., com.my.module.repository)

    • New @Service or @Component classes (e.g., com.my.module.service)

  • The main application class resides in a base library or core project that we cannot modify.

Solution: Define a Local Configuration Class

Instead of modifying the base application class, define a new configuration class in our module to declare custom scanning behavior.

Example

@Configuration
@ComponentScan(basePackages = "com.my.module")
@EntityScan(basePackages = "com.my.module.entity")
@EnableJpaRepositories(basePackages = "com.my.module.repository")
public class MyModuleScanConfig {
    // We can leave this empty. Spring will do the scanning.
}

Where to Place This Configuration

  • Place this config class in our own module.

  • It will be picked up automatically if:

    • It is in a package scanned by the base application.

    • Or we import it manually using @Import(MyModuleScanConfig.class) from any scanned configuration.

If not automatically scanned, we can register it manually:

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

Or, if even this is not possible (e.g., we're adding to an existing WAR), we can create a new @Configuration class that is picked up as part of Spring’s component scanning from a META-INF/spring.factories file in more advanced setups.

1. ComponentScan

About

@ComponentScan is a Spring annotation used to automatically discover and register beans in the Spring application context. It tells Spring which packages to scan for classes annotated with:

  • @Component

  • @Service

  • @Repository

  • @Controller

  • @RestController

  • @Configuration

These annotated classes are automatically detected and registered as Spring beans without needing to declare them manually.

Default Behavior

When we use @SpringBootApplication, it implicitly includes @ComponentScan.

By default, it scans all packages starting from the package of the class annotated with @SpringBootApplication and downward (i.e., all subpackages).

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

So if our @SpringBootApplication class is in the package com.example, all components under com.example.* will be scanned automatically.

Syntax and Usage

1. Scanning Specific Packages

@ComponentScan(basePackages = "com.my.module")

We can also provide multiple packages:

@ComponentScan(basePackages = {
    "com.my.module.service",
    "com.my.module.controller"
})

2. Scanning by Class Reference

We can also reference a class instead of using a string:

@ComponentScan(basePackageClasses = MyService.class)

This is type-safe and avoids hardcoding package names.

Example with Filters

@ComponentScan(
    basePackages = "com.my.module",
    includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyMarker.class),
    excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Internal.*")
)
  • This includes only components marked with @MyMarker.

  • It excludes components whose class names match .*Internal.*.

Attributes

Attribute
Description

basePackages

One or more package names to scan.

basePackageClasses

One or more classes; Spring scans the packages of those classes.

includeFilters

Filters for including specific component types (with @Filter).

excludeFilters

Filters for excluding specific types or patterns.

lazyInit

If true, beans are created lazily instead of at startup.

nameGenerator

Custom bean name generator.

useDefaultFilters

If false, disables default @Component, etc. scanning.

scopedProxy

Scope proxy options (used for advanced scoping like session/request).

Common Issues and Fixes

Problem
Cause
Solution

My service/controller is not getting injected

Not in a scanned package

Add the package to @ComponentScan

Unwanted beans getting created

Default scanning includes everything

Use excludeFilters or disable useDefaultFilters

Slow startup or memory issues in large apps

Scanning too many unnecessary packages

Limit basePackages to only what's needed

Duplicated beans with same name

Scanning same package in multiple modules

Check overlapping scan configurations

2. EntityScan

About

@EntityScan is a Spring Boot-specific annotation used to explicitly specify the packages to scan for JPA entities (@Entity, @Embeddable, @MappedSuperclass).

Spring Boot, by default, only scans entity classes in the package (and subpackages) where the @SpringBootApplication class resides. If our entity classes are located outside of that package, we need @EntityScan.

Why @EntityScan is Needed ?

By default:

@SpringBootApplication // includes @ComponentScan, @EnableAutoConfiguration
public class MainApp {}

Only scans entities under com.example if this class is in com.example.

If our entities are in:

package com.external.module.entity;

They will not be discovered automatically. We will encounter runtime errors such as:

  • Not a managed type

  • Unable to locate entity

To fix this, use:

@EntityScan(basePackages = "com.external.module.entity")

Where to Place @EntityScan

  • On our main application class:

    @SpringBootApplication
    @EntityScan(basePackages = "com.my.module.entity")
    public class MyApplication {}
  • Or in a separate @Configuration class:

    @Configuration
    @EntityScan(basePackages = "com.my.module.entity")
    public class EntityScanConfig {}

    And register using:

    @SpringBootApplication
    @Import(EntityScanConfig.class)
    public class MyApp {}

Syntax

Single Package

@EntityScan(basePackages = "com.example.entity")

Multiple Packages

@EntityScan(basePackages = {
    "com.example.entity",
    "com.shared.common.entity"
})

Type-Safe with Class Reference

@EntityScan(basePackageClasses = MyEntity.class)

This is safer during refactoring as it avoids hardcoded package strings.

Attributes

Attribute
Description

basePackages

Array of package names to scan for entities.

basePackageClasses

Type-safe way to scan the package of the given class.

3. EnableJpaRepositories

About

@EnableJpaRepositories is a Spring Data annotation used to enable scanning and registration of Spring Data JPA repository interfaces, such as those extending:

  • JpaRepository

  • CrudRepository

  • PagingAndSortingRepository

It is responsible for detecting our repository interfaces, generating proxy implementations, and integrating them with the JPA entity manager.

Why is it Needed ?

Spring Boot, by default, scans repositories in the same package or subpackages of our main application class (@SpringBootApplication). If our repository interfaces are located outside of this default scope, we must explicitly specify the package using @EnableJpaRepositories.

Default Behavior

If we don’t specify @EnableJpaRepositories, Spring Boot still scans for repositories, but only under the default package (where our main class resides).

If we need to include repositories from another module, library, or unrelated package, we must declare:

@EnableJpaRepositories(basePackages = "com.external.module.repository")

Example

@EnableJpaRepositories(basePackages = "com.my.module.repository")

Type-Safe Alternative

@EnableJpaRepositories(basePackageClasses = MyRepository.class)

With Additional Configuration

@EnableJpaRepositories(
    basePackages = "com.my.module.repository",
    entityManagerFactoryRef = "myEntityManagerFactory",
    transactionManagerRef = "myTransactionManager"
)

Attributes

Attribute
Description

basePackages

Array of package names to scan for repository interfaces.

basePackageClasses

Type-safe alternative to basePackages, scans package of the given class.

includeFilters

Used to include specific types.

excludeFilters

Used to exclude certain classes from being registered as repositories.

repositoryFactoryBeanClass

Custom factory bean to create repositories.

entityManagerFactoryRef

Bean name of the EntityManagerFactory to associate with these repositories.

transactionManagerRef

Bean name of the PlatformTransactionManager for this repository group.

considerNestedRepositories

Whether nested interfaces should be considered. Default is false.

Does @ComponentScan register JPA repository interfaces

No

For example:

public interface UserRepository extends JpaRepository<User, Long> {
    // no implementation provided
}

Even though Spring dynamically creates a proxy class and marks it as a @Repository, this behavior is triggered by:

@EnableJpaRepositories

Because @ComponentScan works with actual class files annotated with @Component, @Repository, etc. It does not dynamically generate or proxy anything.

But @EnableJpaRepositories activates Spring Data’s infrastructure, which:

  • Scans for repository interfaces

  • Dynamically generates proxy beans

  • Registers them in the application context

4. Import

About

@Import is a Spring annotation used to manually import one or more @Configuration classes (or component classes) into the Spring application context.

It gives us a way to programmatically include configurations or beans that are not automatically picked up by component scanning.

Why and When to Use ?

We use @Import when:

  • We have configuration classes in external modules or libraries.

  • We want to load Java-based configuration from other packages or jars.

  • We do not want to rely on @ComponentScan to find those classes.

  • We want explicit, modular control over what configuration gets loaded.

  • We are registering beans dynamically or combining multiple configuration sources.

Example

Importing a Single Configuration Class

@Configuration
public class MyConfig {
    @Bean
    public MyService myService() {
        return new MyService();
    }
}

Then in our main class:

@SpringBootApplication
@Import(MyConfig.class)
public class MyApp {}

This tells Spring Boot to include MyConfig during context initialization even if it is not in a scanned package.

Importing Multiple Classes

@Import({MyConfig.class, AnotherConfig.class, SecurityConfig.class})

Best Practices

  • Prefer @Import for fine-grained and modular inclusion of configurations.

  • Use @Import instead of @ComponentScan for external libraries or JARs where scanning may not be desirable.

  • Avoid excessive or scattered usage of @Import; centralize imports logically.

Last updated

Was this helpful?