Spring控制反转(IOC)
一、控制反转(百度百科定义)
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
简而言之就是,创建对象的权利,或者是控制的位置,由JAVA代码转移到spring容器,由spring的容器控制对象的创建,就是控制反转,spring创建对象时,会读取配置文件中的信息,然后使用反射给我们创建好对象之后在容器中存储起来,当我们需要某个对象时,通过id获取对象
即可,不需要我们自己去new.一句话:创建对象交给容器
二、技术描述
Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式地用 new 创建 B 的对象。
采用依赖注入技术之后,A 的代码只需要定义一个 private 的B对象,不需要直接 new 来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。
三、实现方法
实现控制反转主要有两种方式:依赖注入和依赖查找。两者的区别在于,前者是被动的接收对象,在类A的实例创建过程中即创建了依赖的B对象,通过类型或名称来判断将不同的对象注入到不同的属性中,而后者是主动索取相应类型的对象,获得依赖对象的时间也可以在代码中自由控制。
1.依赖注入
依赖注入有如下实现方式:
- 基于接口。实现特定接口以供外部容器注入所依赖类型的对象。
- 基于 set 方法。实现特定属性的public set方法,来让外部容器调用传入所依赖类型的对象。
- 基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。
- 基于注解。基于Java的注解功能,在私有变量前加“@Autowired”等注解,不需要显式的定义以上三种代码,便可以让外部容器传入对应的对象。该方案相当于定义了public的set方法,但是因为没有真正的set方法,从而不会为了实现依赖注入导致暴露了不该暴露的接口(因为set方法只想让容器访问来注入而并不希望其他依赖此类的对象访问)。
2.依赖查找
依赖查找更加主动,在需要的时候通过调用框架提供的方法来获取对象,获取时需要提供相关的配置文件路径、key等信息来确定获取对象的状态
四、案例演示
1.创建maven项目,为空项目
2.名字可以是spring_all
3.在项目下创建模块 名为spring5_test01
4.pom.xml中导入spring5依赖
核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring表达式语言,Spring Expression Language)等模块组成,它们的细节如下:
-
spring-core模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。
-
spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
-
context模块建立在由core和 beans 模块的基础上建立起来的,它以一种类似于JNDI注册的方式访问对象。Context模块继承自Bean模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过Servelet容器)等功能。Context模块也支持Java EE的功能,比如EJB、JMX和远程调用等。ApplicationContext接口是Context模块的焦点。
-
spring-context-support提供了对第三方库集成到Spring上下文的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。(这个暂时用不到就没有导入)
-
spring-expression模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中定义的统一表达式语言的扩展,支持set和get属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等。
但是事实上,我们导入spring-context的时候,会自动导入其他依赖的jar,自动进行了依赖传递,所以,导入一个spring-context 依赖也可以,依赖传递关系图如下
为了方便测试,我们导入Junit5测试依赖,以及设置了关于xml文件、properties文件的读取
<dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.22</version> </dependency> <!--配置和加载测试计划的公共API – 典型的使用场景是IDE和构建工具--> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-launcher</artifactId> <version>1.7.0</version> <scope>test</scope> </dependency> <!--JUnit Jupiter测试引擎的实现,仅仅在运行时需要。--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency> <!--JUnit Vintage测试引擎实现,允许在新的JUnit Platform上运行低版本的JUnit测试,即那些以JUnit 3或JUnit 4风格编写的测试。 非必须 --> <dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency> <!--编写测试 和 扩展 的JUnit Jupiter API。--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.7.0</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> </resources> </build>
5.在项目中定义一个接口和实现类
创建service包下创建接口 EmpService,代码如下:
public interface EmpService { int addEmp(); }
在service下创建子包 Impl,在其中创建 EmpServiceImpl实现上面的EmpService中的接口,代码如下
package com.augus.service.impl; import com.augus.service.EmpService; public class EmpServiceImpl implements EmpService { @Override public int addEmp() { System.out.println("=================addEmp==============="); return 1; } }
6.创建spring配置文件
7.文件名没有明确要求,暂时可以叫SpringConfig
8.在spring.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="empServer" class="com.augus.service.impl.EmpServiceImpl"/> </beans>
9.测试通过容器获取对象
创建测试类Test1,代码如下
public class Test1 { @Test public void testGetBean(){ // 获取容器对象,读取springconfig.xml文件,实例化配置文件中 配置的对象后,自动放入容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("SpringConfig.xml"); // 通过id从容器中获取 对象 // EmpServerImpl.class 指明获取的对象是那个类的产生的对象 EmpServiceImpl empService = context.getBean("empService", EmpServiceImpl.class); //调用方法 empService.addEmp(); } }
执行结果如下: