IoC控制反转
# IoC控制反转
在面向对象编程开发中,对象的创建和管理方式是实际开发中要面临的头部问题。Spring框架提供了IoC容器作为解决方案。
IoC是创建和管理对象的容器,因此你可以将IoC理解为Spring全家桶各个功能模块的基础。
IoC(控制反转),将对象的创建进行反转,常规情况下,对象都是开发者手动创建的
使用 IoC 开发者不再需要创建对象,而是由 IoC 容器根据需求自动创建项目所需要的对象。
- 不用 IoC:所有对象开发者自己创建
- 使用 IoC:对象不用开发者创建,而是交给 Spring 框架来完成
# 使用Spring IoC
用idea创建新的maven工程之后
# pom文件引入Spring context依赖
这一步是为了配置IoC的环境
Spring context(上下文)里面就包含了IoC的依赖了,除了IoC,还有aop,bean,context,core,expression,jcl依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.15</version>
</dependency>
2
3
4
5
为了方便后面的开发,顺便引入lombok插件,来自动生成getter,setter方法,减少开发量。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
2
3
4
5
# 创建实体类DataConfig
package com.hincky.ioc;
import lombok.Data;
@Data
public class DataConfig {
private String url;
private String driverName;
private String userName;
private String password;
}
2
3
4
5
6
7
8
9
10
# 创建测试类Test
package com.hincky.ioc;
public class Test {
public static void main(String[] args) {
//不用IoC,就要像下面这样,所有对象的信息,都要自己固定的写死。
//且对像数量多的话,又不方便又冗余
DataConfig dc = new DataConfig();
dc.setDriverName("Driver");
dc.setUrl("localhost:8080");
dc.setUserName("root");
dc.setPassword("root");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
环境搭建好了之后,就可以开始正式使用IoC了
IoC有两种使用方式
- 基于XML配置bean
- 基于注解配置bean
# 基于XML配置
这种方式,现在看来十分麻烦,已经不多用了
IoC基于XML配置bean对象:
- 开发者把需要的对象在 XML 中进行配置
- Spring框架读取这个配置文件,根据配置文件的内容来创建对象
# 创建xml配置文件
resources文件夹下创建
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean class="com.hincky.ioc.DataConfig" id="config">
<property name="driverName" value="Driver"></property>
<property name="url" value="localhost:8080"></property>
<property name="userName" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 读取xml文件中bean对象配置
回到Test测试类中,注释掉原来手写的bean配置信息。 下面通过创建IoC容器读取xml文件中bean对象的配置
// DataConfig dc = new DataConfig();
// dc.setDriverName("Driver");
// dc.setUrl("localhost:8080");
// dc.setUserName("root");
// dc.setPassword("root");
ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
System.out.println(context.getBean("config"));
2
3
4
5
6
7
8
9
这种基于xml文件的配置方式,底层原理就是xml解析+反射
# 基于注解配置
基于注解配置对象bean中,有两种常用的配置方式:
- 配置类
- 扫描包+注解
# 配置类
原理和xml的方式类似,就是配置bean对象的形式变为了java类和方法,不是xml文件了
# 创建配置类和bean的配置方法
package com.hincky.ioc.config;
import com.hincky.ioc.DataConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DataConfigConfig {
// @Bean(value = "config")
@Bean
public DataConfig dataConfig() {
DataConfig dataConfig = new DataConfig();
dataConfig.setDriverName("Driver");
dataConfig.setUrl("localhost:8080");
dataConfig.setUserName("root");
dataConfig.setPassword("root");
return dataConfig;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 读取配置类中bean的配置方法
回到Test测试类中,注释掉之前的代码。
下面通过创建IoC容器读取配置类中bean对象的配置方法。
当配置类很多时,逐个引入不方便;通过扫描包的方式,将特定包下所有的带有注解的配置类加入到IoC容器中
// 注解:配置类。直接读取指定配置类
ApplicationContext context = new AnnotationConfigApplicationContext(DataConfigConfig.class);
//扫描包名下带有Configuration注解的配置类
// ApplicationContext context = new AnnotationConfigApplicationContext("com.hincky.ioc.config");
System.out.println(context.getBean("dataConfig"));
2
3
4
5
# 扫描包+注解
这是最最最常用的方式了,不再需要依赖于 XML 或者配置类
而是创建目标类的时候添加@component
注解加入到IoC容器
# component注解将bean加入IoC
回到一开始创建的实体类中,通过@Value
配置bean的属性值
@Data
@Component
public class DataConfig {
@Value("localhost:8080")
private String url;
@Value("Driver")
private String driverName;
@Value("root")
private String userName;
@Value("root")
private String password;
}
2
3
4
5
6
7
8
9
10
11
12
# 读取指定的包
ApplicationContext context = new AnnotationConfigApplicationContext("com.hincky.ioc");
System.out.println(context.getBean(DataConfig.class));
2
# bean对象之间调用
比如创建一个GlobalConfig对象,里面调用DataConfig
这就需要IoC在创建GlobalConfig的时候,将DataConfig注入进来。
# Autowired类型注入
实现方式:利用@Autowired
注解进行自动装载,自动去IoC容器里面找被@Autowired
修饰的bean类型
@Data
@Component
public class GlobalConfig {
@Value("80")
private String port;
@Value("com.hincky")
private String path;
@Autowired //自动去IoC容器里面找DataConfig的bean
private DataConfig dataConfig;
}
2
3
4
5
6
7
8
9
10
Test类读取一下GlobalConfig验证是否注入成功
ApplicationContext context = new AnnotationConfigApplicationContext("com.hincky.ioc");
System.out.println(context.getBean(GlobalConfig.class));
2
# Qualifier名字注入
因为@Autowired
注解是通过类型进行匹配的,如果要通过名字进行匹配,就要用@Autowired
+@Qualifier
注解
@Data
@Component
public class GlobalConfig {
@Value("80")
private String port;
@Value("com.hincky")
private String path;
@Autowired
@Qualifier("config")
private DataConfig dataConfig;
}
2
3
4
5
6
7
8
9
10
11
同时,被调用对象类上@Component
的名字也要一致
@Data
@Component("config")
public class DataConfig {
@Value("localhost:8080")
private String url;
@Value("Driver")
private String driverName;
@Value("root")
private String userName;
@Value("root")
private String password;
}
2
3
4
5
6
7
8
9
10
11
12
# 总结IoC
- xml方式:
- 写xml配置类
- 创建Ioc容器,使用
ClassPathXmlApplicationContext
- 读取bean对象
- 注解-配置类方式:
- 创建bean的java配置类,并在类上加
@Configuration
注解 - 配置类里创建方法,这个方法对应一个bean对象。所以要在方法上加
@Bean
注解 - 创建IoC容器,使用
AnnotationConfigApplicationContext
- 可以直接指定某个配置类
- 也可以指定某个包名,Ioc容器会去扫描带有
@Configuration
注解的配置类,并将其加入IoC容器中
- 读取bean对象
- 默认是读取配置类里面bean方法的方法名。
- 如果要自定义名字,就要改方法上注解为
@Bean(value="config")
- 也可以通过
方法.class
去读取bean对象,比如上面就是context.getBean(Dataconfig.class)
- 注解-扫描包+注解方式:
- 实体类上添加
@Component
注解,将其添加到IoC容器 - 实体类里面通过
@Value
配置bean的属性值
- 对象之间的调用
- 调用对象和被调用对象通过
@Component
注解,注入到IoC容器中 - 被调用对象在调用对象的类里面,要用
@Autowired
注解进行自动装载@Autowired
是通过类型进行注入的,所以被调用对象在调用对象的类里面取什么名字都无所谓- 如果需要通过名字去获取被调用对象,就用
@Autowired
+@Qualifier
@Qualifier
设置的名字一定要和被调用对象类上@Component
的名字一致,不然找不到bean- 因为IoC里面不知道会不会有同名的其它类型bean,为了不挖坑,还是用类型去获取bean