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.

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay