Spring IOC&DI(上)
1. Spring IOC&DI
Spring 是包含了众多工具方法的 IOC 容器
1.1 容器
概念 :容器时用来容纳物品的装置。
例子:List/Map -> 数据存储容器;Tomcat -> Web 容器
1.2 IOC
概念 :全称:Inversion of Control(控制反转),是 Spring 的核心思想,把对象交给 Spring 管理,就是 IOC 思想。

总的来说,Spring 就是一个"控制反转"的容器。
2. IOC&DI 介绍
2.1 IOC 介绍
如果我们造一辆车
2.1.1 传统开发
思路:先设计轮子,然后根据轮子设计底盘,再根据底盘设计车身,再根据车身设计一辆整车。
所产生的依赖关系:

代码实现:
车:
java
public class Car {
private Framework framework;
public Car(int size) {
this.framework = new Framework(size);
System.out.println("car init...");
}
public void run() {
System.out.println("car run...");
}
}
车身:
java
public class Framework {
private Bottom bottom;
public Framework(int size) {
this.bottom = new Bottom(size);
System.out.println("framework init...");
}
}
底盘:
java
public class Bottom {
private Tire tire;
public Bottom(int size) {
this.tire = new Tire(size);
System.out.println("bottom init...");
}
}
轮胎:
java
public class Tire {
private int size;
public Tire(int size) {
this.size = size;
System.out.println("tire init...,size:" + size);
}
}
2.1.2 问题分析
代码可维护性低,假如我们想要对轮子的颜色也有要求,就需要对上层的程序也进行修改。



当底层代码改动后,整个调用链上的所有代码都需要修改。
2.1.3 IOC 程序开发
如果反正设计车辆,依赖关系就会倒置,轮子依赖底盘,底盘依赖车身,车身依赖汽车。

代码实现:
主程序:
java
public class Main {
public static void main(String[] args) {
Tire tire = new Tire(21);
Bottom bottom = new Bottom(tire);
Framework framework = new Framework(bottom);
Car car = new Car(framework);
car.run();
}
}
车:
java
public class Car {
private Framework framework;
public Car(Framework framework) {
this.framework = framework;
}
public void run() {
System.out.println("car run...");
}
}
车身:
java
public class Framework {
private Bottom bottom;
public Framework(Bottom bottom) {
this.bottom = bottom;
System.out.println("framework init...");
}
}
底盘:
java
public class Bottom {
private Tire tire;
public Bottom(Tire tire) {
this.tire = tire;
System.out.println("bottom init...");
}
}
轮胎:
java
public class Tire {
private int size;
public Tire(int size) {
this.size = size;
System.out.println("tire init...,size:" + size);
}
}
代码经过以上调整,无论底层类如何变化,整个调用链不用做任何变化,这样就完成了代码之间的解耦。
2.1.4 IOC 的优势
资源不由使用双方管理,而由不使用资源的第三方管理的优势在于资源集中管理,实现了资源的可配置和易管理,降低了使用双方的依赖程度。

2.2 DI 介绍
概念 :全称:Dependency Injection(依赖注入),容器在运行期间,动态的为应用程序提供运行时所依赖的资源,称之为依赖注入。
上述代码时通过构造函数的方式,把依赖对象注入到需要使用的对象中的

IOC是一种思想,DI 就属于具体的实现,DI 是 IOC 的一种实现。
3. IOC & DI 使用
Spring 是一个 IOC 容器,作为容器,那么它就具备两个最基础的功能:存和取,Spring 容器管理的主要是对象,这些对象称为"Bean",并且交由 Spring 管理,由 Spring 来负责创建和销毁。
我们利用图书管理系统代码来展示 IOC & DI 的使用
java
@Repository
public class BookDao {
public List<BookInfo> mockData() {
List<BookInfo> bookInfos = new ArrayList<>();
for(int i = 1;i <= 15;i++) {
BookInfo bookInfo = new BookInfo();
bookInfo.setId(i);
bookInfo.setBookName("图书" + i);
bookInfo.setBookAuthor("作者" + i);
bookInfo.setCount(new Random().nextInt(100));
bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));
bookInfo.setPublish("出版社" + i);
bookInfo.setStatus(i%5==0?2:1);
bookInfos.add(bookInfo);
}
return bookInfos;
}
}
java
@Service
public class BookService {
@Autowired
private BookDao bookDao;
public List<BookInfo> getList() {
List<BookInfo> bookInfos = bookDao.mockData();
for(BookInfo bookInfo : bookInfos) {
if(bookInfo.getStatus() == 1) {
bookInfo.setStatusCN("可借阅");
} else {
bookInfo.setStatusCN("不可借阅");
}
}
return bookInfos;
}
}
java
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
private BookService bookService;
@RequestMapping("/getList")
public List<BookInfo> getList() {
List<BookInfo> bookInfos = bookService.getList();
return bookInfos;
}
}
运行程序

4. IOC 详解
IOC 控制反转就是将对象的控制权交给 Spring 的 IOC 容器,由 IOC 容器创建及管理对象,就是 Bean 的存储。
4.1 Bean 的存储
把对象交给 IOC 容器管理,需要两类注解
(1)类注解:@Controller、@Service、@Repository、@Component、@Configuration
(2)方法注解:@Bean
4.1.1 @Controller
使用 @Controller 存储 bean 代码
java
@Controller
public class UserController {
public void sayHello() {
System.out.println("hello");
}
}
从 Spring 容器中获取对象
ConfigurableApplicationContext 类继承了 ApplicationContext 类,ApplicationContext 类 又继承了BeanFactory类,BeanFactory 类提供了大量获取 bean 的方法


java
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
UserController userController = context.getBean(UserController.class);
userController.sayHello();
}
}
观察运行结果,发现成功从 Spring 中获取到 Controller 对象,并执行 Controller 的 sayHello 方法

如果把 @Controller 删掉,再观察运行结果

报错信息显示:找不到类型是:com.example.demo.controller.UserController 的 bean
获取 bean 对象的其他方式
ApplicationContext 也提供了其他获取 bean 的方式,ApplicationContext 获取 bean 对象的功能,是父类 BeanFactory 提供的功能

Bean 命名的约定
官方文档:https://docs.spring.io/spring-framework/reference/core/beans/definition.html#beans-beanname

命名约定使用 Java 标准约定作为实例字段名,bean 名称以小写字母开头,然后使用驼峰式大小写。当有多个字符并且第一个和第二个字符都是大写时,将保留原始的大小写。
根据这个命名规则,我们来获取 bean
java
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
UserController userController1 = context.getBean(UserController.class);
UserController userController2 = (UserController) context.getBean("userController");
UserController userController3 = context.getBean("userController", UserController.class);
System.out.println(userController1);
System.out.println(userController2);
System.out.println(userController3);
}
}
运行结果

4.1.2 @Service
使用 @Service 存储 bean 的代码
java
@Service
public class UserService {
public void sayHi() {
System.out.println("Hi");
}
}
读取 bean 代码
java
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
UserService userService = context.getBean(UserService.class);
userService.sayHi();
}
}
观察结果,发现成功从 Spring 中获取到 UserService 对象,并执行了 sayHi 方法

4.1.3 @Repository
使用 @Repository 存储 bean 的代码
java
@Repository
public class UserRepository {
public void sayHi() {
System.out.println("Hi");
}
}
读取 bean 代码
java
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
UserRepository userRepository = context.getBean(UserRepository.class);
userRepository.sayHi();
}
}
运行结果

4.1.4 @Component
使用 @Component 存储 bean
java
@Component
public class UserComponent {
public void sayHi() {
System.out.println("Hi");
}
}
读取 bean 代码
java
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
UserComponent userComponent = context.getBean(UserComponent.class);
userComponent.sayHi();
}
}
运行结果:

4.1.5 @Configuration
使用 @Configuration 存储 bean 的代码
java
@Configuration
public class UserConfiguration {
public void sayHi() {
System.out.println("Hi");
}
}
读取 bean 代码
java
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);
userConfiguration.sayHi();
}
}
运行结果

4.2 类注解的关系
@Controller:控制层,接收请求,对请求进行处理,并进行响应。
@Service:业务逻辑层,处理具体的业务逻辑。
@Repository:数据访问层,负责数据访问操作。
@Configuration:配置层,处理项目中的一些配置信息。



查看@Controller、@Service、@Repository、@Configuration 等注解的源代码发现,这些注释里面都有一个 @Component 注解,说明他们本身都属于 @Component 的子类,@Controller、@Service、@Repository、@Configuration 被称为衍生注解。