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 theAnnotationConfigApplicationContextclass withTenantConfig.classas 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
AnnotationConfigApplicationContextprocesses theTenantConfigclass annotated with@Configuration. It scans the package"org.example4"for components like@Serviceand 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
@Beanmethods (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
TenantDataSourceclass implementsBeanNameAware, allowing the Spring container to call thesetBeanName(String beanName)method during bean initialization. - The
setBeanNamemethod extracts the tenant name (tenantAortenantB) 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 theTenantServiceclass annotated with@Service. - The
TenantServiceconstructor has dependencies annotated with@Qualifier, which specifies whichTenantDataSourcebeans 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, theTenantServicebean 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 theTenantServicebean. - Inside the method:
- A message is printed:
"Processing data for all tenants...". - The
connect()method is called on bothtenantADataSourceandtenantBDataSource.
- 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
TenantDataSourcebean, theconnect()method:- Prints the
tenantNameanddatabaseUrl:
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)