Inversion of Control Container
Underlying Concepts
IoC shifts object creation and wiring responsibilities from application code to the container. The container relies on three main techniques: XML parsing, the factory pattern, and Java reflection. At startup, metadata (XML or annotations) is read, bean definitions are parsed, and the container instantiates objects through reflection. This decouples components and promotes testability.
Container Interfaces: BeanFactory and ApplicationContext
Spring offers two container entry points. BeanFactory is the low‑level API used internally; it creates beans lazily – only when getBean() is called. ApplicationContext extends BeanFactory with enterprise features (message resolution, event publication, etc.) and eagerly instantiates singletons at startup. In practice developers always use ApplicationContext implementations such as ClassPathXmlApplicationContext or AnnotationConfigApplicationContext.
Bean Management via XML
A bean is defined with the <bean> element. The id uniquely identifies the instance; class specifies the fully‑qualified type. By default a no‑arg constructor is used.
<bean id="customerService" class="com.example.service.CustomerService"/>
Setter‑based Dependency Injection
Properties are injected via <property>. The example below shows a Product class receiving two string values:
public class Product {
private String name;
private String manufacturer;
public void setName(String name) { this.name = name; }
public void setManufacturer(String manufacturer) { this.manufacturer = manufacturer; }
}
<bean id="product" class="com.example.model.Product">
<property name="name" value="Smartphone"/>
<property name="manufacturer" value="Acme Corp"/>
</bean>
Constructor‑based Injection
Use <constructor‑arg> when the class provides a parameterised constructor:
public class PurchaseOrder {
private String orderId;
private String region;
public PurchaseOrder(String orderId, String region) {
this.orderId = orderId;
this.region = region;
}
}
<bean id="order" class="com.example.model.PurchaseOrder">
<constructor-arg name="orderId" value="ORD-101"/>
<constructor-arg name="region" value="EMEA"/>
</bean>
p‑namespace (shortcut)
After declaring the p‑namespace, properties can be set as attributes:
<bean id="product" class="com.example.model.Product" p:name="Tablet" p:manufacturer="GlobalTech"/>
Literal Values: null and Special Characters
<property name="secondaryAddress"><null/></property>
<property name="notes">
<value><![CDATA[Special <<chars>>]]></value>
</property></chars>
Injecting a Collaborator (external bean)
Use ref to reference another bean by its id.
public class CustomerService {
private CustomerRepository repository;
public void setRepository(CustomerRepository repository) { this.repository = repository; }
public void process() { repository.save(); }
}
<bean id="customerService" class="com.example.service.CustomerService">
<property name="repository" ref="customerRepository"/>
</bean>
<bean id="customerRepository" class="com.example.dao.CustomerRepository"/>
Inner Beans
For strong one‑to‑one relationships a bean can be nested directly inside a <property>:
public class Employee {
private Department department;
public void setDepartment(Department department) { this.department = department; }
}
public class Department { private String name; … }
<bean id="employee" class="com.example.model.Employee">
<property name="department">
<bean class="com.example.model.Department">
<property name="name" value="Engineering"/>
</bean>
</property>
</bean>
Cascading Assignment
Properties of nested objects can be set directly using dot notation, provided a getter exists:
<property name="department.name" value="Research"/>
Collection Injection
Arrays, Lists, Sets, and Maps are supported. The following bean holds multiple collection properties:
public class Student {
private String[] courses;
private List<String> hobbies;
private Map<String,String> scores;
private Set<String> certifications;
// setter methods omitted for brevity
}
<bean id="student" class="com.example.model.Student">
<property name="courses">
<array>
<value>Algorithms</value>
<value>Databases</value>
</array>
</property>
<property name="hobbies">
<list>
<value>Cycling</value>
<value>Photography</value>
</list>
</property>
<property name="scores">
<map>
<entry key="Physics" value="A"/>
<entry key="Chemistry" value="B+"/>
</map>
</property>
<property name="certifications">
<set>
<value>AWS</value>
<value>OCPJP</value>
</set>
</property>
</bean>
To inject a list of object references:
<property name="enrolledCourses">
<list>
<ref bean="math101"/>
<ref bean="cs201"/>
</list>
</property>
Extracting Shared Collections with util Namespace
Declare the util namespace and define reusable collection beans:
<util:list id="defaultGenres">
<value>Comedy</value>
<value>Drama</value>
</util:list>
<bean id="movie" class="com.example.model.Movie">
<property name="genres" ref="defaultGenres"/>
</bean>
FactoryBean
When a bean’s creation logic is complex, implement FactoryBean. The container will call getObject() to obtain the actual instance, which may differ from the bean class declared in XML.
public class ReportFactory implements FactoryBean<Report> {
@Override
public Report getObject() throws Exception {
Report r = new Report();
r.setTitle("Quarterly Summary");
return r;
}
@Override
public Class<?> getObjectType() { return Report.class; }
@Override
public boolean isSingleton() { return true; }
}
<bean id="report" class="com.example.factory.ReportFactory"/>
// Usage:
Report rep = context.getBean("report", Report.class);
Bean Scopes
The scope attribute controls instance creation. singleton (default) creates one instance per container; prototype creates a new instance each time getBean() is called. Singleton beans are eager‑loaded; prototype beans are lazy.
Bean Lifecycle
Lifecycle phases (without post‑processor): construct → set properties → optional init method → ready for use → optional destroy method on container close.
public class ConnectionPool implements AutoCloseable {
public ConnectionPool() { System.out.println("Step 1: instantiation"); }
private String url;
public void setUrl(String url) { this.url = url; System.out.println("Step 2: properties set"); }
public void init() { System.out.println("Step 3: custom init"); }
public void cleanup() { System.out.println("Step 5: custom destroy"); }
}
<bean id="pool" class="com.example.util.ConnectionPool"
init-method="init" destroy-method="cleanup">
<property name="url" value="jdbc:h2:mem:test"/>
</bean>
With a BeanPostProcessor two extra steps are inserted: postProcessBeforeInitialization after property setting, and postProcessAfterInitialization after the custom init method.
Autowiring
Set autowire to byName (matches property name with bean id) or byType (matches property type with a single compatible bean).
<bean id="employee" class="com.example.model.Employee" autowire="byType"/>
External Property Files
Load .properties files using <context:property-placeholder> and reference values with ${…}. This is commonly used for datasource configuration.
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.pass}"/>
</bean>
Annotation‑Based Container Configuration
Stereotype annotations – @Component, @Service, @Repository, @Controller – all cause a class to become a Spring bean. Enable component scanning with <context:component-scan>.
@Service("billingService")
public class BillingService { … }
<context:component-scan base-package="com.example"/>
Dependency Injection Annotations:
@Autowiredwires by type. Combined with@Qualifierit narrows down by bean name.@Resource(JSR‑250) can inject by name (@Resource(name="...")) or by type.@Valueinjects simple values or property placeholders.
Full Java‑Based Configuration
Replace XML entirely with @Configuration and @ComponentScan. The container is bootstrapped via AnnotationConfigApplicationContext.
@Configuration
@ComponentScan("com.example")
public class AppConfig { }
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
BillingService service = ctx.getBean(BillingService.class);
Aspect‑Oriented Programming (AOP)
AOP separates cross‑cutting concerns (logging, security, transactions) from business logic. Spring uses dynamic proxies: JDK proxies for interfaces, CGLIB for classes without interfaces.
Key Terminology
- Join point: any point during execution (e.g., method call).
- Pointcut: expression that selects join points.
- Advice: action taken at a join point (before, after, etc.).
- Aspect: module encapsulating pointcuts and advices.
Pointcut Expressions
Using AspectJ expression syntax: execution(modifiers‑pattern? ret‑type‑pattern declaring‑type‑pattern? name‑pattern(param‑pattern) throws‑pattern?)
execution(* com.example.service.*.*(..))
Annotation‑Based Aspects with AspectJ
Enable @AspectJ support with <aop:aspectj-autoproxy/> (or @EnableAspectJAutoProxy). Define an aspect bean and mark advice methods.
@Component
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.service.OrderService.placeOrder(..))")
public void logBefore() { System.out.println("Order placement started"); }
@AfterReturning("execution(* com.example.service.OrderService.placeOrder(..))")
public void logAfterReturn() { System.out.println("Order successfully placed"); }
@After("execution(* com.example.service.OrderService.placeOrder(..))")
public void logAfter() { System.out.println("Order processing completed"); }
@AfterThrowing("execution(* com.example.service.OrderService.placeOrder(..))")
public void logException() { System.out.println("Exception occurred"); }
@Around("execution(* com.example.service.OrderService.placeOrder(..))")
public Object aroundAdvice(ProceedingJoinPoint jp) throws Throwable {
System.out.println("Around before");
Object result = jp.proceed();
System.out.println("Around after");
return result;
}
}
Reusing Pointcuts
@Pointcut("execution(* com.example.service.OrderService.*(..))")
public void orderServiceLayer() { }
@Before("orderServiceLayer()")
public void beforeAnyOrderMethod() { … }
Aspect Ordering
Use @Order(n) on the aspect class; smaller numbers have higher precedence.
XML‑Schema Based AOP
When annotations are not preferred, <aop:config> allows declarative aspect definition.
<aop:config>
<aop:pointcut id="businessMethods" expression="execution(* com.example.service.*.*(..))"/>
<aop:aspect ref="loggingAspect">
<aop:before method="logBefore" pointcut-ref="businessMethods"/>
<aop:after-returning method="logAfterReturn" pointcut-ref="businessMethods"/>
</aop:aspect>
</aop:config>
Data Access with JdbcTemplate
JdbcTemplate simplifies JDBC operations, handling resource management and exception translation. Configure a DataSource and the JdbcTemplate bean.
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/library"/>
<property name="username" value="root"/>
<property name="password" value="secret"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
DAO classes receive the template via @Autowired.
@Repository
public class LibraryItemDao {
@Autowired
private JdbcTemplate jdbc;
public void insert(LibraryItem item) {
String sql = "INSERT INTO catalog(id, title, status) VALUES (?,?,?)";
jdbc.update(sql, item.getId(), item.getTitle(), item.getStatus());
}
public void update(LibraryItem item) {
String sql = "UPDATE catalog SET title=?, status=? WHERE id=?";
jdbc.update(sql, item.getTitle(), item.getStatus(), item.getId());
}
public void delete(String id) {
String sql = "DELETE FROM catalog WHERE id=?";
jdbc.update(sql, id);
}
public int count() {
return jdbc.queryForObject("SELECT COUNT(*) FROM catalog", Integer.class);
}
public LibraryItem findById(String id) {
String sql = "SELECT * FROM catalog WHERE id=?";
return jdbc.queryForObject(sql, new BeanPropertyRowMapper<>(LibraryItem.class), id);
}
public List<LibraryItem> findAll() {
String sql = "SELECT * FROM catalog";
return jdbc.query(sql, new BeanPropertyRowMapper<>(LibraryItem.class));
}
public void batchInsert(List<Object[]> batchArgs) {
String sql = "INSERT INTO catalog(id, title, status) VALUES (?,?,?)";
jdbc.batchUpdate(sql, batchArgs);
}
}
Transaction Management
Transactions group multiple operations into an atomic unit. Spring provides both programmatic and declarative transaction management. Declarative transactions use AOP under the hood.
Annotation‑Driven Declarative Transactions
First, define a PlatformTransactionManager (e.g., DataSourceTransactionManager). Then enable annotation‑driven transactions with <tx:annotation-driven>.
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
Annotate service classes or methods with @Transactional. The following example models a bank transfer:
@Service
@Transactional
public class BankService {
@Autowired
private AccountDao accountDao;
public void transfer(String from, String to, double amount) {
accountDao.debit(from, amount);
accountDao.credit(to, amount);
}
}
@Repository
public class AccountDao {
@Autowired
private JdbcTemplate jdbc;
public void debit(String account, double amount) {
jdbc.update("UPDATE accounts SET balance = balance - ? WHERE acc_no = ?", amount, account);
}
public void credit(String account, double amount) {
jdbc.update("UPDATE accounts SET balance = balance + ? WHERE acc_no = ?", amount, account);
}
}
Transaction Settings
@Transactional accepts attributes like propagation (e.g., REQUIRED), isolation (e.g., READ_COMMITTED), timeout (seconds), readOnly (optimisation for reads), rollbackFor and noRollbackFor.
XML‑Based Declarative Transactions
Use <tx:advice> and AOP to weave transaction behaviour:
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="transfer*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceOps" expression="execution(* com.example.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOps"/>
</aop:config>
Fully Annotated Transaction Configuration
A configuration class can define everything without XML:
@Configuration
@EnableTransactionManagement
@ComponentScan("com.example")
public class TxAppConfig {
@Bean
public DruidDataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/banking");
ds.setUsername("root");
ds.setPassword("secret");
return ds;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource ds) {
JdbcTemplate t = new JdbcTemplate();
t.setDataSource(ds);
return t;
}
@Bean
public PlatformTransactionManager txManager(DataSource ds) {
return new DataSourceTransactionManager(ds);
}
}
Spring 5 Modern Features
- Logging: Spring 5 uses Log4j2 by default; configure
log4j2.xml. - @Nullable: Annotate methods, parameters, or fields to indicate they may accept or return
null, improving integration with static analysis tools. - Functional Bean Registration: Use
GenericApplicationContextto register beans with lambda expressions: ``` GenericApplicationContext ctx = new GenericApplicationContext(); ctx.refresh(); ctx.registerBean("sample", Sample.class, () -> new Sample()); Sample s = ctx.getBean(Sample.class); - JUnit 5 Integration: With
@ExtendWith(SpringExtension.class)and@ContextConfiguration, or the composite@SpringJUnitConfig, tests seamlessly inject beans. - Reactive Web: Spring WebFlux
Reactive Programming with Spring WebFlux
WebFlux is a non‑blocking, asynchronous web framework introduced in Spring 5. It runs on Netty (or Servlet 3.1+ containers) and is built on Project Reactor.
Reactor Core: Mono and Flux
Mono emits 0 or 1 item; Flux emits 0..N items. Both adhere to the Reactive Streams specification and emit item signals, error signals, and completion signals. Subscribe to trigger data flow.
Flux.just("Spring", "Reactor", "WebFlux")
.map(String::toUpperCase)
.subscribe(System.out::println);
Annotation‑Based WebFlux
Similar to Spring MVC but returns reactive types:
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users/{id}")
public Mono<User> getUser(@PathVariable int id) {
return userService.findById(id);
}
@GetMapping("/users")
public Flux<User> listUsers() {
return userService.findAll();
}
}
Functional Endpoints
Define routes with RouterFunction and handle requests with HandlerFunction:
public class UserHandler {
private final UserService service;
public UserHandler(UserService service) { this.service = service; }
public Mono<ServerResponse> getById(ServerRequest req) {
int id = Integer.parseInt(req.pathVariable("id"));
return service.findById(id)
.flatMap(user -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(user))
.switchIfEmpty(ServerResponse.notFound().build());
}
}
@Bean
public RouterFunction<ServerResponse> route(UserHandler handler) {
return RouterFunctions.route()
.GET("/users/{id}", handler::getById)
.GET("/users", req -> handler.listUsers(req))
.build();
}
WebClient
For reactive HTTP calls, use WebClient:
WebClient client = WebClient.create("http://localhost:8080");
Mono<User> user = client.get().uri("/users/1")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(User.class);
Spring 5 consolidates classic dependency injection and AOP with forward‑looking reactive patterns, making it a versatile choice for both traditional and high‑throughput applications.