Spring IoC &DI

Spring两大核心:1.IoC 2.DI

初始IoC & DI

基本概念

在 前面的学习中,我们知道了Spring是一个开源框架,他让我们的开发更加简单,他支持广泛的应用场景,我们用一句更具体的话来概括Spring,那就是:Spring是包含了众多工具的IoC容器

IoC:Inversion of Controller 控制权反转(对象的控制权发生了反转) 我们可以理解为为之前是谁用对象控制权就在谁手里 而现在是由Spring来控制

DI:Dependency Injection:依赖注入

容器在运行期间,动态的为应用程序提供运行时所依赖的资源,称之为依赖注入

Bean:表示对象,IoC容器存的就是Bean对象

我们可以认为:依赖注入(DI)和控制权反转(IoC)是从不同的角度描述同一件事,依赖注入是从应用程序的角度来描述,就是指通过引入IoC容器,.利用依赖关系注入的方式,实现对象之间的解耦

IoC是一种思想,也是"目标",而思想只是一种指导原则,最终还是要有可行的落地方案,而DI就属于具体的实现.所以也可以说,DI是IoC的一种实现

IoC & DI 使用

既然Spring是一个IoC(控制反转)容器,那么他就具备两个最基础的功能:1.存 2.取

Spring容器管理的主要对象,这些对象,我们称之为Bean 我们把这些对象交由Spring管理,由Spring负责对象的创建和销毁 我们程序只需要告诉Spring哪些需要存,以及如何从Spring中取出对象

这里我们要先知道两个注解:1.@Component 表示:把对象交给Spring管理 存

2.@Autowired 表示:从容器中获取对象并给属性赋值 取

IoC详解

IoC控制反转就是将对象的控制权交给Spring的IoC容器,由IoC容器创建以及管理对象 也就是Bean的存储

Bean的存储

两类注解:

1.类注解:@Controller(控制器存储) @Service(服务存储) @Repository(仓库存储) @Component (组件存储)@Configuration(配置存储)

2.方法注解:@Bean

类注解

java 复制代码
@Controller("aaa")
public class HController {
    public void print(){
        System.out.println("do Controller");
    }
}

如何观察这个对象已经存在Spring容器当中了呢?

接下来我们学习如何从Spring容器中获取对象

java 复制代码
@SpringBootApplication
public class SpringiocDemoApplication {
	public static void main(String[] args) {
   //获取Spring上下文对选哪个
		ApplicationContext context=SpringApplication.run(SpringiocDemoApplication.class, args);
  //从Spring上下文中获取对象
       HController bean = (HController) context.getBean("aaa");
  //使用对象
	   bean.print();

}

ApplicationContext 翻译过来就是Spring 上下文 ,因为对象都交给Spring管理了,所以获取对象要从Spring中获取,那么就要先得到Spring的上下文

如果我们删除掉注解@Controller再次运行就会观察到报错信息:no qualifying bean of type

获取bean对象有以下几种方式

1.// 根据bean名称获取bean

Object getBean(String var1) throws BeansException

2.//根据bean名称和类型来获取bean

<T> T getBean(String var1,class<T>var2)throws BeansException

3.//按bean名称和构造函数参数动态创建bean,只使用与具有原型(prototype)作用域的Bean

Object getBean(String var1, Object ... var2)throws BeansException

4.//根据类型获取bean

<T> T getBean(class<T> var1) throws BeansException

5.//按bean类型和构造函数参数动态创建bean,只使用与具有原型(prototype)作用域的Bean

<T> T getBean(class<T> var1,Object ... var2) throws BeansException

常用的是上述1,2,4中,这三种方式,获取到的bean是一样的

其中1,2都涉及到根据名称来获取对象,bean的名称是什么样呢?

Spring bean是Spring框架在运行时管理的对象,Spring会给管理的对象起一个名字

Bean命名约定

程序开发人员不需要为bean指定名称(beanId),如果没有显示的提供名称(beanId),Spring容器将为该bean生成唯一的名称

命名约定成使用Java标准约定作为实例字段名,也就是说bean以小写字母开头,然后使用驼峰式大小写

那么既然获取bean对象有不同的方式,这些不同的方式获取到的bean会是一个对象吗?

java 复制代码
ApplicationContext context=SpringApplication.run(SpringiocDemoApplication.class, args);
		//适合该类型的对象只有一个
		HelloController bean = context.getBean(HelloController.class);
		bean.print();

		
		HelloController helloController = new HelloController();
		helloController.print();

		HelloController bean2 = (HelloController) context.getBean("helloController");
		bean2.print();

		HelloController bean3 = context.getBean("helloController", HelloController.class);
		bean3.print();

		System.out.println(bean);
		System.out.println(bean2);
		System.out.println(bean3);

运行结果

地址一样,说明对象是一个

获取bean对象,是父类BeanFactory提供的功能

ApplicationContext vs BeanFactory

继承关系和功能方面来说:Spring容器有两个顶级的接口:BeanFactory和SpringApplication.其中BeanFactory提供了基础的访问容器的能力,而ApplicationContext属于BeanFactory的子类,他除了继承BeanFactory的所有功能以外,他还具有独特的特性,还添加了对国际化支持,资源访问支持以及事件传播等方面的支持

从性能方面来说:ApplicationContext是一次性加载并初始化所有的BeanFactory对象,而BeanFactory是需要那个才去加载那个,因此更加轻量(空间换时间)

剩下的四个类注解的大致用法是一样的,这里我们主要关注这几个类注解的不同

观察这些注解的源代码我们可以发现这些注解除了@Component都有@Component,我们可以理解其他的几个注解是@Component的衍生注解

@Controller:控制层 @Service:业务逻辑层

@Respository:数据层 @Configuration:配置层

@Component:组件层

在实际的应用过程中,这5个注解的边界没有那么清

这5个注解,@Controller不可以和其他注解替换,控制层必须使用@Controller 在不固定的场景下,控制层使用@Service也能访问成功,但不标准,也不稳定,其他注解可以替换,但我们不建议(因为不同注解表示的含义不同)

方法注解@Bean

类注解是添加到某个类上的,但是存在两个问题:1.使用外部包的类,没办法添加类注解

2.一个类,需要多个对象,比如多个数据源

这种情况下,我们需要使用方法注解@Bean

当如果只添加@Bean是无法获取到对象的,在Spring框架的设计中,方法注解@Bean要配合类注解才能将对象正常的存储到Spring容器中

我们也可以定义多个对象,也可以通过设置name属性给Bean对象进行重命名操作

扫描路径

Q:使用学习的注解声明的Bean,一定会生效吗?

A:不一定(原因:bean想要生效,还需要被Spring扫描)

Spring默认的扫描路径:启动类所在的路径(包含子孙目录)

加了@ComponentScan之后,就以这个为主(这个可以配置多个路径(类似数组的定义))

这里我们推荐的做法:把启动类放在我们希望扫描的包的路径下,这样我们定义的bean就可以被扫描到

DI详解

依赖注入是一个过程,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象

在之前的学习中,我们有用@Autowired这个注解,完成了依赖注入的操作

简单来说,就是把对象取出来放到某个类的属性中

关于依赖注入,Spring也给我们提供了三种方式:1.属性注入(Field Injection)

2.构造方法注入(Constructor Injection)

3,Setter注入(Setter Injection)

属性注入

属性注入是使用@Autowired来实现的

@Autowired从Spring容器中根据类型获取对象,并进行赋值 只会是@Autowired下面一行的代码生效

Spring创建对象是依靠反射

java 复制代码
@Controller
public class HelloConrtoller{
     @Autowied
     private UserService userService;


     public void print(){
        System.out.print("HelloController");
        userService.print();
     }    
        
}

构造方法注入

如果只有一个构造方法时,@Autowired可以省略

如果存在多个构造方法,默认的构造方法是无参的构造方法,也可以通过@Autowired来指定默认的构造方法

java 复制代码
@Autowired
public HelloController(UserService userService){
    this.userService = userService;
}
public HelloController(){
}

Setter方法注入

java 复制代码
private UserSerivce us;

private UserConfig uc;

@Autowired 
public void setUs(UserService us){
    this.us = us;
}

@Autowired
public void setUc(UserConfig uc){
    this.uc = uc;
}

三种注入优缺点分析

|----|---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------|
| | 属性注入 | 构造方法注入(Spring4.x推荐) | Setter注入(Spring3.X推荐) |
| 优点 | 简洁,使用方便 | 1.可以注入final修饰的属性 2.注入的对象不会被修改 3.依赖对象在使用前一定会被彻底初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法 4.通用性好,构造方法是JDK支持的,所以更换任何框架,他都是适用的 | 方便在类实例之后,重新对该对象进行配置或者注入 |
| 缺点 | 1.只能用IoC容器,如果是非IoC容器不可用,并且只有在使用的时候才会出现NPE(空指针异常) 2.不能注入一个Final修饰的属性 | 注入多个对象的时候,比较繁琐 | 1.不能注入Final修饰的属性 2.注入对象可能会被改变,因为Setter方法可能会被多次调用,就有被修改的风险 |

@Autowired存在的问题:

当同一个类型存在多个bean时,使用@Autowired会存在问题,会报required a single bean,but 2 were found(非唯一的Bean对象)

java 复制代码
@Component
public class BeanConfig {
    @Bean("u1")
    public User user1(){
        User u1 = new User();
        u1.setName("july");
        u1.setAge("12");
        return u1;
    }


    @Bean("u2")
    public User use2r(){
        User u2 = new User();
        u2.setName("nora");
        u2.setAge("13");
        return u2;
    }
}
java 复制代码
@ResponseBody
@Controller
public class UserController {
    @Autowired
    private UserService us;

    @Autowired
    private User user;


    public void sayHi(){
        us.print();
        System.out.println(user);
    }
}

如何解决这一问题呢?

Spring提供了一下几种解决方案:

1.@Primary

使用@Primary注解,当存在多个相同类型的Bean注入时,加入@Primary注解 来确定默认的实现

2.@Qualifier

使用@Qualifier注解:指定当前要注入的bean对象.在@Qualifier的value属性中,指定注入的bean名称

@Qualifier注解不能单独使用,必须配合@Autowired使用

3.@Resource

使用@Resource注解:按照bean的名称进行注入,通过name属性指定要注入的bean的名称

依赖注入,通常使用@Autowired和@Resource

参数更加推荐使用@Qualifier

常见的面试题总结

@Autowired和@Resource的区别

1.@Autowired是Spring框架提供的注解,而@Resource是JDK提供的注解

2.@Autowired默认是按照类型注入,而@Resource是按照名称注入 相比于@Autowired来说,@Resource支持更多的参数设置,例如name设置,根据名称获取bean

(这里我们要注意严格来说@Resouce是按照类型+名称来注入)

获取bean对象,是父类BeanFactory的功能

ApplicationContext vs BeanFactory

1.继承关系和功能方面来说:Spring容器有两个顶级的接口:BeanFactory和ApplicationContext.其中BeanFactory提供了基础的访问容器的能力,而ApplicationContext属于BeanFactory的子类,他除了继承了BeanFactory的所有功能之外,他还有独特的特性,还添加了对国际化的支持,资源访问支持,以及事件传播等方面的支持

2.从性能方面来说:ApplicationContext是一次性加载并初始化所有的bean对象(饿汉模式),而BeanFactory是需要哪个才去加载哪个(懒汉模式)

Autowired的装配顺序

Spring,Spring Boot,和Spring MVC的关系和区别

Spring:简单来说,Spring是一个开发应用框架,轻量级,一站式,模块化,其目的是用于简化企业级应用程序开发 Spring主要功能:管理对象,以及对象之间的依赖关系,面向切面编程,数据库事务管理,数据访问,web框架支持等 同时Spring具有高度可开放性,并不强制依赖Spring 比如开发者可以自由选择Spring的部分或者全部,Spring可以无缝继承第三方框架,比如数据访问框架,web框架(这里我们说的是Spring core 不是Spring全家桶)

Spring Boot:Spring Boot是对Spring的一个封装,是为了简化Spring应用的开发而出现的 中小型企业没有成本研究自己的框架,使用SpringBoot可以快速搭建框架降低开发成本,让开发人员更加专注于Spring应用的开发,无需过多关注xml的配置和一些底层的实现

Spring MVC:Spring MVC是Spring的一个子框架,Spring诞生之后,大家觉得好用,于是按照MVC模式设计一个MVC框架(一些用Spring解耦的组件),主要用于web应用和网络接口,所以Spring MVC是一个web框架

总结来说:SpringMVC和SpringBoot都属于Spring SpringMVC是基于Spring的一个MVC框架 而SpringBoot是基于Spring的一套快速开发整合包

相关推荐
Spider Cat 蜘蛛猫1 小时前
Springboot SSO系统设计文档
java·spring boot·后端
未若君雅裁1 小时前
MySQL高可用与扩展-主从复制读写分离分库分表
java·数据库·mysql
学习中.........1 小时前
从扰动函数的变化,感受红黑树带来的性能提升
java
计算机安禾2 小时前
【c++面向对象编程】第24篇:类型转换运算符:自定义隐式转换与explicit
java·c++·算法
weixin199701080163 小时前
【保姆级教程】淘宝/天猫商品详情 API(item_get)接入指南:Python/Java/PHP 调用示例与 JSON 返回值解析
java·python·php
环流_3 小时前
redis核心数据类型在java中的操作
java·数据库·redis
雨辰AI3 小时前
SpringBoot3 项目国产化改造完整流程|从 MySQL 到人大金仓落地
java·数据库·后端·mysql·政务
带刺的坐椅3 小时前
Java 流程编排新范式 Solon Flow:一个引擎,七种节点,覆盖规则/任务/工作流/AI 编排全场景
java·spring·ai·solon·flow
知彼解己3 小时前
Arthas:Java生产环境问题排查利器,从入门到实战
java