【实战版】Spring IOC 控制反转详解

📅 2026/8/5 12:19:51
【实战版】Spring IOC 控制反转详解
目录一、含义二、基于 XML 管理 Bean1、准备2、配置 Bean3、获取 Bean4、Spring 管理数据源5、自动装配三、基于注解管理 Bean1、准备2、标记3、扫描4、获取 Bean5、自动装配四、其他知识1、Bean 的作用域2、Bean 的生命周期3、Bean 的后置处理器4、FactoryBean一、含义把自己创建资源、向环境索取资源变成环境将资源准备好我们享受资源注入。二、基于 XML 管理 Bean1、准备创建 Maven 工程pom.xml 添加依赖。dependencies dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.3.1/version /dependency /dependenciescom.example.pojo 包下创建实体类 Teacher、Clazz、Student。public class Teacher { //AltInsert一键生成构造器、Get、Set、toString 方法... private Integer tid; private String tname; public Teacher() { } public Teacher(Integer tid, String tname) { this.tid tid; this.tname tname; } public void setTid(Integer tid) { this.tid tid; } public void setTname(String tname) { this.tname tname; } public Integer getTid() { return tid; } public String getTname() { return tname; } Override public String toString() { return Teacher{ tid tid , tname tname \ }; } } public class Clazz { private Integer cid; private String cname; private ListStudent students; public Clazz() { } public Clazz(Integer cid, String cname) { this.cid cid; this.cname cname; } public Integer getCid() { return cid; } public String getCname() { return cname; } public ListStudent getStudents() { return students; } public void setCid(Integer cid) { this.cid cid; } public void setCname(String cname) { this.cname cname; } public void setStudents(ListStudent students) { this.students students; } Override public String toString() { return Clazz{ cid cid , cname cname \ , students students }; } } public class Student { private Integer sid; private String sname; private Integer age; private String gender; private Clazz clazz; private String[] hobby; private MapString,Teacher teacherMap; public Student() { } public Student(Integer sid, String sname, Integer age, String gender) { this.sid sid; this.sname sname; this.age age; this.gender gender; } public Integer getSid() { return sid; } public String getSname() { return sname; } public Integer getAge() { return age; } public String getGender() { return gender; } public Clazz getClazz() { return clazz; } public String[] getHobby() { return hobby; } public MapString, Teacher getTeacherMap() { return teacherMap; } public void setSid(Integer sid) { this.sid sid; } public void setSname(String sname) { this.sname sname; } public void setAge(Integer age) { this.age age; } public void setGender(String gender) { this.gender gender; } public void setClazz(Clazz clazz) { this.clazz clazz; } public void setHobby(String[] hobby) { this.hobby hobby; } public void setTeacherMap(MapString, Teacher teacherMap) { this.teacherMap teacherMap; } Override public String toString() { return Student{ sid sid , sname sname \ , age age , gender gender \ , clazz clazz , hobby Arrays.toString(hobby) , teacherMap teacherMap }; } }2、配置 Bean右击目录 resources依次选择 New - XML Configuration File - Spring Config创建 Spring 配置文件 applicationContext.xml。?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:utilhttp://www.springframework.org/schema/util xmlns:contexthttp://www.springframework.org/schema/context xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd !-- bean 标签用于配置 bean将对象交给 IOC 容器管理其中 id 用于设置 bean 的唯一标识class 用于设置 bean 对应类型的全类名。 -- bean idstudent classcom.example.pojo.Student !-- property 标签用于给对象的属性赋值其中 name 用于设置属性名value 用于设置属性值 ref 用于设置要引用的 IOC 容器中 bean 的 id从而将 bean 对应对象赋值给当前属性。 此时Spring 使用 bean 对应类型的全类名通过反射调用无参构造创建对象再通过 setXxx() 为对象的属性赋值。 -- property namesid value1001/property property namesname value张三/property !-- 为属性赋 null 值 -- property nameage null / /property !-- 为属性赋含有 、等符号 的值使用 CDATA 节将内容原样解析 -- property namegender value![CDATA[男]]/value /property !-- 通过引用外部已声明的 bean为属性赋 类类型 的值 -- !--property nameclazz refclazz/property-- !-- 通过内部 bean为属性赋 类类型 的值 -- property nameclazz !-- 内部 bean 仅用于给属性赋值不能在外部通过 IOC 容器获取因此可以省略 id 属性 -- bean idclazzInner classcom.example.pojo.Clazz property namecid value3002/property property namecname value飞船班/property /bean /property !-- 为属性赋 数组类型 的值 -- property namehobby array value抽烟/value value喝酒/value value烫头/value /array /property !-- 通过引用外部已声明的集合类型的 bean为属性赋 Map 集合类型 的值 -- !--property nameteacherMap refteacherMap/property-- !-- 通过 map 标签为属性赋 Map 集合类型 的值 -- property nameteacherMap map entry key2001 value-refteacherOne/entry entry key2002 value-refteacherTwo/entry /map /property /bean bean idclazz classcom.example.pojo.Clazz property namecid value3001/property property namecname value火箭班/property !-- 通过引用外部已声明的集合类型的 bean为属性赋 List 集合类型 的值 -- !--property namestudents refstudentList/property-- !-- 通过 list 标签为属性赋 List 集合类型 的值 -- property namestudents list ref beanstudent/ref /list /property /bean !-- 通过 util 约束配置集合类型的 bean -- util:list idstudentList ref beanstudent/ref /util:list !-- 通过 util 约束配置集合类型的 bean -- util:map idteacherMap entry key2001 value-refteacherOne/entry entry key2002 value-refteacherTwo/entry /util:map bean idteacherOne classcom.example.pojo.Teacher !-- constructor-arg 标签用于指定有参构造其中 name 用于设置参数名value 用于设置参数值。 此时Spring 使用 bean 对应类型的全类名通过反射调用与 constructor-arg 标签参数匹配的有参构造创建对象。 -- constructor-arg nametid value2001/constructor-arg constructor-arg nametname value李白/constructor-arg /bean bean idteacherTwo classcom.example.pojo.Teacher property nametid value2002/property property nametname value陶渊明/property /bean /beans要点精炼基于 XML 配置 Bean依赖注入之 setter 注入、依赖注入之构造器注入为属性赋字面量类型的值、null 值、含有特殊符号的值、类类型的值、数组类型的值、List 集合类型的值、Map 集合类型的值。3、获取 Bean根据 id、类型、id 类型从 IOC 容器中获取 Bean 对象。//通过读取类路径下 XML 格式的配置文件创建 IOC 容器对象 ApplicationContext ioc new ClassPathXmlApplicationContext(applicationContext.xml); //1根据 id从 IOC 容器中获取 bean 对象 Student studentOne (Student) ioc.getBean(student); /* 2根据类型从 IOC 容器中获取 bean 对象 注意要求容器中有且仅有一个类型匹配的 bean否则报错 NoSuchBeanDefinitionException/NoUniqueBeanDefinitionException 满足 bean 唯一性的前提下通过 bean 的类型、bean 所继承的类的类型、bean 所实现的接口的类型均可获取 bean 对象。 */ Student studentTwo ioc.getBean(Student.class); //3根据类型 id从 IOC 容器中获取 bean 对象 Teacher teacher ioc.getBean(teacherOne, Teacher.class);4、Spring 管理数据源pom.xml 添加依赖。!-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.16/version /dependency !-- 数据源 -- dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.0.31/version /dependency右击目录 resources依次选择 New - Resource Bundle创建属性配置文件 jdbc.properties。jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/mydb?useUnicodetruecharacterEncodingutf-8serverTimezoneGMT%2b8 jdbc.usernameroot jdbc.passwordroot jdbc.initialSize10 jdbc.maxActive20 jdbc.maxWait5000Spring 配置文件 applicationContext.xml配置数据源。!-- 配置 Alibaba Druid 数据库连接池 -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName value${jdbc.driver}/property property nameurl value${jdbc.url}/property property nameusername value${jdbc.username}/property property namepassword value${jdbc.password}/property !-- 数据库连接池初始化连接数默认为 0 -- property nameinitialSize value${jdbc.initialSize}/property !-- 数据库连接池最大连接数默认为 8 -- property namemaxActive value${jdbc.maxActive}/property !-- 从数据库连接池获取连接的最大等待时间默认为 -1(无限等待) -- property namemaxWait value${jdbc.maxWait}/property /bean !-- 引入外部属性文件 -- context:property-placeholder locationjdbc.properties/context:property-placeholder根据类型从 IOC 容器中获取数据源。//通过读取类路径下 XML 格式的配置文件创建 IOC 容器对象 ApplicationContext ioc new ClassPathXmlApplicationContext(applicationContext.xml); //根据类型从 IOC 容器中获取 Druid 数据库连接池对象 DruidDataSource dataSource ioc.getBean(DruidDataSource.class); //从数据库连接池获取数据库连接对象 System.out.println(dataSource.getConnection());5、自动装配IOC 容器按照既定规则匹配某个 bean为指定 bean 依赖的类类型或接口类型属性赋值的过程。通过 bean 标签的属性 autowire 设置自动装配的规则值 no、default 表不装配属性使用默认值byType 表根据 setter 参数类型匹配 bean为属性赋值无匹配则不装配多个匹配则抛 NoUniqueBeanDefinitionExceptionbyName 表截取 setter 名称作为 bean 的 id 匹配 bean为属性赋值无匹配则不装配。值得注意的是基于 XML 配置 Bean设置 autowire 为 byType / byNameSpring 会先调用无参构造创建对象再 set 注入为对象类类型或接口类型的属性赋值字符串、数字等字面量类型的属性跳过不赋值需手动 property 赋值。三、基于注解管理 Bean1、准备创建 Maven 工程pom.xml 添加依赖。dependencies dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.3.1/version /dependency /dependencies2、标记com.example.controller 包下创建类 UserController添加注解 Controller 将其标识为控制层组件。Controller public class UserController { Autowired private UserService userService; //添加用户信息 public void saveUser(){ userService.saveUser(); } }com.example.service 包下创建接口 UserServicecom.example.service.impl 包下创建实现类 UserServiceImpl添加注解 Service 将其标识为业务层组件。public interface UserService { //保存用户信息 void saveUser(); } Service public class UserServiceImpl implements UserService { Autowired private UserDao userDao; Override public void saveUser() { userDao.saveUser(); } }com.example.dao 包下创建接口 UserDaocom.example.dao.impl 包下创建实现类 UserDaoImpl添加注解 Repository 将其标识为持久层组件。public interface UserDao { //保存用户信息 void saveUser(); } Repository public class UserDaoImpl implements UserDao { Override public void saveUser() { System.out.println(保存成功); } }此外注解 Component 用于将类标识为普通组件Controller、Service、Repository 都是其派生注解基本功能一致仅语义分层区别方便开发者区分代码层次注解只是做标记为了让 Spring 知道开发者在哪些地方标记了什么注解还需要配置组件扫描。3、扫描右击目录 resources依次选择 New - XML Configuration File - Spring Config创建 Spring 配置文件 applicationContext.xml。?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd !-- 扫描组件 -- context:component-scan base-packagecom.example !-- 不扫描谁 type 用于设置排除依据值 annotation 表根据注解排除此时 expression 用于设置要排除的注解的全类名 值 assignable 表根据类型排除此时 expression 为要排除的类型的全类名 SSM 整合时springMVC 只扫描控制层spring 扫描其他层。 -- !--context:exclude-filter typeannotation expressionorg.springframework.stereotype.Controller/-- !--context:exclude-filter typeassignable expressioncom.example.controller.UserController/-- !-- 只扫描谁此时需要在 context:component-scan 标签中设置属性 use-default-filters 的值为 false use-default-filters 属性默认值为 true表所设置包下的所有类都需要扫描此时可以使用排除扫描 use-default-filters 属性设置值为 false表所设置包下的所有类都不需要扫描此时可以使用包含扫描 -- !--context:include-filter typeannotation expressionorg.springframework.stereotype.Controller/-- !--context:include-filter typeassignable expressioncom.example.controller.UserController/-- /context:component-scan /beans通过 注解 扫描 配置的 beanid 默认为类的小驼峰如userController可通过标识类的注解的 value 属性值设置 bean 的自定义 id如Controller(controller)此时Spring 通过反射调用默认无参构造创建对象。4、获取 Bean根据 id、类型、id 类型从 IOC 容器中获取 Bean 对象。//通过读取类路径下 XML 格式的配置文件创建 IOC 容器对象 ApplicationContext ioc new ClassPathXmlApplicationContext(applicationContext.xml); //1根据 id从 IOC 容器中获取 bean 对象 UserController userController (UserController) ioc.getBean(userController); //2根据类型从 IOC 容器中获取 bean 对象 UserService userService ioc.getBean(UserService.class); //3根据类型 id从 IOC 容器中获取 bean 对象 UserDao userDao ioc.getBean(userDaoImpl, UserDao.class);5、自动装配成员变量添加 Autowired 注解即可完成自动装配。Spring 默认根据成员变量的类型在 IOC 容器中匹配某个 bean为成员变量赋值。若有多个类型匹配的 bean则将成员变量的变量名作为 bean 的 id在 IOC 容器中匹配某个 bean为成员变量赋值若有多个类型匹配的 bean但这些 bean 的 id 均与要赋值成员变量的变量名不一致则抛 NoUniqueBeanDefinitionException此时可以在要赋值成员变量的变量名上添加注解 Qualifier通过该注解 value 属性值指定某个 bean 的 id为成员变量赋值。若没有任何一个类型匹配的 bean则抛 NoSuchBeanDefinitionException因为 Autowired 注解中属性 required 默认值为 true要求必须完成自动装配可以将属性 required 设置为 false此时能装配则装配无法装配则使用成员变量默认值。值得注意的是基于注解配置 Bean成员变量添加 Autowired 注解Spring 会先调用默认无参构造创建对象再利用反射直接给成员变量赋值不属于 setter 注入、构造器注入而 Autowired 注解也可标记在 set 方法、构造器上此处不做探讨。四、其他知识1、Bean 的作用域规定 Spring IOC 容器何时创建对象、创建多少个对象以及对象的生效范围。基于 XML 管理 Bean通过 bean 标签的属性 scope 设置 bean 的作用域值 singleton 表单例默认、prototype 表多例每次获取均为新对象基于注解管理 Bean通过注解 Scope 设置 bean 的作用域值 ConfigurableBeanFactory.SCOPE_SINGLETON 表单例默认、ConfigurableBeanFactory.SCOPE_PROTOTYPE 表多例每次获取均为新对象。2、Bean 的生命周期bean 为单例获取 IOC 容器时创建对象、依赖注入、执行后置处理器的 postProcessBeforeInitialization() 方法、执行初始化方法、执行后置处理器的 postProcessAfterInitialization() 方法IOC 容器关闭时执行销毁方法代码中没有地方持有这个 bean 的引用后等待 JVM 发生 GC 回收对象。bean 为多例获取 bean 时创建对象、依赖注入、执行后置处理器的 postProcessBeforeInitialization() 方法、执行初始化方法、执行后置处理器的 postProcessAfterInitialization() 方法不会执行销毁方法代码中没有地方持有这个 bean 的引用后等待 JVM 发生 GC 回收对象。基于 XML 管理 Bean通过 bean 标签的属性 init-method 设置 bean 的初始化方法destroy-method 设置 bean 的销毁方法基于注解管理 Bean通过注解 PostConstruct 设置 bean 的初始化方法PreDestroy 设置 bean 的销毁方法。3、Bean 的后置处理器用于在 Bean 生命周期初始化前后添加额外操作。创建类 MyBeanPostProcessor 实现 BeanPostProcessor 接口重写 postProcessBeforeInitialization()、postProcessAfterInitialization() 方法public class MyBeanPostProcessor implements BeanPostProcessor { //CtrlO选择需要实现的方法 //在 bean 生命周期初始化之前执行参数 bean 为当前 IOC 容器管理的 bean、beanName 为 bean 的 id Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } //在 bean 生命周期初始化之后执行参数 bean 为当前 IOC 容器管理的 bean、beanName 为 bean 的 id Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }Spring 配置文件 applicationContext.xml配置后置处理器。此处以 XML 方式进行配置也可使用注解方式配置 bean。bean classcom.example.process.MyBeanPostProcessor/bean值得注意的是Bean 的后置处理器不是单独针对某一个 bean 生效而是针对 IOC 容器中所有 bean 都会执行。4、FactoryBeanSpring 提供的工厂 Bean 接口用于封装复杂对象的创建逻辑。com.example.pojo 包下创建实体类 User。public class User { private Integer id; private String username; private String password; private Integer age; public User() { } public User(Integer id, String username, String password, Integer age) { this.id id; this.username username; this.password password; this.age age; } public Integer getId() { return id; } public String getUsername() { return username; } public String getPassword() { return password; } public Integer getAge() { return age; } public void setId(Integer id) { this.id id; } public void setUsername(String username) { this.username username; } public void setPassword(String password) { this.password password; } public void setAge(Integer age) { this.age age; } Override public String toString() { return User{ id id , username username \ , password password \ , age age }; } }创建类 UserFactoryBean 实现 FactoryBean 接口泛型配置为最终交给 IOC 容器管理的对象类型重写 getObject()、getObjectType()、isSingleton() 方法public class UserFactoryBean implements FactoryBeanUser { //指定泛型为最终交给 IOC 容器管理的对象类型 Override public User getObject() throws Exception { //在这里手写复杂创建逻辑 return new User(); //返回最终交给 IOC 容器管理的对象 } Override public Class? getObjectType() { return User.class; //返回最终交给 IOC 容器管理的对象的类型 } Override public boolean isSingleton() { return true; //设置最终交给 IOC 容器管理的对象是否单例true表单例(默认)、false表多例 } }Spring 配置文件 applicationContext.xml配置 UserFactoryBean。此处以 XML 方式进行配置也可使用注解方式配置 bean。bean iduserBean classcom.example.factory.UserFactoryBean/bean根据 id从 IOC 容器中获取 Bean 对象。//通过读取类路径下 XML 格式的配置文件创建 IOC 容器对象 ApplicationContext ioc new ClassPathXmlApplicationContext(applicationContext.xml); //根据 id从 IOC 容器中获取 bean 对象 User user (User) ioc.getBean(userBean); //加 符号从 IOC 容器中获取工厂本身 UserFactoryBean 对象 UserFactoryBean userFactoryBean (UserFactoryBean) ioc.getBean(userBean);值得注意的是BeanFactory 是 IOC 容器最顶层根接口是 Bean 的工厂负责创建、获取、管理所有 Bean 对象而 FactoryBean 是 Spring 提供的工厂 Bean 接口用于封装复杂对象的创建逻辑Spring 先实例化 FactoryBean 工厂再调用 getObject() 获取目标对象放入容器对外提供使用工厂本身也可通过 id 加 前缀从 IOC 容器中获取。本文相关项目请关注程序员钟霖回复CSDN获取。友情链接【附安装包】Java/JDK介绍、下载、安装、配置与使用保姆级教程_java jdk-CSDN博客【附安装包】Maven下载、安装、配置与使用保姆级教程-CSDN博客【附安装包】IDEA下载、安装、配置与使用保姆级教程_idea下载安装配置教程-CSDN博客