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();
}
}
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();
}
-
What happens here:
- A new Spring application context (
AnnotationConfigApplicationContext) is initialized withTenantConfigas the configuration class. - Spring begins scanning for beans and components, following the configuration provided in
TenantConfig.
- A new Spring application context (
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();
}
}
-
What happens here:
-
@Configuration:- Marks
TenantConfigas a configuration class.
- Marks
-
@ComponentScan:- Spring scans the
org.example4package for classes annotated with@Component,@Service,@Repository, etc. - It detects and registers the
TenantServiceclass as a Spring-managed bean because of the@Serviceannotation.
- Spring scans the
-
@BeanDefinitions:- Spring calls the
tenantADataSource()andtenantBDataSource()methods to create two instances ofTenantDataSource. - These beans are registered in the Spring context with the names:
tenantA-dataSourcetenantB-dataSource
- Spring calls the
-
3. Spring Instantiates TenantDataSource Beans
public class TenantDataSource implements BeanNameAware {
@Override
public void setBeanName(String beanName) { ... }
}
-
What happens here:
- Spring creates two instances of
TenantDataSourceby calling the methods inTenantConfig: -
tenantADataSource()-> Creates an instance ofTenantDataSourceand registers it astenantA-dataSource. -
tenantBDataSource()-> Creates another instance ofTenantDataSourceand registers it astenantB-dataSource.
- Spring creates two instances of
-
Bean Lifecycle -
BeanNameAware:- Spring detects that
TenantDataSourceimplementsBeanNameAware. - 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"
- Spring detects that
4. Spring Creates and Configures TenantService
@Service
public class TenantService {
@Autowired
public TenantService(
@Qualifier("tenantA-dataSource") TenantDataSource tenantADataSource,
@Qualifier("tenantB-dataSource") TenantDataSource tenantBDataSource
) {
...
}
}
-
What happens here:
- Spring detects
TenantServicebecause of the@Serviceannotation and registers it as a Spring-managed bean. - Spring finds the constructor annotated with
@Autowiredand resolves the dependencies:- For the first parameter,
@Qualifier("tenantA-dataSource")ensures Spring injects the bean namedtenantA-dataSource. - For the second parameter,
@Qualifier("tenantB-dataSource")ensures Spring injects the bean namedtenantB-dataSource.
- For the first parameter,
- Spring calls the constructor of
TenantService:
new TenantService(tenantADataSource, tenantBDataSource); - Spring detects
- The
TenantServicebean is now fully initialized and managed by Spring.
5. Retrieve the Spring-Managed TenantService Bean
TenantService tenantService = context.getBean(TenantService.class);
-
What happens here:
- The
context.getBean(TenantService.class)call retrieves theTenantServicebean from the Spring context. - This bean is already fully configured by Spring, with its dependencies (
tenantADataSourceandtenantBDataSource) injected.
- The
6. Call TenantService.processTenantData()
tenantService.processTenantData();
-
What happens here:
- The
processTenantData()method is invoked on theTenantServicebean. -
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
- The
Summary of the Execution Order
-
Main Class Initialization:
- Spring context is initialized with
TenantConfig.
- Spring context is initialized with
-
Bean Registration:
- Spring scans
TenantConfig:- Registers two beans:
tenantA-dataSourceandtenantB-dataSource. - Registers
TenantServiceas a Spring-managed bean.
- Registers two beans:
- Spring scans
-
Bean Creation and Lifecycle:
- Spring creates
TenantDataSourcebeans and callssetBeanName()to configure them dynamically. - Spring creates the
TenantServicebean, injecting the correctTenantDataSourcebeans using@Qualifier.
- Spring creates
-
Retrieve
TenantServiceBean:- The
TenantServicebean is retrieved from the Spring context.
- The
-
Method Execution:
- The
processTenantData()method is called, connecting to both tenant databases and printing connection details.
- The
Key Takeaways
- Spring Context: The Spring IoC container is responsible for creating, initializing, and managing beans.
-
Dependency Injection: Spring resolves dependencies using
@Autowiredand@Qualifier. -
Bean Lifecycle: The
TenantDataSourcebeans are initialized with tenant-specific properties during thesetBeanName()phase. -
Spring-Managed Beans: The
TenantServicebean is fully managed by Spring, and all dependencies are injected automatically.
Top comments (0)