Here’s the detailed explanation of the execution flow with the actual code from a simple Java program to demonstrate BeanNameAware's setBeanName() lifecycle interface method.
Buckle up! It will be a long ride 🎢
1. Execution Starts in main()
- The program begins execution in the
main()
method. The Spring context is initialized using theAnnotationConfigApplicationContext
class withTenantConfig.class
as its configuration.
Code:
public static void main(String[] args) {
// Initialize Spring context
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TenantConfig.class);
TenantService tenantService = context.getBean(TenantService.class);
// Process tenant data
tenantService.processTenantData();
}
2. Spring Context Initialization
- The
AnnotationConfigApplicationContext
processes theTenantConfig
class annotated with@Configuration
. It scans the package"org.example4"
for components like@Service
and other Spring-managed beans.
Code:
@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();
}
}
3. Bean Creation in TenantConfig
- Spring invokes the two
@Bean
methods (tenantADataSource()
andtenantBDataSource()
) to create two beans of typeTenantDataSource
:"tenantA-dataSource"
"tenantB-dataSource"
Code for @Bean
definitions:
@Bean(name = "tenantA-dataSource")
public TenantDataSource tenantADataSource() {
return new TenantDataSource();
}
@Bean(name = "tenantB-dataSource")
public TenantDataSource tenantBDataSource() {
return new TenantDataSource();
}
4. TenantDataSource
Bean Initialization
- The
TenantDataSource
class implementsBeanNameAware
, allowing the Spring container to call thesetBeanName(String beanName)
method during bean initialization. - The
setBeanName
method extracts the tenant name (tenantA
ortenantB
) from the bean name and assigns a corresponding database URL.
Code:
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
}
}
5. TenantService
Bean Creation
- Spring scans the package
"org.example4"
and finds theTenantService
class annotated with@Service
. - The
TenantService
constructor has dependencies annotated with@Qualifier
, which specifies whichTenantDataSource
beans to inject:"tenantA-dataSource"
"tenantB-dataSource"
Code:
@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();
}
}
6. Retrieving TenantService
from Context
-
Back in the
main()
method, theTenantService
bean is retrieved from the Spring context:
TenantService tenantService = context.getBean(TenantService.class);
This bean is fully initialized, with its dependencies injected.
7. Calling processTenantData()
- The
processTenantData()
method is invoked on theTenantService
bean. - Inside the method:
- A message is printed:
"Processing data for all tenants..."
. - The
connect()
method is called on bothtenantADataSource
andtenantBDataSource
.
- A message is printed:
Code in main()
:
tenantService.processTenantData();
Code in TenantService
:
public void processTenantData() {
System.out.println("Processing data for all tenants...");
tenantADataSource.connect();
tenantBDataSource.connect();
}
8. Connecting to Tenant Databases
-
For each
TenantDataSource
bean, theconnect()
method:- Prints the
tenantName
anddatabaseUrl
:
Connecting to database for tenant: tenantA Using database URL: jdbc:mysql://localhost:3306/tenantA_db Connecting to database for tenant: tenantB Using database URL: jdbc:mysql://localhost:3306/tenantB_db
- Prints the
Code in TenantDataSource
:
public void connect() {
System.out.println("Connecting to database for tenant: " + tenantName);
System.out.println("Using database URL: " + databaseUrl);
// Simulate connection logic here
}
9. Program Completion
- After processing data for both tenants, the program completes execution.
Complete Output:
Processing data for all tenants...
Connecting to database for tenant: tenantA
Using database URL: jdbc:mysql://localhost:3306/tenantA_db
Connecting to database for tenant: tenantB
Using database URL: jdbc:mysql://localhost:3306/tenantB_db
More info at https://docs.spring.io/spring-framework/docs/6.2.2/javadoc-api/org/springframework/beans/factory/BeanFactory.html
Top comments (0)