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 Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay