【Spring】Spring IOCDI:架构旋律中的“依赖交响”与“控制华章”
前言
🌟🌟本期讲解关于Spring IOC&DI的详细介绍~~~
🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客
🔥 你的点赞就是小编不断更新的最大动力
🎆那么废话不多说直接开整吧~~
目录
📚️1.IOC&DI入门
1.1什么是Spring
1.2什么是IOC
1.3IOC的介绍
1.3.1传统的开发过程
1.3.2IOC程序开发
1.3.3IOC的优势
1.4DI的介绍
📚️2.IOC&DI的使用
📚️3.IOC详解
3.1Bean的存储
3.2类注解实现
3.3获取Bean的其他方式
3.4多种注解含义
3.5方法注解Bean
3.5.1方法注解要配合注解类
3.5.2存在多个对象
📚️4.DI详解
4.1属性的注入
4.2构造方法注入
4.3setter方式注入
4.4几种方式的优缺
4.5@Autowired的缺点
4.5.1使用@Primary
4.5.2使用@Qualifier
4.5.3使用@Resource
📚️5.总结
📚️1.IOC&DI入门
1.1什么是Spring
通过前⾯的学习, 我们知道了Spring是⼀个开源框架, 他让我们的开发更加简单. 他⽀持⼴泛的应⽤场景, 有着活跃⽽庞⼤的社区, 这也是Spring能够⻓久不衰的原因.但是这个概念相对来说, 还是⽐较抽象.
我们⽤⼀句更具体的话来概括Spring, 那就是: Spring 是包含了众多⼯具⽅法的 IoC 容器
1.2什么是IOC
在前⾯讲到, 在类上⾯添加 @RestController 和@Controller 注解, 就是把这个对象交给Spring管理, Spring 框架启动时就会加载该类. 把对象交给Spring管理, 就是IoC思想
IOC:: Inversion of Control (控制反转), 也就是说 Spring 是⼀个"控制反转"的容器
控制反转:
当需要某个对象时, 传统开发模式中需要⾃⼰通过 new 创建对象, 现在不需要再进⾏创建, 把创建对象的任务交给容器, 程序中只需要依赖注⼊ (Dependency Injection,DI)就可以了
总结:
IOC就是一个创建任务对象的容器;
DI就是我们后面需要注入的依赖
1.3IOC的介绍
1.3.1传统的开发过程
假如我们在实现创建一个车子的时候,我们知道他是依赖于车身,轮胎,底盘...那么就有如下的依赖的情况:
那么我们在程序实现的时候就是如下所示的:
public class Car {
//车依赖于车身
private Framwork framwork;
public Car(int size) {
framwork = new Framwork(size);
System.out.println("Car init....");
}
public void run(){
System.out.println("car run....");
}
}
解释:
这就是车在制造的时候,依赖车身,所以就会创建一个车身的对象,那但是此时车身依赖由于底盘,就会在车身类进行对象的构造;
public class Framwork {
private Bottem bottem;
public Framwork(int size){
bottem=new Bottem(size);
System.out.println("framwork init....");
}
}
解释:
此时我们就会发现,车身的制造依赖于底盘,那么就要创建底盘的对象,那么此时一直到最后一环指定就是轮胎;
public class Tire {
private int size;
public Tire(int size){
this.size=size;
System.out.println("tire init...");
}
}
解释:
如果这里我们这里,最后一环的情况下,假如我们要规定颜色和轮胎的尺寸大小,此时就会发现一个问题;
以上程序的问题是:当最底层代码改动之后,整个调⽤链上的所有代码都需要修改.程序的耦合度⾮常⾼(修改⼀处代码, 影响其他处的代码修改)
1.3.2IOC程序开发
此时我们就可以使用IOC程序开发的思想进行改进,将整个对象进行统一管理,然后就可以大大降低耦合性;
public static void main(String[] args) {
Tire tire = new Tire(20);
Bottom bottom = new Bottom(tire);
Framework framework = new Framework(bottom);
Car car = new Car(framework);
car.run();
}
static class Car {
private Framework framework;
public Car(Framework framework) {
this.framework = framework;
System.out.println("Car init....");
}
public void run() {
System.out.println("Car run...");
}
}
解释:
这里小编只展示了一小部分,其余的写法基本是一致的;代码经过以上调整,⽆论底层类如何变化,整个调⽤链是不⽤做任何改变的,这样就完成了代码之间的解耦,从⽽实现了更加灵活、通⽤的程序设计了
1.3.3IOC的优势
在传统的代码中对象创建顺序是:Car -> Framework -> Bottom -> Tire
改进之后解耦的代码的对象创建顺序是:Tire -> Bottom -> Framework -> Car
改进之后的控制权发⽣的反转,不再是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由当前类控制了
那么此时就可以看做是如下所示的情况:
此时优点就是如下:
1. 资源集中管理: IoC容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC容器中去取就可以了
2. 我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度, 也就是耦合度.
1.4DI的介绍
DI: Dependency Injection(依赖注⼊)
容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。程序运⾏时需要某个资源,此时容器就为其提供这个资源.
就是一个依赖的注入;
IoC 是⼀种思想,也是"⽬标", ⽽思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽ DI 就属于具体的实现。所以也可以说, DI 是IoC的⼀种实现
📚️2.IOC&DI的使用
既然 Spring 是⼀个 IoC(控制反转)容器,作为容器, 那么它就具备两个最基础的功能:
• 存
• 取
Spring 容器 管理的主要是对象, 这些对象, 我们称之为"Bean". 我们把这些对象交由Spring管理, 由
Spring来负责对象的创建和销毁. 我们程序只需要告诉Spring, 哪些需要存, 以及如何从Spring中取出对象
此时我们就会使用@component来进行管理Bean,使用@Autowired来进行依赖的注入,那么代码如下:
@Component
public class BookDao {
public List<BookInfo> mockData(){
List<BookInfo> books=new ArrayList<>();
for (int i = 0; i < 5; i++) {
BookInfo book = new BookInfo();
book.setId(i);
book.setBookName("书籍" + i);
book.setAuthor("作者" + i);
book.setCount(i * 5 + 3);
book.setPrice(new BigDecimal(new Random().nextInt(100)));
book.setPublish("出版社" + i);
book.setStatus(1);
books.add(book);
}
return books;
}
}
我们将这个数据对象交给spring管理,就是加上注解@Component;
@Component //交给spring进行管理
public class BookService {
@Autowired
private BookDao bookDao;
public List<BookInfo> getBookList(){
List<BookInfo> books=bookDao.mockData();
for (BookInfo book : books) {
if (book.getStatus() == 1) {
book.setStatusCN("可借阅");
} else {
book.setStatusCN("不可借阅");
}
}
return books;
}
}
这里就是将逻辑处理的对象交给spring管理,由于这里要使用数据,所以要引入依赖,这个依赖就可以将数据的对象传过来;
@RequestMapping("/book")
@RestController
public class BookController {
@Autowired
private BookService bookService;
@RequestMapping("/getList")
public List<BookInfo> getList() {
//获取数据
//交给spring注入依赖
List<BookInfo> books = bookService.getBookList();
//处理⻚⾯展⽰
return books;
}
}
最后在controller表现层操作时,将引入逻辑处理层的对象,此时就可以spring管理的对象拿出来,就是依赖的引入;
📚️3.IOC详解
通过上⾯的案例, 我们已经知道了Spring IoC 和DI的基本操作, 接下来我们来系统的学习Spring IoC和DI的操作.前⾯我们提到IoC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。也就是bean的存储
3.1Bean的存储
在之前的⼊⻔案例中,要把某个对象交给IOC容器管理,需要在类上添加⼀个注解:@Component⽽Spring框架为了更好的服务web应⽤程序, 提供了更丰富的注解
1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration.
2. ⽅法注解:@Bean.
那么下面小编来示范一下;
3.2类注解实现
这里小编就只使用controller进行操作,通过controller注解来进行存储Bean;
代码如下所示:
@Controller
public class UserController {
public void userController(){
System.out.println("userController start...");
}
}
此时我们就将这里的对象给spring进行管理了;
如何从Spring容器中获取对象:
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
//获取Spring上下⽂对象
ApplicationContext context =
SpringApplication.run(SpringIocDemoApplication.class, args);
//从Spring上下⽂中获取对象
UserController userController = context.getBean(UserController.class);
//使⽤对象
userController.sayHi();
}
}
然后我们将这个controller进行删除试试:
此时就是出现异常了:就是这不到这个类型对象的存在,所以就抛出了异常;
3.3获取Bean的其他方式
上述就是通过类型来进行获取Bean的;
上述代码是根据类型来查找对象, 如果Spring容器中, 同⼀个类型存在多个bean的话, 怎么来获取呢?
ApplicationContext 也提供了其他获取bean的⽅式, ApplicationContext 获取bean对象的功能, 是⽗类BeanFactory提供的功能.
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
可以看到这里可以根据类型和名字进行对象的获取,那么Bean的名字是啥?
Spring bean是Spring框架在运⾏时管理的对象, Spring会给管理的对象起⼀个名字,给每个对象起⼀个名字, 根据Bean的名称(BeanId)就可以获取到对应的对象.
Bean的命名
官方的文档就是如下所示:
具体的内容就是:
命名约定使⽤Java标准约定作为实例字段名. 也就是说,bean名称以⼩写字⺟开头,然后使⽤驼峰式⼤⼩写 ;
类名: UserController, Bean的名称为: userController
类名: AccountManager, Bean的名称为: accountManager
当然也有特殊的情况,前面两个单词都是大写:
类名: UController, Bean的名称为: UController
类名: AManager, Bean的名称为: AManager
还有就是三个大写字母,的Bean的命名就是:
类名:UserControllerDI,Bean的名称;usercontrollerDI
此时我们使用三种获取Bean的方式进行获取Bean对象:
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(SpringIocApplication.class, args);
UserController bean = context.getBean(UserController.class);
bean.userController();
UserController bean1 = (UserController) context.getBean("userController");
bean1.userController();
UserController bean2 = context.getBean("userController",UserController.class);
bean2.userController();
}
解释:
此时我们这里第一种获取Bean的方式就是通过类型进行获取,然后第二种就是通过bean的名称进行获取,第三种就是前面两种的合并的方式;
此时我们进入运行状态:
ApplicationContext与BeanFactory
继承关系和功能⽅⾯来说:Spring 容器有两个顶级的接⼝:BeanFactory 和ApplicationContext。其中 BeanFactory 提供了基础的访问容器的能⼒,⽽ApplicationContext 属于 BeanFactory 的⼦类,它除了继承了 BeanFactory 的所有功能之外,它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持
从性能⽅⾯来说:ApplicationContext 是⼀次性加载并初始化所有的 Bean 对象,⽽
BeanFactory 是需要那个才去加载那个,因此更加轻量. (空间换时间
这里小编就不再演示其他几个了,基本代码和思想都是一样的~~~
3.4多种注解含义
• @Controller:控制层, 接收请求, 对请求进⾏处理, 并进⾏响应.
• @Servie:业务逻辑层, 处理具体的业务逻辑.
• @Repository:数据访问层,也称为持久层. 负责数据访问操作
• @Configuration:配置层. 处理项⽬中的⼀些配置信息
类注解之间的关系查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发
现:
这些注解⾥⾯都有⼀个注解 @Component ,说明它们本⾝就是属于 @Component 的"⼦类".
@Component 是⼀个元注解,这些其他的注解就是@Component的衍生类;
3.5方法注解Bean
1.使⽤外部包⾥的类, 没办法添加类注解
2. ⼀个类, 需要多个对象, ⽐如多个数据源
3.5.1方法注解要配合注解类
代码如下所示:
@Configuration
public class BeanConfig {
@Bean
public UserInfo userInfo(){
return new UserInfo("zhangsan");
}
}
那么此时就将这个对象进行了管理了;
那么我们就可以拿到这个对象:
UserInfo bean = context.getBean("UserInfo.class");
System.out.println(bean);
3.5.2存在多个对象
代码如下:
@Configuration
public class BeanConfig {
@Bean
public UserInfo user1(){
return new UserInfo("zhangsan");
}
//@Primary
@Bean
public UserInfo user2(){
return new UserInfo("lisi");
}
}
那么此时我们使用类型进行获取时候;
那么此时我们就要通过bean的名称来进行获取了;
User user1 = (User) context.getBean("user1");
User user2 = (User) context.getBean("user2")
📚️4.DI详解
依赖注⼊是⼀个过程,是指IoC容器在创建Bean时, 去提供运⾏时所依赖的资源,⽽资源指的就是对象.在上⾯程序案例中,我们使⽤了 @Autowired 这个注解,完成了依赖注⼊的操作
1. 属性注⼊(Field Injection)
2. 构造⽅法注⼊(Constructor Injection)
3. Setter 注⼊(Setter Injection)
4.1属性的注入
代码如下:
@Controller
public class UsercontrollerDI {
@Autowired
private UserServiceDI userServicedi;
//private final UserServiceDI userServiceDI=new UserServiceDI();
public void start(){
userServicedi.start();
}
}
这里就是通过属性的注入的方式进行依赖的注入;
此时我们进行对象的获取和打印:
UsercontrollerDI bean3 = context.getBean(UsercontrollerDI.class);
bean3.start();
去掉@Autowired , 再运⾏⼀下程序看看结果:
就是说明了这里由于没有注入依赖,那么就直接为空了(这个对象)
4.2构造方法注入
代码如下所示:
@Controller
public class UserControllerDI2 {
private UserServiceDI userServiceDI;
public UserControllerDI2(){
}
@Autowired
public UserControllerDI2(UserServiceDI userServiceDI){
this.userServiceDI=userServiceDI;
}
public void start(){
userServiceDI.start();
}
}
解释:
这就是通过构造方法来进行操作的,当然这里有点问题就是当存在两个构造方法的时候,默认就是无参的构造方法;
只有一个构造方法的时候,默认是使用这一个
若存在两个构造方法,此时就要指定使用那个构造方法
4.3setter方式注入
这里就和get与set方式基本一致了:
@Controller
public class UserSet {
private UserServiceDI userServiceDI;
@Autowired
public void setUserServiceDI(UserServiceDI userServiceDI) {
this.userServiceDI = userServiceDI;
}
public void start(){
userServiceDI.start();
}
}
这里小编就不在过多的解释了;
4.4几种方式的优缺
属性注入
◦ 优点: 简洁,使⽤⽅便;
◦ 缺点:
▪ 只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指
针异常)
▪ 不能注⼊⼀个Final修饰的属性
构造函数注入
优点:
▪ 可以注⼊final修饰的属性
▪ 注⼊的对象不会被修改
▪ 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法
是在类加载阶段就会执⾏的⽅法.
▪ 通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
◦ 缺点:
▪ 注⼊多个对象时, 代码会⽐较繁琐
setter方式注入
优点: ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
◦ 缺点:
▪ 不能注⼊⼀个Final修饰的属性
▪ 注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险
4.5@Autowired的缺点
当出现多个同种类型的对象的时候:
public class BeanConfig {
@Bean
public UserInfo userInfo(){
return new UserInfo("zhangsan");
}
//@Primary
@Bean
public UserInfo userInfo1(){
return new UserInfo("lisi");
}
}
此时我们进行注入,就会出现如下的情况:
@Autowired
private UserInfo user;
public void start(){
System.out.println(user);
}
直接出错;
很明显就是分不清楚,那个是要注入的Bean;
那么就有下面几种方法;
4.5.1使用@Primary
代码如下:
@Bean
public UserInfo userInfo(){
return new UserInfo("zhangsan");
}
@Primary
@Bean
public UserInfo userInfo1(){
return new UserInfo("lisi");
}
此时我们默认这个对象;
4.5.2使用@Qualifier
注意这里要和autowired进行搭配使用
@Qualifier("userInfo1")
@Autowired
private UserInfo user;
这里就要指定那个;
4.5.3使用@Resource
这里和上面一样,只不过不用搭配@Autowired了;
@Sesource("userInfo1")
private UserInfo user;
•@Autowird 与 @Resource的区别
• @Autowired 是spring框架提供的注解,⽽@Resource是JDK提供的注解
• @Autowired 默认是按照类型注⼊,⽽@Resource是按照名称注⼊. 相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean.
📚️5.总结
本期小编主要讲解了关于Spring IOC和DI的详细介绍,从入门讲解概念到实际的应用,都有涉及,当然其中还涉及比较重要的面试题哦~~~
🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!
💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。
😊😊 期待你的关注~~~