DEV Community

Hunor Vadasz-Perhat
Hunor Vadasz-Perhat

Posted on

spring-006: logical-execution-order-and-code-flow

public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(TenantConfig.class);
    TenantService tenantService = context.getBean(TenantService.class);
    tenantService.processTenantData();
}

@Configuration
@ComponentScan(basePackages = "org.example4") // Adjust to your package structure
public class TenantConfig {

    @Bean(name = "tenantA-dataSource")
    public TenantDataSource tenantADataSource() {
        return new TenantDataSource();
    }

    @Bean(name = "tenantB-dataSource")
    public TenantDataSource tenantBDataSource() {
        return new TenantDataSource();
    }
}

public class TenantDataSource implements BeanNameAware {

    private String tenantName;
    private String databaseUrl;

    @Override
    public void setBeanName(String beanName) {
        // Extract tenant name from the bean name
        if (beanName.contains("-")) {
            this.tenantName = beanName.split("-")[0];
        } else {
            throw new IllegalArgumentException("Invalid bean naming convention. Expected format: <tenantName>-dataSource");
        }

        // Assign a database URL dynamically based on the tenant name
        this.databaseUrl = "jdbc:mysql://localhost:3306/" + tenantName + "_db";
    }

    public void connect() {
        System.out.println("Connecting to database for tenant: " + tenantName);
        System.out.println("Using database URL: " + databaseUrl);
        // Simulate connection logic here
    }
}

@Service
public class TenantService {

    private final TenantDataSource tenantADataSource;
    private final TenantDataSource tenantBDataSource;

    @Autowired
    public TenantService(
            @Qualifier("tenantA-dataSource") TenantDataSource tenantADataSource,
            @Qualifier("tenantB-dataSource") TenantDataSource tenantBDataSource
    ) {
        this.tenantADataSource = tenantADataSource;
        this.tenantBDataSource = tenantBDataSource;
    }

    public void processTenantData() {
        System.out.println("Processing data for all tenants...");
        tenantADataSource.connect();
        tenantBDataSource.connect();
    }
}
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Execution Order

1. Main Class Execution Begins

public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(TenantConfig.class);
    TenantService tenantService = context.getBean(TenantService.class);
    tenantService.processTenantData();
}
Enter fullscreen mode Exit fullscreen mode
  • What happens here:
    • A new Spring application context (AnnotationConfigApplicationContext) is initialized with TenantConfig as the configuration class.
    • Spring begins scanning for beans and components, following the configuration provided in TenantConfig.

2. Spring Processes TenantConfig

@Configuration
@ComponentScan(basePackages = "org.example4")
public class TenantConfig {
    @Bean(name = "tenantA-dataSource")
    public TenantDataSource tenantADataSource() {
        return new TenantDataSource();
    }

    @Bean(name = "tenantB-dataSource")
    public TenantDataSource tenantBDataSource() {
        return new TenantDataSource();
    }
}
Enter fullscreen mode Exit fullscreen mode
  • What happens here:
    1. @Configuration:
      • Marks TenantConfig as a configuration class.
    2. @ComponentScan:
      • Spring scans the org.example4 package for classes annotated with @Component, @Service, @Repository, etc.
      • It detects and registers the TenantService class as a Spring-managed bean because of the @Service annotation.
    3. @Bean Definitions:
      • Spring calls the tenantADataSource() and tenantBDataSource() methods to create two instances of TenantDataSource.
      • These beans are registered in the Spring context with the names:
      • tenantA-dataSource
      • tenantB-dataSource

3. Spring Instantiates TenantDataSource Beans

public class TenantDataSource implements BeanNameAware {
    @Override
    public void setBeanName(String beanName) { ... }
}
Enter fullscreen mode Exit fullscreen mode
  • What happens here:

    • Spring creates two instances of TenantDataSource by calling the methods in TenantConfig:
    • tenantADataSource() -> Creates an instance of TenantDataSource and registers it as tenantA-dataSource.
    • tenantBDataSource() -> Creates another instance of TenantDataSource and registers it as tenantB-dataSource.
  • Bean Lifecycle - BeanNameAware:

    • Spring detects that TenantDataSource implements BeanNameAware.
    • For each bean:
    • setBeanName("tenantA-dataSource") is called on the first instance.
      • tenantName = "tenantA"
      • databaseUrl = "jdbc:mysql://localhost:3306/tenantA_db"
    • setBeanName("tenantB-dataSource") is called on the second instance.
      • tenantName = "tenantB"
      • databaseUrl = "jdbc:mysql://localhost:3306/tenantB_db"

4. Spring Creates and Configures TenantService

@Service
public class TenantService {
    @Autowired
    public TenantService(
        @Qualifier("tenantA-dataSource") TenantDataSource tenantADataSource,
        @Qualifier("tenantB-dataSource") TenantDataSource tenantBDataSource
    ) {
        ...
    }
}
Enter fullscreen mode Exit fullscreen mode
  • What happens here:

    1. Spring detects TenantService because of the @Service annotation and registers it as a Spring-managed bean.
    2. Spring finds the constructor annotated with @Autowired and resolves the dependencies:
      • For the first parameter, @Qualifier("tenantA-dataSource") ensures Spring injects the bean named tenantA-dataSource.
      • For the second parameter, @Qualifier("tenantB-dataSource") ensures Spring injects the bean named tenantB-dataSource.
    3. Spring calls the constructor of TenantService:
     new TenantService(tenantADataSource, tenantBDataSource);
    
  1. The TenantService bean is now fully initialized and managed by Spring.

5. Retrieve the Spring-Managed TenantService Bean

TenantService tenantService = context.getBean(TenantService.class);
Enter fullscreen mode Exit fullscreen mode
  • What happens here:
    • The context.getBean(TenantService.class) call retrieves the TenantService bean from the Spring context.
    • This bean is already fully configured by Spring, with its dependencies (tenantADataSource and tenantBDataSource) injected.

6. Call TenantService.processTenantData()

tenantService.processTenantData();
Enter fullscreen mode Exit fullscreen mode
  • What happens here:

    • The processTenantData() method is invoked on the TenantService bean.
    • Inside the method:
      tenantADataSource.connect() is called:

      • Outputs:
       Connecting to database for tenant: tenantA
       Using database URL: jdbc:mysql://localhost:3306/tenantA_db
      

    tenantBDataSource.connect() is called:

    • Outputs:

       Connecting to database for tenant: tenantB
       Using database URL: jdbc:mysql://localhost:3306/tenantB_db
      

Summary of the Execution Order

  1. Main Class Initialization:

    • Spring context is initialized with TenantConfig.
  2. Bean Registration:

    • Spring scans TenantConfig:
      • Registers two beans: tenantA-dataSource and tenantB-dataSource.
      • Registers TenantService as a Spring-managed bean.
  3. Bean Creation and Lifecycle:

    • Spring creates TenantDataSource beans and calls setBeanName() to configure them dynamically.
    • Spring creates the TenantService bean, injecting the correct TenantDataSource beans using @Qualifier.
  4. Retrieve TenantService Bean:

    • The TenantService bean is retrieved from the Spring context.
  5. Method Execution:

    • The processTenantData() method is called, connecting to both tenant databases and printing connection details.

Key Takeaways

  • Spring Context: The Spring IoC container is responsible for creating, initializing, and managing beans.
  • Dependency Injection: Spring resolves dependencies using @Autowired and @Qualifier.
  • Bean Lifecycle: The TenantDataSource beans are initialized with tenant-specific properties during the setBeanName() phase.
  • Spring-Managed Beans: The TenantService bean is fully managed by Spring, and all dependencies are injected automatically.

AWS Q Developer image

Your AI Code Assistant

Generate and update README files, create data-flow diagrams, and keep your project fully documented. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (0)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

👥 Ideal for solo developers, teams, and cross-company projects

Learn more