Spring IOC的详解
一、前言
学习spring boot框架,了解到@Bean、@Componet等注解。通过注解的使用拓展到Spring IOC内容,在Spring中Spring IOC、Spring AOP都是至关重要的模块,所以本章就具体介绍Spring IOC,从Spring IOC的通俗的定义->具体的实践->内在的原理->思考总结完成整个学习内容。
二、定义
Spring IOC是Spring框架的重要技术,其中IOC是控制反转(技术思想)的意思,核心就是将new对象的操作交由IOC容器去帮助我们完成,包括创建实例化对象并管理它,我们需要使用哪个对象,去IOC容器获取即可。控制指的是对象创建(实例化、管理)的权力,反转指的是控制权交给了外部环境(IOC容器)。解决的问题是面向对象编程中对象之间的耦合问题,目标是设计成高内聚低耦合的系统。
上述图形化的方式解释了IOC设计思想,借助于第三方(IOC)实现具有依赖关系的对象之间的解耦,使用的A、B、C、D四个对象不在相互交互,没有耦合关系(在代码中对象间使用new创建关联对象)。对象之间的传动全部依靠IOC容器,全部对象的控制权上交给IOC容器,所以IOC容器成了整个系统的关键核心,它起到一个“粘合剂”的作用,把系统中所有对象粘合在一起发挥作用。IOC本质是大容器、大工程,主要作用是创建和管理对象的依赖关系,削减计算机程序的耦合,提供程序的可扩展和维护性。
三、实践
第一步、在IDEA创建一个spring boot项目,使用Maven工程管理项目的包依赖关系,引入相关的springframework包、XML读取的dom4j包、单元测试包,pom.xml如下代码所示。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--引入dom4J--> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> </dependencies>
第二步、在项目中新增各个层级目录文件夹或文件,比如控制层Controller、服务层Service、持久化层Dao、领域层Domain、ORM层Mapper等文件夹,创建UserController、IUserService、UserServiceImpl、IUserDao、UserDaoImpl、User、UserMapper。
package com.test.springioc.controller; import org.springframework.stereotype.Controller; /** * 控制器 */ @Controller public class UserController { }
package com.test.springioc.domain; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * 领域对象 */ @Data @Builder @AllArgsConstructor @NoArgsConstructor public class User { private Integer id; private String name; private Integer sex; }
package com.test.springioc.service; import com.test.springioc.domain.User; /** * 服务层接口 */ public interface IUserService { int Save(User user); } package com.test.springioc.service.impl; import com.test.springioc.domain.User; import com.test.springioc.service.IUserService; import org.springframework.stereotype.Service; /** * 服务层实例 */ @Service public class UserServiceImpl implements IUserService { @Override public int Save(User user) { System.out.println("UserService save method running...... "); return 0; } }
package com.test.springioc.dao; import com.test.springioc.domain.User; /** * 持久化层接口 */ public interface IUserDao { int save(User user); } package com.test.springioc.dao.impl; import com.test.springioc.dao.IUserDao; import com.test.springioc.domain.User; import org.springframework.stereotype.Repository; /** * 持久化层实例 */ @Repository public class UserDaoImpl implements IUserDao { @Override public int save(User user) { System.out.println("userDao save method running...... "); return 0; } }
package com.test.springioc.mapper; import com.test.springioc.domain.User; /** * ORM层mybits-plus */ public interface IUserMapper { int save(User user); }
第三步、新增Spring配置文件,可以通过XML或者注解的方式来定义Bean实例,本文通过XML的方式来获取容器中的Bean,文件all.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"> <!--1、使用无参构造函数构建Bean--> <!--配置userDaoImpl--> <bean id="userDao" class="com.test.springioc.dao.impl.UserDaoImpl"></bean> <!--配置userServiceImpl--> <bean id="userService" class="com.test.springioc.service.impl.UserServiceImpl"></bean> <!--2、使用静态方法构建Bean--> <bean id="userServiceStaticFactory" class="com.test.springioc.factory.MyBeanFactory" factory-method="getUserService"></bean> <!--3、使用实例方法构建Bean--> <bean id="instanceFactory" class="com.test.springioc.factory.InstanceFactory"></bean> <bean id="userServiceMethodFactory" factory-bean="instanceFactory" factory-method="getUserService"></bean> </beans>
在Bean标签中通过scope属性定义Bean的作用范围及生命周期,包括①singleton:单例模式,在容器中仅有一个实例对象,IOC创建的对象默认都是单例的;②prototype:原型模型(多例模式),每次getBean都会创建一个新的对象;③request:(web环境)每个request请求维护一个实例session(web环境)每个;④session:会话维护一个实例。单例模式:单例模式bean对象生命周期与容器相同;多例模式:每次使用对象都重新创建一个新对象,Spring框架只负责创建,销毁由JAVA垃圾回收器负责。
在Bean标签中常用的属性包括①id属性:bean对象的唯一标识;class属性:类全限定名;②name属性:类名称,可以重复;③factory-bean属性:用于指定创建当前bean对象的工厂方法,如配合factory-bean属性使用, 则class属性失效。如配合class属性使用,则用法必须是static的;④scope:指定bean对象的作用范围,默认是singleton;⑤init-method属性:指定bean对象的初始化方法,此方法会在bean对象装配后调用,该方法必须是一个无参方法;⑥destory-method属性:用于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执行。它只 能为scope是singleton时起作用。
第三步、在测试单元中测试获取配置的实例,主要通过三种方式实例化Bean,①使用无参的构造函数,在默认情况下,通过反射调用无参构造函数来创建对象,如果类中没有无参构造函数,则创建对象失败。
@Test public void TestBean() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("all.xml"); User user = User.builder().id(1).name("test").sex(1).build(); IUserDao userDao = (IUserDao) applicationContext.getBean("userDao"); userDao.save(user); IUserService userService = (IUserService) applicationContext.getBean("userService"); userService.Save(user); IUserMapper userMapper = (IUserMapper) applicationContext.getBean("userMapper"); userMapper.save(user); }
②使用静态方法构建Bean。
package com.test.springioc.factory; import com.test.springioc.service.IUserService; import com.test.springioc.service.impl.UserServiceImpl; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Bean创建工厂 */ public class MyBeanFactory { /** * 使用静态方法创建Bean * @return */ public static IUserService getUserService(){ return new UserServiceImpl(); } }
@Test public void TestFactoryStaticMethodBean() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("all.xml"); User user = User.builder().id(1).name("test").sex(1).build(); IUserService userService = (IUserService) applicationContext.getBean("userServiceStaticFactory"); userService.Save(user); }
③使用实例方法构建Bean。
package com.test.springioc.factory; import com.test.springioc.service.IUserService; import com.test.springioc.service.impl.UserServiceImpl; /** * 模拟一个工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数) */ public class InstanceFactory { /** * 实例方法构建Bean * @return */ public IUserService getUserService() { return new UserServiceImpl(); } }
@Test public void TestFactoryInstaceMethodBean() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("all.xml"); User user = User.builder().id(1).name("test").sex(1).build(); IUserService userService = (IUserService) applicationContext.getBean("userServiceMethodFactory"); userService.Save(user); }
第四步、自定义一个简易的IOC容器,基于Spring IOC的构建Bean的原理,通过Bean的Id获取实例对象。①声明一个Bean的test.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"> <!--配置userDaoImpl--> <bean id="userDao" class="com.test.springioc.dao.impl.UserDaoImpl"></bean> </beans>
②定义一个静态工厂方法,设置对象容器,遍历XML文件Bean声明,创建相关的对象实例,使用Id,对象的方式Map存储,提供静态方法获取对象实例,工厂类代码如下所示。
package com.test.springioc.factory; import com.test.springioc.service.IUserService; import com.test.springioc.service.impl.UserServiceImpl; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Bean创建工厂 */ public class MyBeanFactory { private static Map<String,Object> beanMap =new HashMap<>(); static { try { SAXReader saxreader = new SAXReader(); String xml = "src/main/resources/test.xml"; Document doc = saxreader.read(xml); Element rootElement = doc.getRootElement(); List<Element> BeanList = rootElement.elements("bean"); for (Element element :BeanList){ String id1 = element.attributeValue("id"); String class1=element.attributeValue("class"); Class clz = Class.forName(class1); Object object = clz.newInstance(); beanMap.put(id1,object); } }catch (Exception e){ e.printStackTrace(); } } public static Object getBean(String id){ Object o = beanMap.get(id); return o; } /** * 使用静态方法创建Bean * @return */ public static IUserService getUserService(){ return new UserServiceImpl(); } }
③在单元测试中获取对象实例。
/** * 自定义容器、构建Bean */ @Test public void testMyFactory(){ //1:创建工厂对象 MyBeanFactory factory =new MyBeanFactory(); //2:从容器当中根据id获得对象 IUserDao userDao = (IUserDao)factory.getBean("userDao"); System.out.println(userDao); User user = User.builder().id(1).name("test").sex(1).build(); userDao.save(user); }
四、总结
通过上述定义、实践学习了Spring IOC,使用IOC技术在面向对象编程中实现对象间解耦,提供代码可扩展、可维护行。在实际开发中使用XML的方式被注解所代替,减少XML的配置化工作。基于IOC容器的控制反转技术在不同的高级语言都提供开源或自带的框架,比如JAVA的Spring IOC、.NET的Autofac、.NET Core自带的依赖注入等。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?