目录
✨ 一、什么是IoC
1、定义 🎊
2、IoC思想 🎊
3、优势 🎊
4、对象的管理 🎊
存对象:@Component
取对象:@AutoWired
✨二、什么是DI
1、定义 🎊
2、IoC和DI的关系🎊
可以说DI是IoC的一种实现
✨三、IoC&DI的使用
1、实现实例:用@Component注解实现存 🎊
2、 使用@AutoWired注解实现从容器中取对象🎊
✨四、IoC详解
1、Bean的存储(类注解)🎊
1) @Controller(控制器存储)(控制层,接受请求,对请求进行处理,并进行响应)
从容器中获取对象:Spring上下文
2)@Service(服务存储)(业务逻辑层,处理具体的业务逻辑)
编辑
3)Repository(仓库存储)(数据访问层,也称为持久层,负责数据的访问操作)
4)Component(组件存储)
5)Configuration(配置存储)(配置层,处理项目中的一些配置信息)
2、Bean的命名🎊
3、获取bean对象的方法(是父类(BeanFactory)提供的功能)🎊
常见的面试题: ApplicationContext VS BeanFactory
4、方法注解(@Bean)🎊
1、一个对象的情况
2、多个对象的情况
3、重命名Bean
5、扫描路径🎊
✨五、DI详解
1、依赖注入的三种方法 🎊
1)属性注入(Field Injection)Autowired
2) 构造方法注入(Constructor Injection)
3)Setter注入(Setter Jniection)
2、三种注入的优缺点分析 🎊
3、@Autowired存在问题(当同一类型存在多个bean时)🎊
让我们先回顾一下什么是Spring:Spring是一个开源框架,他让我们的开发更简单,简单来说,Spring是包含了众多工具的IoC容器。
那么问题来了,什么是容器?什么是IoC容器?
容器是用来容纳某种物品的(基本)装置
- List/Map ->数据存储器
- Tomact -> Web容器
一、什么是IoC
1、定义
Inversion of Control(控制反转),也就是说“Spring”是一个控制反转容器。
当需要某个对象时,传统的开发模式中需要new来创建对象,现在不需要再进行创建了,把创建对象的任务交给容器,由Spring来负责对象的创建和销毁,程序只需要依赖注入(Dependency Injection,DI) 就可以了。
这个容器称为:IoC容器。Spring是一个IoC容器,有时候Spring也称为Spring容器。(容器里面装的是对象,Spring管理的对象称之为bean)
2、IoC思想
像我们之前写代码,在类上面添加@RestController和@Controller注解,就是把这个对象交给Spring来管理,Spring框架启动时就会加载该类,把对象交给Spring管理,就是IoC思想。
3、优势
资源不由使用资源的双方管理,而由不使用资源的第三方管理。
- 资源集中管理:IoC容器会帮我们管理一些资源(对象等),需要时,只需要从IoC容器中去取就可以了
- 我们在创建实例的时候不需要了解其中的细节,降低了使用资源双方的依赖程度,也就是耦合度。
4、对象的管理
-
存对象:@Component
-
取对象:@AutoWired
二、什么是DI
1、定义
Dependency Injection,DI(依赖注入),容器在运行期间,动态的为应用程序提供运行时所依赖的资源,称之为依赖注入。
2、IoC和DI的关系
可以说DI是IoC的一种实现
可以看出,依赖注入和控制反转是从不同的角度的描述同一件事情,就是指通过引入IoC容器,利用依赖关系注入的方式,实现对象之间的解耦。
三、IoC&DI的使用
1、实现实例:用@Component注解实现存
@Component
public class BookDao {
public List<BookInfo> mockBookData() {
List<BookInfo> bookInfos = new ArrayList<>();
for(int i = 1;i < 15; i++) {
BookInfo bookInfo = new BookInfo();
bookInfo.setId(i);
bookInfo.setBookName("图书" + i);
bookInfo.setAuthor("作者" + i);
bookInfo.setCount(i*15 + 3);
bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));
bookInfo.setPublish("出版社" + i);
bookInfo.setStatus(i%5==0?2:1);
bookInfos.add(bookInfo);
}
return bookInfos;
}
}
@Component
public class BookService {
/**
* 根据数据层返回的结果对数据进行处理
* @return
*/
//从Spring中获取对象
@Autowired
private BookDao bookDao;
public List<BookInfo> bookInfoList(){
List<BookInfo> bookInfos = bookDao.mockBookData();
for(BookInfo bookInfo: bookInfos) {
if(bookInfo.getStatus()==1) {
bookInfo.setStateCN("可借阅");
}else if(bookInfo.getStatus()==2){
bookInfo.setStateCN("不可借阅");
}
}
return bookInfos;
}
}
2、 使用@AutoWired注解实现从容器中取对象
@Autowired
private BookDao bookDao;
四、IoC详解
1、Bean的存储(类注解)
通过前面 学习,我们知道了把某个对象交给IoC容器进行管理,需要在类上添加一个注解,而Spring也提供了更丰富的注解。
类注解:@Controller、@Service(服务存储)、@Component(组件存储)、@Repository(仓库存储)、@Configuration(配置存储)
方法注解:@Bean
设置这么多类注解目的是为了让程序员看到类注解之后,就直接了当的了解当前类的用途
1) @Controller(控制器存储)(控制层,接受请求,对请求进行处理,并进行响应)
看下面例子:使用 @Controller存储bean的代码
package com.example.example1.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
public void sayHi() {
System.out.println("祝你好运,小李");
}
}
如何观察这个对象已经存在在Spring容器中了呢?
@SpringBootApplication
public class Example1Application {
public static void main(String[] args) {
//获取Spring上下文对象
ApplicationContext context = SpringApplication.run(Example1Application.class, args);
//从Spring上下文中获取对象
UserController userController = context.getBean(UserController.class);
//使用对象
userController.sayHi();
}
}
运行程序:
但如果删除@Controller,观察运行结果:
从容器中获取对象:Spring上下文
上下文:就是指当前的运行环境,也可以看做是一个容器,容器里面存了很多内容,这些内容是当前的运行的环境。
ApplicationContext context = SpringApplication.run(Example1Application.class, args);
2)@Service(服务存储)(业务逻辑层,处理具体的业务逻辑)
package com.example.example1.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void sayHi() {
System.out.println("祝你幸福快乐,小李");
}
}
//获取Spring上下文对象
ApplicationContext context = SpringApplication.run(Example1Application.class, args);
UserService userService = context.getBean(UserService.class);
userService.sayHi();
3)Repository(仓库存储)(数据访问层,也称为持久层,负责数据的访问操作)
4)Component(组件存储)
5)Configuration(配置存储)(配置层,处理项目中的一些配置信息)
2、Bean的命名
程序开发人员不需要为bean指定名称 ,如果没有显示的提供名称,Spring容器将为该Bean生成唯一的名称
来看spring官方文档中说明:Bean Overview :: Spring Framework
3、获取bean对象的方法(是父类(BeanFactory)提供的功能)
package com.example.example1;
import com.example.example1.controller.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Example1Application {
public static void main(String[] args) {
//获取Spring上下文对象
ApplicationContext context = SpringApplication.run(Example1Application.class, args);
//从Spring上下文中获取对象,根据类型
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);
}
}
常见的面试题: ApplicationContext VS BeanFactory
- 继承关系和功能方面来说:Spring容器有两个顶级的接口:BeanFactory和ApplicationContext。其中前者提供了基础的访问容器的能力,后者属于 BeanFactory的子类,他除了继承了BeanFactory的所有功能之外,它还具有独特的特性,还添加了对国际化支持、资源访问支持、以及事件传播等方面的支持。
- 性能方面:后者是一次性加载并初始化所有的Bean对象,而前者是需要哪个才去加载哪个,因此更加轻量(空间换时间)
4、方法注解(@Bean)
考虑以下场景:1、使用外部包里面的类,没办法添加类注解
2、一个类,需要多个对象,比如多个数据源
1、一个对象的情况
示例:
package com.example.example1.bean;
import lombok.Data;
import javax.lang.model.element.NestingKind;
@Data
public class User {
private String name;
private int age;
}
package com.example.example1.bean;
import org.springframework.context.annotation.Bean;
public class BeanConfig {
@Bean
public User user(){
User user = new User();
user.setName("zhangsan");
user.setAge(12);
return user;
}
}
User user = context.getBean(User.class);
System.out.println(user);
让我查看运行结果,结果报错了
什么原因造成的呢?
在Spring框架的设计中,方法注解@Bean要配合类注解才能将对象正常的存储到Spring容器中
对上述代码进行修改
package com.example.example1.bean; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public class BeanConfig { @Bean public User user(){ User user = new User(); user.setName("zhangsan"); user.setAge(12); return user; } }
再次运行:
2、多个对象的情况
从运行结果可以看出:@Bean注解的Bean,bean名称就是它的方法名
所以我们根据名称来获取bean对象
3、重命名Bean
@Bean(name = {"u3","user3"})
public User user3(){
User user = new User();
user.setName("wangwu");
user.setAge(14);
return user;
}
User u3 = (User) context.getBean("u3");
System.out.println(u3);
5、扫描路径
问题:使用前面学过的四个注解声明Bean,一定会生效吗?
答案是不一定的,bean要想生效,还需要被Spring扫描
- 通过@CompontentScan
@ComponentScan({"com.example.example1"})
五、DI详解
依赖注入是一个过程,是指IoC在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象,之前,我们使用@Autowired这个注解,完成了依赖注入的操作
简单来说,就是把对象取出来放到某个类属性中
1、依赖注入的三种方法
1)属性注入(Field Injection)Autowired
@Controller
public class UserController {
@Autowired
private UserService userService;
public void sayHi() {
System.out.println("祝你好运,小李");
userService.sayHi();
}
}
@Service
public class UserService {
public void sayHi() {
System.out.println("祝你幸福快乐,小李");
}
}
UserController userController = (UserController) context.getBean("userController");
userController.sayHi();
2) 构造方法注入(Constructor Injection)
package com.example.example1.controller;
import com.example.example1.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController2 {
private UserService userService;
@Autowired
public UserController2 (UserService userService) {
this.userService = userService;
}
public void sayHi() {
System.out.println("祝你好运,小李");
userService.sayHi();
}
}
注意:如果类只有一个构造方法,那注解可以省略,反之则不行
3)Setter注入(Setter Jniection)
2、三种注入的优缺点分析
• 属性注⼊ ◦:
优点: 简洁,使⽤⽅便;
◦ 缺点: ▪ 只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指 针异常) ▪ 不能注⼊⼀个Final修饰的属性
• 构造函数注⼊(Spring 4.X推荐)
◦ 优点: ▪ 可以注⼊final修饰的属性 ▪ 注⼊的对象不会被修改 ▪ 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法 是在类加载阶段就会执⾏的⽅法. ▪ 通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
◦ 缺点: ▪ 注⼊多个对象时, 代码会⽐较繁琐
• Setter注⼊(Spring 3.X推荐)
◦ 优点: ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
◦ 缺点: ▪ 不能注⼊⼀个Final修饰的属性 ▪ 注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险.
3、@Autowired存在问题(当同一类型存在多个bean时)
@Autowird 与 @Resource的区别
• @Autowired 是spring框架提供的注解,⽽@Resource是JDK提供的注解
• @Autowired 默认是按照类型注⼊,⽽@Resource是按照名称注⼊.
相⽐于 @Autowired 来说, @Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean。