IoC Container
Inversion of Control
The IoC container serves as the practical implementation of the IoC design pattern in Spring. Components managed by the IoC container are referred to as beans. Before creating a bean, the IoC container must be instantiated first. Spring provides two primary implementations:
① BeanFactory
The fundamental implementation of the IoC container, serving as a internal Spring interface. Designed for Spring's internal use, not intended for application developers.
② ApplicationContext
A sub-interface of BeanFactory offering advanced features. Geared toward Spring users, it is the preferred choice in virtually all scenarios rather than the underlying BeanFactory.
③ Key ApplicationContext Implementations
Retrieving Beans: Three Approcahes
package com.example.spring6.iocxml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("bean.xml");
// Method 1: retrieve by bean ID
User user1 = (User) context.getBean("user1");
System.out.println("" + user1);
// Method 2: retrieve by type
User user2 = context.getBean(User.class);
System.out.println("" + user2);
// Method 3: retrieve by both ID and type
User user3 = context.getBean("user", User.class);
System.out.println("" + user3);
}
}
Can a bean be retrieved by its interface type?
Yes, provided the bean is unique.
If an interface has multiple implementations and all are configured as beans, can you retrieve by interface?
No, because the bean is not unique.
Dependency Injection
When a class has properties, the values must be set during object creation.
Approach One: Setter Injection
package com.example.spring6.iocxml.di;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBook {
@Test
public void testSetter() {
ApplicationContext context = new
ClassPathXmlApplicationContext("bean-di.xml");
Book book = context.getBean("book", Book.class);
System.out.println(book);
}
}
Book.java
package com.example.spring6.iocxml.di;
public class Book {
private String bname;
private String author;
@Override
public String toString() {
return "Book{" +
"bname='" + bname + '\'' +
", author='" + author + '\'' +
'}';
}
public String getBname() {
return bname;
}
public void setBname(String bname) {
this.bname = bname;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Book(String bname, String author) {
this.bname = bname;
this.author = author;
}
public Book() {
}
}
bean-di.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="book" class="com.example.spring6.iocxml.di.Book">
<property name="author" value="backend"/>
<property name="bname" value="java"/>
</bean>
</beans>
Approach Two: Constructor Injection
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- setter injection -->
<bean id="book" class="com.example.spring6.iocxml.di.Book">
<property name="author" value="backend"/>
<property name="bname" value="java"/>
</bean>
<!-- constructor injection -->
<bean id="bookCon" class="com.example.spring6.iocxml.di.Book">
<constructor-arg name="author" value="java development"/>
<constructor-arg name="bname" value="spring"/>
</bean>
</beans>
package com.example.spring6.iocxml.di;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBook {
@Test
public void testConstructor() {
ApplicationContext context = new
ClassPathXmlApplicationContext("bean-di.xml");
Book book = context.getBean("bookCon", Book.class);
System.out.println(book);
}
}
Special Value Handling
Literal Value Assignment
What is a literal?
int a = 10;declares variableainitialized to10. When referencinga, you get the value10.However, if
ahas quotes:'a', it is no longer a variable but represents the letteraitself—this is a literal. Literals have no extended meaning; they are the data as displayed.
<!-- Spring treats value attribute as literal when assigning properties -->
<property name="name" value="John"/>
null Values
<property name="name">
<null/>
</property>
Note:
<property name="name" value="null"></property>This assigns the string
null, not the Java null value.
XML Entities
<!-- Less-than symbol in XML denotes tag start and cannot be used freely -->
<!-- Solution 1: use XML entity -->
<property name="expression" value="a &lt; b"/>
CDATA Section
<property name="expression">
<!-- Solution 2: CDATA section -->
<!-- C in CDATA stands for Character, indicating plain text -->
<!-- XML parser recognizes CDATA as pure text and won't parse as XML -->
<!-- Any symbols inside CDATA are allowed -->
<value><![CDATA[a < b]]></value>
</property>
Assigning Object-Type Properties
Approach One: External Bean Reference
- Create two classes: Department and Employee
- In Employee bean, use property to reference Department's bean
package com.example.spring6.iocxml.ditest;
public class Department {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
public void info() {
System.out.println("Department: " + dname);
}
}
package com.example.spring6.iocxml.ditest;
public class Employee {
private Department dept;
private String ename;
private Integer age;
public Department getDept() {
return dept;
}
public void work() {
System.out.println(ename + " working..." + age);
dept.info();
}
public void setDept(Department dept) {
this.dept = dept;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
package com.example.spring6.iocxml.ditest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestEmp {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("bean-ditest.xml");
Employee emp = context.getBean("emp", Employee.class);
emp.work();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dept" class="com.example.spring6.iocxml.ditest.Department">
<property name="dname" value="Operations"/>
</bean>
<bean id="emp" class="com.example.spring6.iocxml.ditest.Employee">
<property name="age" value="23"/>
<property name="ename" value="alice"/>
<property name="dept" ref="dept"/>
</bean>
</beans>
Approach Two: Inner Bean Injection
<!-- inner bean injection -->
<bean id="emp2" class="com.example.spring6.iocxml.ditest.Employee">
<property name="age" value="25"/>
<property name="ename" value="bob"/>
<property name="dept">
<bean id="dept2" class="com.example.spring6.iocxml.ditest.Department">
<property name="dname" value="HR"/>
</bean>
</property>
</bean>
Approach Three: Cascading Assignment
<!-- cascading assignment -->
<bean id="dept3" class="com.example.spring6.iocxml.ditest.Department">
<property name="dname" value="R&D"/>
</bean>
<bean id="emp3" class="com.example.spring6.iocxml.ditest.Employee">
<property name="ename" value="Tom"/>
<property name="age" value="22"/>
<property name="dept" ref="dept3"/>
<property name="dept.dname" value="QA"/>
</bean>
Array Property Injection
package com.example.spring6.iocxml.ditest;
import java.util.Arrays;
public class Employee {
private Department dept;
private String ename;
private Integer age;
private String[] loves;
public void work() {
System.out.println(ename + " working..." + age);
dept.info();
System.out.println(Arrays.toString(loves));
}
public void setLoves(String[] loves) {
this.loves = loves;
}
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dept" class="com.example.spring6.iocxml.ditest.Department">
<property name="dname" value="Operations"/>
</bean>
<bean id="emp" class="com.example.spring6.iocxml.ditest.Employee">
<property name="age" value="23"/>
<property name="ename" value="alice"/>
<property name="dept" ref="dept"/>
<property name="loves">
<array>
<value>sleeping</value>
<value>music</value>
<value>movies</value>
</array>
</property>
</bean>
</beans>
Collection Property Injection
List Collection Injection
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="empOne" class="com.example.spring6.iocxml.ditest.Employee">
<property name="age" value="23"/>
<property name="ename" value="alice"/>
</bean>
<bean id="empTwo" class="com.example.spring6.iocxml.ditest.Employee">
<property name="age" value="33"/>
<property name="ename" value="bob"/>
</bean>
<bean id="dept" class="com.example.spring6.iocxml.ditest.Department">
<property name="dname" value="Tech"/>
<property name="empList">
<list>
<ref bean="empOne"/>
<ref bean="empTwo"/>
</list>
</property>
</bean>
</beans>
package com.example.spring6.iocxml.ditest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDept {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("bean-dilist.xml");
Department dept = context.getBean("dept", Department.class);
dept.info();
}
}
package com.example.spring6.iocxml.ditest;
import java.util.List;
public class Department {
private List<Employee> empList;
public List<Employee> getEmpList() {
return empList;
}
public void setEmpList(List<Employee> empList) {
this.empList = empList;
}
private String dname;
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public void info() {
System.out.println("Department: " + dname);
for (Employee emp : empList) {
System.out.println(emp.getEname());
}
}
}
Map Collection Injection
Student.java
package com.example.spring6.iocxml.dimap;
import java.util.Map;
public class Student {
private String sid;
private String name;
private Map<String, Teacher> teacherMap;
public void run() {
System.out.println("Student ID: " + sid + " Name: " + name);
System.out.println(teacherMap);
}
@Override
public String toString() {
return "Student{" +
"sid='" + sid + '\'' +
", name='" + name + '\'' +
", teacherMap=" + teacherMap +
'}';
}
public Map<String, Teacher> getTeacherMap() {
return teacherMap;
}
public void setTeacherMap(Map<String, Teacher> teacherMap) {
this.teacherMap = teacherMap;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Teacher.java
package com.example.spring6.iocxml.dimap;
public class Teacher {
private String teacherId;
private String teacherName;
@Override
public String toString() {
return "Teacher{" +
"teacherId='" + teacherId + '\'' +
", teacherName='" + teacherName + '\'' +
'}';
}
public String getTeacherId() {
return teacherId;
}
public void setTeacherId(String teacherId) {
this.teacherId = teacherId;
}
public String getTeacherName() {
return teacherName;
}
public void setTeacherName(String teacherName) {
this.teacherName = teacherName;
}
}
package com.example.spring6.iocxml.dimap;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestStu {
@Test
public void testStu() {
ApplicationContext context = new
ClassPathXmlApplicationContext("bean-dimap.xml");
Student student = context.getBean("student", Student.class);
student.run();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.example.spring6.iocxml.dimap.Student">
<property name="name" value="alice"/>
<property name="sid" value="111"/>
<property name="teacherMap">
<map>
<entry>
<key>
<value>10001</value>
</key>
<ref bean="teacherOne"/>
</entry>
<entry>
<key>
<value>10099</value>
</key>
<ref bean="teacherTwo"/>
</entry>
</map>
</property>
</bean>
<bean id="teacherOne" class="com.example.spring6.iocxml.dimap.Teacher">
<property name="teacherId" value="22"/>
<property name="teacherName" value="Wang"/>
</bean>
<bean id="teacherTwo" class="com.example.spring6.iocxml.dimap.Teacher">
<property name="teacherId" value="33"/>
<property name="teacherName" value="Li"/>
</bean>
</beans>
Extracting Collection Beans
- Create three objects
- Inject primitive properties
- Use
util:namespace for definitions - Reference the util collection bean in Student bean for list and map injection
Lesson.java
package com.example.spring6.iocxml.dimap;
public class Lesson {
private String lessonName;
@Override
public String toString() {
return "Lesson{" +
"lessonName='" + lessonName + '\'' +
'}';
}
public String getLessonName() {
return lessonName;
}
public void setLessonName(String lessonName) {
this.lessonName = lessonName;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.example.spring6.iocxml.dimap.Student">
<property name="name" value="lucy"/>
<property name="sid" value="111"/>
<property name="lessonList" ref="lessonList"/>
<property name="teacherMap" ref="teacherMap"/>
</bean>
<util:list id="lessonList">
<ref bean="lessonOne"/>
<ref bean="lessonTwo"/>
</util:list>
<util:map id="teacherMap">
<entry>
<key>
<value>10001</value>
</key>
<ref bean="teacherOne"/>
</entry>
<entry>
<key>
<value>1208</value>
</key>
<ref bean="teacherTwo"/>
</entry>
</util:map>
<bean id="lessonOne" class="com.example.spring6.iocxml.dimap.Lesson">
<property name="lessonName" value="Java Development"/>
</bean>
<bean id="lessonTwo" class="com.example.spring6.iocxml.dimap.Lesson">
<property name="lessonName" value="Frontend Development"/>
</bean>
<bean id="teacherOne" class="com.example.spring6.iocxml.dimap.Teacher">
<property name="teacherId" value="2201"/>
<property name="teacherName" value="Wang"/>
</bean>
<bean id="teacherTwo" class="com.example.spring6.iocxml.dimap.Teacher">
<property name="teacherId" value="2200"/>
<property name="teacherName" value="Liu"/>
</bean>
</beans>
p Namespace
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- p namespace injection -->
<bean id="studentP" class="com.example.spring6.iocxml.dimap.Student"
p:sid="111" p:name="stream" p:lessonList-ref="lessonList" p:teacherMap-ref="teacherMap">
</bean>
<bean id="student" class="com.example.spring6.iocxml.dimap.Student">
<property name="name" value="lucy"/>
<property name="sid" value="111"/>
<property name="lessonList" ref="lessonList"/>
<property name="teacherMap" ref="teacherMap"/>
</bean>
<util:list id="lessonList">
<ref bean="lessonOne"/>
<ref bean="lessonTwo"/>
</util:list>
<util:map id="teacherMap">
<entry>
<key>
<value>10001</value>
</key>
<ref bean="teacherOne"/>
</entry>
<entry>
<key>
<value>1208</value>
</key>
<ref bean="teacherTwo"/>
</entry>
</util:map>
<bean id="lessonOne" class="com.example.spring6.iocxml.dimap.Lesson">
<property name="lessonName" value="Java Development"/>
</bean>
<bean id="lessonTwo" class="com.example.spring6.iocxml.dimap.Lesson">
<property name="lessonName" value="Frontend Development"/>
</bean>
<bean id="teacherOne" class="com.example.spring6.iocxml.dimap.Teacher">
<property name="teacherId" value="2201"/>
<property name="teacherName" value="Wang"/>
</bean>
<bean id="teacherTwo" class="com.example.spring6.iocxml.dimap.Teacher">
<property name="teacherId" value="2200"/>
<property name="teacherName" value="Liu"/>
</bean>
</beans>
package com.example.spring6.iocxml.dimap;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestStu {
@Test
public void testStu() {
ApplicationContext context = new
ClassPathXmlApplicationContext("bean-diref.xml");
Student student = context.getBean("studentP", Student.class);
student.run();
}
}
External Property Files
Maven dependencies
<!-- MySQL Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- DataSource -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
jdbc.properties
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/spring?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
Bean configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
</bean>
</beans>
Test
package com.example.spring6.iocxml.jdbc;
import com.alibaba.druid.pool.DruidDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestJdbc {
@Test
public void demo1() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/spring?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
}
@Test
public void demo2() {
ApplicationContext context = new
ClassPathXmlApplicationContext("bean-jdbc.xml");
DruidDataSource dataSource = context.getBean(DruidDataSource.class);
System.out.println(dataSource.getUrl());
}
}
Bean Scope and Lifecycle
package com.example.spring6.iocxml.life;
public class User {
private String name;
public User() {
System.out.println("1 Bean instance created via no-arg constructor");
}
public void initMethod() {
System.out.println("4 Bean initialization via specified init method");
}
public void destroyMethod() {
System.out.println("7 Bean destruction via specified destroy method");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("2 Setting property values on bean");
this.name = name;
}
}
package com.example.spring6.iocxml.life;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("3 BeanPostProcessor before initialization");
System.out.println(beanName + "::" + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("5 BeanPostProcessor after initialization");
System.out.println(beanName + "::" + bean);
return bean;
}
}
package com.example.spring6.iocxml.life;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new
ClassPathXmlApplicationContext("bean-life.xml");
User user = context.getBean("user", User.class);
System.out.println("6 Bean creation complete, ready for use");
System.out.println(user);
context.close();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.example.spring6.iocxml.life.User"
scope="singleton" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="lucy"/>
</bean>
<!-- BeanPostProcessor must be registered in the container to take effect -->
<bean id="myBeanPost" class="com.example.spring6.iocxml.life.MyBeanPost"/>
</beans>
FactoryBean
FactoryBean is a Spring mechanism for integrating third-party frameworks. Unlike regular beans, when configuring a FactoryBean-type bean, what you get is not the class specified in the class attribute, but the return value of the getObject() method. Through this mechanism, Spring hides complex component creation details and presents a simplified interface.
When integrating MyBatis, Spring uses the FactoryBean mechanism to create SqlSessionFactory objects.
package com.example.spring6.iocxml.factorybean;
import org.springframework.beans.factory.FactoryBean;
public class MyFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
package com.example.spring6.iocxml.factorybean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("bean-factorybean.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
}
package com.example.spring6.iocxml.factorybean;
public class User {
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.example.spring6.iocxml.factorybean.MyFactoryBean"/>
</beans>
XML-Based Autowiring
package com.example.spring6.iocxml.auto.controller;
import com.example.spring6.iocxml.auto.service.UserService;
import com.example.spring6.iocxml.auto.service.UserServiceImpl;
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void addUser() {
System.out.println("controller method executed");
userService.addUserService();
}
}
package com.example.spring6.iocxml.auto.service;
public interface UserService {
void addUserService();
}
package com.example.spring6.iocxml.auto.service;
import com.example.spring6.iocxml.auto.dao.UserDao;
import com.example.spring6.iocxml.auto.dao.UserDaoImpl;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUserService() {
System.out.println("userService method executed");
userDao.addUserDao();
}
}
package com.example.spring6.iocxml.auto.dao;
public class UserDaoImpl implements UserDao {
@Override
public void addUserDao() {
System.out.println("userDao method executed");
}
}
package com.example.spring6.iocxml.auto.dao;
public interface UserDao {
void addUserDao();
}
package com.example.spring6.iocxml.auto;
import com.example.spring6.iocxml.auto.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("bean-auto.xml");
UserController controller = context.getBean("userController", UserController.class);
controller.addUser();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- autowire by type -->
<!-- <bean id="userController" class="com.example.spring6.iocxml.auto.controller.UserController" autowire="byType"/> -->
<!-- <bean id="userService" class="com.example.spring6.iocxml.auto.service.UserServiceImpl" autowire="byType"/> -->
<!-- <bean id="userDao" class="com.example.spring6.iocxml.auto.dao.UserDaoImpl"/> -->
<!-- autowire by name -->
<bean id="userController" class="com.example.spring6.iocxml.auto.controller.UserController" autowire="byName"/>
<bean id="userService" class="com.example.spring6.iocxml.auto.service.UserServiceImpl" autowire="byName"/>
<bean id="userDao" class="com.example.spring6.iocxml.auto.dao.UserDaoImpl"/>
</beans>
Autowire mode: byName
Matches property names as bean IDs in the IoC container to find corresponding beans for assignment.
Annotation-Based Bean Management
Enabling Component Scanning
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<context:component-scan base-package="com.example"/>
</beans>
Scenario 1: Basic scanning
<context:component-scan base-package="com.example.spring6">
</context:component-scan>
Scenario 2: Exclude specified components
<context:component-scan base-package="com.example.spring6">
<!-- context:exclude-filter specifies exclusion rules -->
<!-- type: sets exclusion/inclusion criteria -->
<!-- type="annotation" excludes by annotation, expression contains full class name -->
<!-- type="assignable" excludes by type, expression contains full class name -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!-- <context:exclude-filter type="assignable" expression="com.example.spring6.controller.UserController"/> -->
</context:component-scan>
Scenario 3: Scan only specified components
<context:component-scan base-package="com.example" use-default-filters="false">
<!-- context:include-filter specifies additional rules -->
<!-- use-default-filters="false" disables default scanning rules -->
<!-- must set use-default-filters="false" because default scans all classes in package -->
<!-- type: sets exclusion/inclusion criteria -->
<!-- type="annotation" includes by annotation, expression contains full class name -->
<!-- type="assignable" includes by type, expression contains full class name -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!-- <context:include-filter type="assignable" expression="com.example.spring6.controller.UserController"/> -->
</context:component-scan>
Bean Creation via Annotations
All four annotations can create beans:
package com.example.bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
@Component(value = "user") // equivalent to <bean id="user" class="..."/>
// @Controller
// @Repository
// @Service
public class User {
}
Property Injection
Using @Autowired alone defaults to byType injection.
package com.example.autowired.controller;
import com.example.autowired.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
@Autowired // find by type, complete injection
private UserService userService;
public void add() {
System.out.println("controller...");
userService.add();
}
}
package com.example.autowired.service;
import com.example.autowired.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
// Constructor injection
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void add() {
System.out.println("service...");
userDao.add();
}
}
package com.example.autowired.controller;
import com.example.autowired.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
// Constructor parameter injection
public UserController(@Autowired UserService userService) {
this.userService = userService;
}
public void add() {
System.out.println("controller...");
userService.add();
}
}
@Autowired can be appllied to: fields, setter methods, constructors, and constructor parameters.
- When a constructor has only one parameter,
@Autowiredcan be omitted. @Autowireddefaults to byType injection. Use@Qualifierfor byName injection.
@Resource Injection
@Resource also performs property injection. Key differences from @Autowired:
@Resourceis part of the JDK extension (JSR-250 standard), making it more universal.@Autowiredis Spring-specific.@Resourcedefaults to byName; if name is unspecified, uses field name. Falls back to byType if not found.@Autowireddefaults to byType; use@Qualifierfor byName.@Resourceapplies to fields and setter methods.@Autowiredapplies to fields, setter methods, constructors, and constructor parameters.
@Resource requires additional dependency for JDK versions other than JDK 8:
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
package com.example.resource.controller;
import com.example.resource.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
@Controller("myUserController")
public class UserController {
// byName injection
// @Resource(name = "myUserService")
// private UserService userService;
// byType injection
@Resource
private UserService userService;
public void add() {
System.out.println("controller...");
userService.add();
}
}
Full Annotation-Based Development
package com.example.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.example")
public class SpringConfig {
}
package com.example.resource;
import com.example.config.SpringConfig;
import com.example.resource.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestUserControllerAnno {
public static void main(String[] args) {
ApplicationContext context = new
AnnotationConfigApplicationContext(SpringConfig.class);
UserController controller = context.getBean(UserController.class);
controller.add();
}
}
Java Reflection Mechanism
package com.example;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestCar {
// 1. Multiple ways to obtain Class object
@Test
public void test01() throws Exception {
// Method 1: Class.class
Class<?> clazz1 = Car.class;
// Method 2: object.getClass()
Class<?> clazz2 = new Car().getClass();
// Method 3: Class.forName("fully.qualified.name")
Class<?> clazz3 = Class.forName("com.example.Car");
Car car = (Car) clazz3.getDeclaredConstructor().newInstance();
System.out.println(car);
}
// 2. Obtain constructors via reflection
@Test
public void test02() throws Exception {
Class<?> clazz = Car.class;
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> c : constructors) {
System.out.println("Method: " + c.getName() + " Parameter count: " + c.getParameterCount());
}
// 1. Public constructor
// Constructor<?> c1 = clazz.getConstructor(String.class, int.class, String.class);
// Car car1 = (Car) c1.newInstance("HQ", 10, "Yellow");
// 2. Private constructor
Constructor<?> c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
c2.setAccessible(true);
Car car2 = (Car) c2.newInstance("HQ", 11, "Yellow");
System.out.println(car2);
}
// 3. Obtain fields via reflection
@Test
public void test03() throws Exception {
Class<?> clazz = Car.class;
Car car = (Car) clazz.getDeclaredConstructor().newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.getName().equals("name")) {
field.setAccessible(true);
field.set(car, "Audi");
}
System.out.println(field.getName());
}
System.out.println(car);
}
// 4. Obtain methods via reflection
@Test
public void test04() throws Exception {
Car car = new Car("BMW", 19, "Black");
Class<?> clazz = car.getClass();
Method[] methods = clazz.getMethods();
for (Method m : methods) {
if (m.getName().equals("toString")) {
String invoke = (String) m.invoke(car);
}
Method[] allMethods = clazz.getDeclaredMethods();
for (Method m2 : allMethods) {
if (m2.getName().equals("run")) {
m2.setAccessible(true);
m2.invoke(car);
}
}
}
}
}
Implementing a Custom IoC Container
ApplicationContext.java
package com.example.bean;
import com.example.anno.Bean;
public interface ApplicationContext {
Object getBean(Class<?> clazz);
}
AnnotationApplicationContext.java
package com.example.bean;
import com.example.anno.Bean;
import com.example.anno.Di;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class AnnotationApplicationContext implements ApplicationContext {
private Map<Class<?>, Object> beanFactory = new HashMap<>();
private static String rootPath;
@Override
public Object getBean(Class<?> clazz) {
return beanFactory.get(clazz);
}
public AnnotationApplicationContext(String basePackage) {
try {
String packagePath = basePackage.replaceAll("\\.", "\\\\");
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
String filePath = URLDecoder.decode(url.getFile(), "utf-8");
rootPath = filePath.substring(0, filePath.length() - packagePath.length());
loadBean(new File(filePath));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
loadDi();
}
private void loadBean(File file) throws Exception {
if (file.isDirectory()) {
File[] childrenFiles = file.listFiles();
if (childrenFiles == null || childrenFiles.length == 0) {
return;
}
for (File child : childrenFiles) {
if (child.isDirectory()) {
loadBean(child);
} else {
String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
if (pathWithClass.contains(".class")) {
String allName = pathWithClass.replaceAll("\\\\", ".")
.replace(".class", "");
Class<?> clazz = Class.forName(allName);
if (!clazz.isInterface()) {
Bean annotation = clazz.getAnnotation(Bean.class);
if (annotation != null) {
Object instance = clazz.getConstructor().newInstance();
if (clazz.getInterfaces().length > 0) {
beanFactory.put(clazz.getInterfaces()[0], instance);
} else {
beanFactory.put(clazz, instance);
}
}
}
}
}
}
}
}
private void loadDi() {
Set<Map.Entry<Class<?>, Object>> entries = beanFactory.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Object obj = entry.getValue();
Class<?> clazz = obj.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
Di annotation = field.getAnnotation(Di.class);
if (annotation != null) {
field.setAccessible(true);
try {
field.set(obj, beanFactory.get(field.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
Bean.java
package com.example.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
Di.java
package com.example.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
TestUser.java
package com.example;
import com.example.bean.AnnotationApplicationContext;
import com.example.bean.ApplicationContext;
import com.example.service.UserService;
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new AnnotationApplicationContext("com.example");
UserService userService = (UserService) context.getBean(UserService.class);
System.out.println(userService);
userService.add();
}
}