Spring Core - Java Configuration
Define Spring Beans using Java code
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
@Bean
public MyRepository myRepository() {
return new MyRepositoryImpl();
}
}
@Configuration: Marks the class as a configuration source.
@Bean: Indicates that the method produces a bean managed by the Spring container.
Access Beans in the Application Context
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = context.getBean(MyService.class);
myService.performTask();
}
}
Handle multiple Configuration files
Using @Import Annotation
You can import multiple configuration classes into a primary configuration class using the @Import annotation.
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({DataSourceConfig.class, ServiceConfig.class})
public class AppConfig {
// This class serves as the main configuration file
}
Using AnnotationConfigApplicationContext
You can directly register multiple configuration classes when creating the application context.
Organizing Configurations by Profiles
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public DataSource devDataSource() {
// Configure dev DataSource
return new DataSource();
}
}
XML + Java Configuration
Importing Java into XML
<beans>
<import resource="com/example/config/AppConfig.class" />
</beans>
Importing XML into Java
@Configuration
@ImportResource("classpath:applicationContext.xml")
public class AppConfig {
}
Handle Dependencies between Beans
Constructor Injection
The dependent bean is passed as a parameter to the constructor of the primary bean.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public ServiceA serviceA() {
return new ServiceA(serviceB());
}
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}
// ServiceA depends on ServiceB
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
public void performTask() {
serviceB.assistTask();
}
}
public class ServiceB {
public void assistTask() {
System.out.println("Task Assisted!");
}
}
When Spring creates ServiceA, it automatically injects ServiceB.
Setter Injection
Dependencies are injected via setter methods.
@Configuration
public class AppConfig {
@Bean
public ServiceA serviceA() {
ServiceA serviceA = new ServiceA();
serviceA.setServiceB(serviceB());
return serviceA;
}
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}
// ServiceA with Setter
@Service
public class ServiceA {
private ServiceB serviceB;
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
public void performTask() {
serviceB.assistTask();
}
}
Field Injection (with @Autowired)
Spring can directly inject dependencies into fields using the @Autowired annotation.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB;
public void performTask() {
serviceB.assistTask();
}
}
@Component
public class ServiceB {
public void assistTask() {
System.out.println("Task Assisted!");
}
}
With component scanning enabled, Spring automatically injects ServiceB into ServiceA:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}
Using @DependsOn
If one bean must be initialized before another, use the @DependsOn annotation. This ensures ServiceB is initialized before ServiceA.
@Configuration
public class AppConfig {
@Bean
@DependsOn("serviceB")
public ServiceA serviceA() {
return new ServiceA();
}
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}
Circular Dependencies
If two beans depend on each other, Spring resolves it by using proxies or lazily initialized beans.
@Configuration
public class AppConfig {
@Bean
public BeanA beanA() {
return new BeanA(beanB());
}
@Bean
public BeanB beanB() {
return new BeanB();
}
}
If circular dependencies occur, use @Lazy to break the initialization loop:
public class BeanA {
private final BeanB beanB;
public BeanA(@Lazy BeanB beanB) {
this.beanB = beanB;
}
}
Best Practices
- Use constructor injection for mandatory dependencies (recommended for immutability).
- Use setter injection for optional dependencies.
- Avoid circular dependencies by reviewing bean design.
- Use @Lazy or @DependsOn only when necessary to manage initialization order.
Spring's IoC container ensures dependencies are managed efficiently, making it easy to handle relationships between beans.
Explain and define Bean Scopes
@Configuration
public class AppConfig {
@Bean
@Scope("singleton")
public MyBean myBean() {
return new MyBean();
}
}
Scopes
Singleton
Definition: A single instance of the bean is created and shared across the entire Spring container.
Prototype
Definition: A new instance of the bean is created every time it is requested.
Request
Definition: A new instance of the bean is created for each HTTP request and is valid only within the lifecycle of the request.
Session
Definition: A single instance of the bean is created and associated with an HTTP session. It is valid until the session is terminated.
Application
Definition: A single instance of the bean is shared across the entire application and remains valid for the entire lifecycle of the ServletContext.
WebSocket
Definition: A single instance of the bean is created and tied to the lifecycle of a WebSocket.
Choosing the Right Scope
- Use singleton for stateless, reusable components.
- Use prototype for stateful or per-user/request components.
- Use request or session for web-specific scenarios.
- Use application for global data shared across the application.
- Use websocket for WebSocket-based interactions.
