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 传统开发
思路:先设计轮子,然后根据轮子设计底盘,再根据底盘设计车身,再根据车身设计一辆整车。
所产生的依赖关系:

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



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

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

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

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

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 代码
1@Controller 2public class UserController { 3 public void sayHello() { 4 System.out.println("hello"); 5 } 6} 7
从 Spring 容器中获取对象
ConfigurableApplicationContext 类继承了 ApplicationContext 类,ApplicationContext 类 又继承了BeanFactory类,BeanFactory 类提供了大量获取 bean 的方法


1@SpringBootApplication 2public class SpringIocDemoApplication { 3 4 public static void main(String[] args) { 5 ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args); 6 UserController userController = context.getBean(UserController.class); 7 userController.sayHello(); 8 } 9} 10
观察运行结果,发现成功从 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
1@SpringBootApplication 2public class SpringIocDemoApplication { 3 public static void main(String[] args) { 4 ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args); 5 UserController userController1 = context.getBean(UserController.class); 6 UserController userController2 = (UserController) context.getBean("userController"); 7 UserController userController3 = context.getBean("userController", UserController.class); 8 System.out.println(userController1); 9 System.out.println(userController2); 10 System.out.println(userController3); 11 } 12} 13
运行结果

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

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

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

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

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



查看@Controller、@Service、@Repository、@Configuration 等注解的源代码发现,这些注释里面都有一个 @Component 注解,说明他们本身都属于 @Component 的子类,@Controller、@Service、@Repository、@Configuration 被称为衍生注解。
《Spring IOC&DI(上)》 是转载文章,点击查看原文。
