目录
经典面试题 :属性注入 ,构造方法注入 和 Setter 注入 之间,有什么区别?
一、前言:
上篇博客,我们讲了一个spring core项目的大致流程:
创建项目——》将对象储存到Spring容器中 ——》从Spring容器中取出Bean对象
但是吧,上篇讲的这些流程还是太繁琐了,有没有一个更简单的方式来实现对象的存取呢?
当然有,一起来看看吧!
首先,Spring项目的创建——这个没有什么好说的!就按我们上篇博客的步骤来进行。
但注意:与上篇博客相比,spring的配置文件发生了改变——为了更简单的实现Bean对象的存入(把对象存到spring中)
更改后的spring配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:content="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <content:component-scan base-package="在对象储存中,要扫描的路径"></content:component-scan> </beans>
二、储存Bean对象
首先我们回忆下,我们执行储存Bean对象的方式。
之前我们存储 Bean 时,需要在 spring-config 中添加⼀⾏ bean 注册内容才⾏,如下图所示:
这种存入 Bean 的方式,并不是很好!
1、需要手动添加 bean 对象 到 配置文件中
2、如果 是配置文件中出现了问题,不好调试。
而现在我们不需要一个一个的在spring配置文件中添加我们要储存的Bean对象。我们直接:
你以为把扫描路径添加到配置文件中就行了吗?
不你还需要再类中添加注解——再扫描的过程中,只有添加了注解的类才能成功存储到spring中
这就引起了我们注解的概念
一共有两者方法:
1、使用 5 大类注解实现1、@Controller 【Controller - 控制器】
2、@Service 【service - 服务】
3、@Repository 【repository - 仓库】
4、@Configuration 【configuration - 配置/布局】
5、@Component 【component - 组件】
通过上述五大类注解中的任何一个,都可以将 bean 存储到 Spring 中。2、通过 方法注解@Bean ,也可以将 一个 bean 存储到 Spring 中。
5大类注解
上面我们用到了@Service注解,其他四个注解@Controller、@Repository也是一样的。
补充1:
既然这5大类注解的用法这么相似,那为啥还要分成5个不同的注解?统一弄成一个注解不好吗?
要解答这个问题,首先我们先要了解软件工程方面的知识
之所以讲这个 关于“软件分层” 的定义呢,就是为了 后面讲解 “为什么要有五大注解类” 做铺垫!
我们接着看
既然这5大类注解对应着软件分层中的不同层——他们各自要实现的功能是不同的。
这也就不难理解为啥要分成5大类中注解了,
补充2:
你可能还有一个疑惑:为什么?我们的 Spring 一定要有下面这个配置呢?
如果没有这个配置,意味着什么??
大家可以想象一下:
一个 Spring 项目中,我们的类可分两种类型:1、需要 进行 控制反转的类,将类的“生命周期”交给 Spring 来管理的类。【比如:UserController】
2、不需要存入 Spring 中的 类。
假设,我们有一个大型项目,需要存入 Spring 的 类 和 不需要存入 Spring 中的类,个数占比是五五开的。
这就会存在问题了!
如果我们没有 描述 根目录 的 这一行代码,
Spring 就会去扫描 所有的类,看看这些类中有哪些。
但是!项目中 需要存入 Spring 中的类,只占 50 %。
即:Spring 要浪费一倍的时间,去排查 不需要 存入 Spring 中的类。
所以,Spring 为了 提升效率,你必须要给我指定扫描的目录。
保证该目录下,一定是需要存入 Spring中的类。
这样 Spring就只需要扫描 对应目录中的类,就可以了!
@Bean方法注解
上面我们通过5大类注解还有Bean方法实现了简单往spring中存Bean对象。那么怎样实现简单的从spring中取Bean对象呢?
三、获取Bean对象
获取 bean 对象也叫做 对象装配,是把对象取出来放到某个类中,有时候也叫 对象注⼊。
对象注入,其实就是我们spring中的DI——依赖注入。
IoC 和 DI 是 Spring 中最重要的两个概念,其中 IoC(Inversion of Control)为控制反转的思想,而 DI(Dependency Injection)依赖注入为其(IoC)具体实现。那么 DI 实现依赖注入的方式有几种?这些注入方式又有什么不同?
在 Spring 中通过@Autowired实现依赖注入的常见方式有以下 3 种:
- 属性注入(Field Injection);
- Setter 注入(Setter Injection);
- 构造方法注入(Constructor Injection)。
属性注入
因此,可以得出结论:
在使用 @Autowired 进行 属性注入的时候
如果 注入的对象,被多次 存入 Spring 中了,那么,光凭 属性的类型是找不到匹配的 bean的!需要将 属性的变量名 改成 BeanName,根据 BeanName 来找寻匹配的对象(bean)并进行 属性注入。
方法不止这一种,但是上述这种 “精确描述 bean 的名称” 的方法,是最简单高效的!是不是很简单,不用去获取 Spring 的上下文对象 和 getBean 方法,直接通过一个注解,即可获取对应的bean(从Spring中取出 bean)。
再来看一个例子
当同一个类型的 Bean 存在多个时就有可能出现非唯一 Bean 的异常,
那么我们怎么解决这个问题呢?
两种办法:
1、把你的属性名给改成类中的方法名student1或者student2
2、用@Qualifier 是用来筛选 Bean
优缺点
优点:
属性注入最大的优点就是实现简单、使用简单,只需要给变量上添加一个注解(@Autowired),就可以在不 new 对象的情况下,直接获得注入的对象了(这就是 DI 的功能和魅力所在)
缺点:
- 功能性问题:无法注入一个不可变的对象(final 修饰的对象——无法被初始化);
- 通用性问题:只能适应于 IoC 容器;
- 设计原则问题:更容易违背单一设计原则(因为使用方便,在程序中可能大量使用属性注入)。
Setter注入
优缺点分析
从上面代码可以看出,Setter 注入比属性注入要麻烦很多。
优点:
要说 Setter 注入有什么优点的话,那么首当其冲的就是它完全符合单一职责的设计原则,因为每一个 Setter 只针对一个对象。
缺点:
但它的缺点也很明显,它的缺点主要体现在以下 2 点:
- 不能注入不可变对象(final 修饰的对象——无法通过构造方法初始化);
- 注入的对象可被修改(setter方法可多次被调用)
构造方法注入
优缺点分析
优点:
构造方法注入相比于前两种注入方法,它可以注入不可变对象,并且它只会执行一次,也不存在像 Setter 注入那样,被注入的对象随时被修改的情况,它的优点有以下 4 个:
- 可注入不可变对象(在构造方法中就进行了初始化);
- 注入对象不会被修改(一个类中,构造方法只能调用一次);
- 注入对象会被完全初始化;
- 通用性更好(即可以用于IoC容器,也可以用于非IoC容器)。
缺点:
因为在构造方法中可以传多个参数,容易违背单一性原则
经典面试题 :属性注入 ,构造方法注入 和 Setter 注入 之间,有什么区别?
@Resource VS @Autowired 的区别
@Autowired是spring提供的注解,@Resource是jdk提供的注解
1、用法不同
- @Autowired,支持 属性注入,构造方法注入,Setter 方法注入。
- @Resource:支持 属性注入,Setter方法注入。不支持 构造方法注入。
2、@Resource 的 属性注入 比 @Autowired 的 属性注入,使用的更舒服。 因为 @Resource 有很多的属性可以设置;而 @Autowired 只有一个 value 属性。
有很多的属性,即意味着可以使用很多其它的功能。
比如在上面的当同一个类型的 Bean 存在多个时就有可能出现非唯一 Bean 的异常,如果是@Autowired,我们还需要额外的@Qualifier 是用来筛选并 Bean。
而@Resource则可以直接: