0
点赞
收藏
分享

微信扫一扫

vue3从精通到入门12:vue3的生命周期和组件

静守幸福 04-04 21:30 阅读 1
springjava

本篇主要介绍Spring两大核心思想之一的IOC以及与之密切相关的DI。

目录

一、IOC与DI是什么?

二、Spring中IOC&DI的使用

IOC

Bean

存入Bean

五大类注解

方法注解@Bean

Bean的命名

扫描路径

取出Bean

DI

属性注入

构造方法注入

Setter注入

三种注入方式的优缺点

使用@Autowired存在的问题


一、IOC与DI是什么?

IOC,可以简单概况为四个字,“控制反转”,具体来说就是将一个对象创建、销毁、使用的控制权统一反转给一个固定的第三方来进行管理,而这个统一的第三分被称为IOC容器,Spring就是这样一种IOC容器。

下面我们通过一个案例来体会一下
一辆车一般有以下依赖关系:

 转换成代码表示:

这样的依赖关系虽然不影响一辆车的制造,但当我们需要更改轮胎的尺寸也就是size变量时,会直接对这四个组件(四个类)都造成影响, 这样耦合程度就显得太高了,下面我们基于IOC思想优化一下,

在优化后的代码中我们可以发现在main方法中已经事先创建好了当前组件所需要的依赖,而不是像先前那样自己创建依赖,因此当我们需要修改size时,只需要修改Tire的实参即可,其他组件是无需做出任何反应的,因为其他组件只需要去拿自己需要的依赖即可,至于依赖的内部如何实现,发生了怎样的变化,是不需要去关心的,这样一来也就实现了各个组件之间的解耦 。

从汽车的案例中我们可以总结出IOC具有以下两大优点:

  • 依赖交由第三方(容器)来进行创建,我们要使用时直接去容器中拿即可,因此对于依赖的使用相比传统开发更加方便。
  • 不再需要关心依赖的具体实现细节,只需要关心自己的业务逻辑即可,降低了各依赖之间的耦合程度

DI,直接的意思是依赖注入,具体来说就是将存入IOC容器的依赖对象取出来,并注入到需要使用该依赖的地方。DI也可以理解成从取的角度对IOC的一种实现。

二、Spring中IOC&DI的使用

前面我们说过Spring是一个IOC容器,因此IOC&DI在spring中均有体现,下面我们来看一下IOC&DI在spring中的具体代码实现。

IOC
Bean

Bean可以简单理解成保存在spring容器中的对象,针对Bean的操作具体有两种:

  • 存入
  • 取出

下面我们来具体来了解一下。

存入Bean

存入bean一般有两种方式,一种是使用五大类注解,另一种是使用方法注解@Bean。

五大类注解

Spring中的五大注解具体如下:

  • @Controller
  • @Service
  • @Repository
  • @Component
  • @Configuration

如果要将当前类的对象存入到Sping中,直接在类上添加五大类注解中的其中一个,Spring就会自动为我们创建一个对象并存入,例如我们在Spring Boot项目中创建一个Controller类,并将其通过@Controller存到spring中,代码如下:

接下来我们观察一下这五个注解的源码

 

 

 

 

通过源码可以发现,@Component在其他另外四个类注解中都有使用,这也就意味着这四个类注解 都是由@Component衍生而来。并且仔细观察可以发现这些注解除了名字不一样外,其他部分基本上可以说是一模一样的,也就是说这五个注解的功能基本上是大差不差的,那为什么Spring还要实现五个呢?直接用一个不就行了吗?事实上,这五个注解都有自己特定的使用场景,具体如下:

  • @Controller:控制层,接收请求,对请求进行处理,并进行响应
  • @Service:业务逻辑层,处理具体的业务逻辑
  • @Repository:数据访问层,负责数据相关的操作
  • @Configuration:配置层,处理项目中配置相关的信息
  • @Component:上述四个注解未涉及的均使用@Component

 通过在特定功能的类上加上对应的注解,可以让开发人员能够一眼就知道这个类大概是什么功能,从而加快开发效率。并且通常情况下我们会将使用相同类注解的类放到一个包下,从而实现前面在介绍Spring MVC中所说的“三层架构”。

因此我们在使用类注解时,应当结合当前类的特点来使用,并将使用相同类注解的类放到同一个包下进行管理。

最后,在使用五大类注解时需要注意确保类中具有一个无参的构造方法,因为在Spring创建对象时会调用这个无参构造方法来创建  ,如果没有bean是无法存入Spring的。

方法注解@Bean

另一种存Bean的方式是使用方法注解@Bean,他与五大类注解不同的是@Bean是加在方法上的,并且会将方法的返回值作为Bean存入到Spring中,例如我们先创建一个User类,然后将其通过方法注解的方式存入Bean中,具体代码如下

需要注意的是只有在添加了五大类注解的类中的方法才能使用@Bean来存Bean,在其他类中使用@Bean是存不了的。

Bean的命名

Bean是有名称的,如果我们使用五大类注解存入的Bean,bean的名称默认为类名的小驼峰,另外还有一个特殊情况,如果类名前为大写,bean名默认为类名,如果使用的是@Bean,bean名默认为方法名称。我们还可以通过注解中的value参数,来指定Bean的名称,指定Bean名称后,Bean的名称将不会再是先前默认的名称,而是我们指定的名称,具体代码如下:

另外,使用@Bean时我们还可以为一个bean指定多个名称,具体为使用name参数代码如下:

 

扫描路径

在spring中并不是项目中所有的使用了五大类注解和@Bean的类都会被存到Spring中的,只有处于扫描路径下,才会被Spring扫描并存入Spring中。在Spring Boot项目中默认的扫描路径为加了@SpringBootApplication注解的类(启动类)所在路径及其子路径,如果类或方法不在这个路径下,就无法被Spring扫描到,也就无法存入到Spring容器中。我们可以通过@ComponentScan来配置扫描路径,例如,我们要把扫描路径配置为只扫描我们前面的controller包,代码如下:

在大括号内我们还可以添加多给路径作为扫描路径。

之所以默认的路径为启动类所在路径,也是因为在@SpringBootApplication中通过@ComponentScan对扫描路径进行了配置

由于在@SpringBootApplication中也使用了@ComponentScan,因此,并不推荐自己使用@ComponentScan来配置扫描路径,如果一定要修改扫描路径,直接移动启动类即可。

取出Bean

从Spring容器中取出Bean可以通过使用上下文的方式,上下文可以简单的理解为就是用来储存Bean的Spring容器,上下文具体为ApplicationContext接口,其创建方法如下:

ApplicationContext context = SpringApplication.run(启动类类型);

我们可以通过上下文为我们提供的方法来获取Bean,具体如下:

方法作用
T getBean(Class<T> aClass)通过Bean的类型获取
Object getBean(String s)通过Bean的名称获取
TgetBean(String s , Class<T> aClass)通过Bean的名称和类型获取

在使用Bean的名称来获取Bean时由于返回类型是Object,所以在使用时需要强转成Bean的类型再使用。下面我们来通过上下文来获取一下前面存的Bean

通过类型获取:

存入Bean

控制台的输出:

需要注意的是如果在Spring中存在多个相同类型的Bean时,使用类型来获取Bean会报错,具体报错信息如下:

因此,当存在多个类型相同的Bean时,更建议通过Bean的名称来获取。具体代码如下:

存入Bean

通过名称取Bean

控制台:

当Bean被设置了多个名称时,使用任意一个都能获取,具体代码如下

存Bean:

取Bean:

控制台:

和使用“u1”结果一样。

如果Spring中有两个相同名称(通过五大类注解创建相同名称的Bean时会报错,使用@Bean则不会),则会获取先存入Spring的那个,示例代码如下

存Bean

取Bean:

控制台:

最后我们再来看一下根据类名和名称来获取Bean,代码示例:

存Bean:

取Bean:

控制台:

如果spring中存在多个相同Bean,结果和使用名称获取相似。

在Spring中还可以通过BeanFactory来获取Bean,但上下文已经对BeanFactory进行了封装,因此上下文是具备BeanFactory的所有功能,并且上下文还在BeanFactory的基础上进行了很多拓展,功能更丰富。BeanFactory在处理Bean时采用的是懒汉模式,只有当需要使用Bean才会真正去创建Bean,而上下文则是采用的饿汉模式,在使用前就把所有Bean创建好了,因此获取Bean时,上下文的速度会更快。所以,一般情况下,更推荐使用上下文的方式来获取Bean。

DI

前面我们介绍过,通过上下文的方式可以取出Bean,但在日常开发中,更多的还是通过依赖注入,也就是DI来获取Bean。DI,一共有三种注入方式,属性注入、构造方法注入和Setter注入。下面我们来具体了解一下这三种注入方式。

属性注入

 属性注入的方式非常简单,只需要在需要注入的属性上加上@Autowired(一般在加了五大类注解的类中使用),然后Spring就会自动将容器中与属性类型相同的Bean注入到属性中。接下来我们创建一个类Controllertest2,然后创建属性ControllerTest,然后再将先前存入的ControllerTest类型的Bean注入到该属性中,并通过web的方式检验是否真正注入,具体代码如下:

访问接口后控制台的显示:

可以发现属性注入成功了。

构造方法注入

构造方法注入与属性注入类似,也是使用@Autowired,不过构造方法注入是加在构造方法上,具体代码如下:

控制台:

这里在把Controllertest2存到Spring中时将不会再调用默认的无参构造方法来创建对象,而是调用这个加了@Autowired的构造方法来创建对象,因此这里可以省略无参构造方法,但通常情况下还是建议加一个无参构造方法,因为其他的一些Api可能会调用到无参构造方法,例如通过对象来接收请求中的JSON字符串时,需要调用无参构造方法来构建对象。

Setter注入

最后一种注入方法是Setter注入,这种注入方式也是通过@AutoWired来实现的,具体代码如下:

控制台:

由于set方法在其他地方也可以调用,因此通过这种方式注入的属性值是很容易修改的。

三种注入方式的优缺点

下面我们来总结一下这三种注入方式的优缺点

注入方式
属性注入

优点:简单使用方便

缺点:1.不能注入final修饰的属性

           2.只能再IOC容器中使用,其他环境下使用不了

构造方法注入(Spring4.x推荐使用)

优点:1.能够注入final修饰的属性(因为在构造方法中对属性进行初始化)

           2.注入的属性值不能修改

           3.由于依赖是注入到构造方法中,因此依赖在使用前,一定是已经初始化完毕的,因为构造方法再类加载阶段就已经完成了

           4.通用性好,构造方法是JDK支持的,在任何框架下都能使用

 缺点:注入多个对象时会很繁琐

Setter注入(Spring3.x推荐使用)

优点:1.在创建类实例后,还能对注入的对象进行修改和重新配置

缺点:1.不能注入一个Final修饰的属性。

           2.Set能够在很多地方使用,有被修改的风险

使用@Autowired存在的问题

@Autowired在进行依赖注入时会优先根据属性类型去Spring中找Bean,如果找到的Bean有多个,就会根据属性名称去找,如果没有找到与属性名称同名的Bean,就会报异常

当我们通过指定属性名称去Spring中取Bean时,会出现这样一个问题。通常情况下,大家都会认为属性名称的修改对项目并不会影响。因此,在开发时很有可能在无意中将注入的属性名称给改了,如果此时该属性类型的Bean有多个,就会导致注入失败。这种问题的解决方案通常有三种,一种是加@Primary注解,通过@Primary注解就会将默认注入的Bean设置为由加了@Primary的方法存入spring的Bean。具体代码示例如下:

此时通过@Autowried注入的User类型的对象就会默认为使用上述加了@Primary的user1了。

另一种方法是通过@Qualifier,这个注解有一个value参数,它可以指定@Autowired注入的bean的名称,具体代码示例如下:

 此时@Autowired会根据@Qualifier设置的bean名称去Spring寻找需要注入的Bean。这样无论如何改变属性名称都只会去获取@Qualifiler指定名称的bean了。

最后还有一种方法是使用由JDK提供@Resource来代替@Autowired来进行依赖注入,在@Resource中自带一个name属性用来设置需要注入的Bean的名称,其效果和前面使用@Qualifiler是一样的,代码示例如下:

 最后我们再来看一个常见面试题:

@Autowired和@Resource的区别?

  • 提供方不同,@Autowired是由Spring提供的,@Resources是由JDK提供的
  • 默认方式不同,@Autowire的默认根据类型注入,当同类型Bean存在多个就会根据属性名注入,而@Resource则是根据指定的Bean名称进行注入
  • 支持的参数不同,@Resource支持更多的参数,如name等。
举报

相关推荐

0 条评论