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 withTenantConfig
as 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
TenantConfig
as a configuration class.
- Marks
-
@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.
- Spring scans the
-
@Bean
Definitions:- Spring calls the
tenantADataSource()
andtenantBDataSource()
methods to create two instances ofTenantDataSource
. - These beans are registered in the Spring context with the names:
tenantA-dataSource
tenantB-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
TenantDataSource
by calling the methods inTenantConfig
: -
tenantADataSource()
-> Creates an instance ofTenantDataSource
and registers it astenantA-dataSource
. -
tenantBDataSource()
-> Creates another instance ofTenantDataSource
and registers it astenantB-dataSource
.
- Spring creates two instances of
-
Bean Lifecycle -
BeanNameAware
:- Spring detects that
TenantDataSource
implementsBeanNameAware
. - 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
TenantService
because of the@Service
annotation and registers it as a Spring-managed bean. - Spring finds the constructor annotated with
@Autowired
and 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
TenantService
bean 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 theTenantService
bean from the Spring context. - This bean is already fully configured by Spring, with its dependencies (
tenantADataSource
andtenantBDataSource
) injected.
- The
6. Call TenantService.processTenantData()
tenantService.processTenantData();
-
What happens here:
- The
processTenantData()
method is invoked on theTenantService
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
- 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-dataSource
andtenantB-dataSource
. - Registers
TenantService
as a Spring-managed bean.
- Registers two beans:
- Spring scans
-
Bean Creation and Lifecycle:
- Spring creates
TenantDataSource
beans and callssetBeanName()
to configure them dynamically. - Spring creates the
TenantService
bean, injecting the correctTenantDataSource
beans using@Qualifier
.
- Spring creates
-
Retrieve
TenantService
Bean:- The
TenantService
bean 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
@Autowired
and@Qualifier
. -
Bean Lifecycle: The
TenantDataSource
beans are initialized with tenant-specific properties during thesetBeanName()
phase. -
Spring-Managed Beans: The
TenantService
bean is fully managed by Spring, and all dependencies are injected automatically.
Top comments (0)