【Java Web】 -> Spring 5 ->IOC 操作 -> Bean 管理 ->关于 IOC 的一切就在这里了

本文详细介绍了Spring框架中Bean管理的两大方式:基于XML配置文件和基于注解的方式。从创建对象、注入属性到Bean的作用域与生命周期,深入探讨了Bean管理的各个方面,包括依赖注入、属性注入、自动装配和外部属性文件的引入。此外,还对比了基于XML和注解的不同实现,展示了完全注解开发的配置类使用。
摘要由CSDN通过智能技术生成

Ⅰ 什么是 Bean 管理

Bean 管理通俗的看,其实指的是两个操作。

  1. Spring 创建对象
  2. Spring 注入属性

创建对象很好理解

在这里插入图片描述
注入属性就是我们创建一个类,要生成它的一个对象,由Spring 来完成它的成员的赋值,不用我们传构造方法或者用set方法之类的。这个成员变量既可以是八大基本类型,也可以是引用类型。

所以 Bean 管理就两点:创建对象,注入属性。

Ⅱ Bean 管理的两种方式

第一种是基于 xml 配置文件方式实现,第二种是基于注解方式实现。接下来的文章里我来详细讲解一下这两个方式。

A. 基于xml配置文件的Bean管理

a. 创建对象

基于 xml 文件配置创建对象,可以很快地扫一下我的第一篇文章 👉 第一个Spring程序

在这里插入图片描述
要创建对象就是基于 bean 标签做一个配置,写清楚名字 id 和 类路径 class,这个标签中其实还有很多属性,我们一起来看一下。

最重要的就是我上面说的两个:

id : 是一个唯一的标识,最后从工程中获取对象需要通过不同的 id 来得到,所以如果你两个类用了一个 id ,那在获取的时候大概率会发生错误。

class : 类全路径,也就是包类路径,大家可以对比我左边的包结构和我右边 class 的值做一个对照。

name : 这个属性和 id 的作用是一样的,区别在于 name 中可以加入特殊符号,id 不行。现在 name 已经没什么人用了,因为它在最初是配合 Struts2 框架使用的,而这个框架已经很少被使用了。
在这里插入图片描述

关于创建对象要注意一点,在 IOC 底层原理 中我提过,IOC 是基于反射实现的,也就是执行的是 newInstance()方法,(这是Java 8 的方法,如果是更高版本,就是先生成 Constructor 构造器,再进行同样的instance操作),这个方法执行的无参构造。

当我们不写构造方法时,默认就是有一个无参构造的。但是当我们写了一个带参构造,无参构造方法就默认没有了,所以如果你没写的话,生成对象就会出问题。

比如我在 User 中,只写一个带参构造。

在这里插入图片描述

我们用 Spring 生成一下试试。

在这里插入图片描述

程序就出错了,还会提示你没有找到无参构造方法。

在这里插入图片描述
所以一定要注意这一点。

b. 注入属性

注入属性专业一点就是 DI(Dependency Injection),依赖注入。它在对象创建好的基础上才能完成

① set 方法注入

首先我们先来给测试的User类写两个属性,并且写上setter方法,再覆盖一个 toString 方法以便测试。

在这里插入图片描述
然后我们来看看配置文件的写法。

在这里插入图片描述
也很简单,就是在创建对象的bean标签里,再写一个 property 标签,name是要注入的属性名, value 是要注入的属性值。

测试方法如下:
在这里插入图片描述
和之前一样,还是直接获取对象,然后输出即可。

在这里插入图片描述
DI 成功。

② 有参构造方法注入

这次我们创建一个简单的 Book 类,不用写setter方法了,直接写一个有参构造。

在这里插入图片描述
配置文件写法如下:

在这里插入图片描述
和set方法注入差不多,只不过需要把标签换成 constructor-arg

我们写一个简单的测试方法。

在这里插入图片描述
结果如下,DI 成功
在这里插入图片描述
还有一个配置的写法,是通过下标

在这里插入图片描述

从0开始依次对应有参构造里的顺序。这样结果是一样的,不过还是建议通过name 来写,通过 index 如果你的构造方法里的参数顺序改变了,就要重新改,很麻烦,也不好读。

③ p 名称空间注入

p 名称空间注入是可以简化 xml 文件配置的一种方式。

首先添加 p 名称空间在配置文件中。

我们先看配置文件的最上面,有长长的一段。复制箭头指向的那行,然后再写一行,把箭头标记的地方,换成p。

在这里插入图片描述
接着,在bean的属性中进行注入。

我们先来回顾一下用set方法是怎么注入的,做一个对比。
在这里插入图片描述
我直接拿这个做演示,看改成 p 命名空间要怎么做。

在这里插入图片描述
就这么一行就够了。

在这里插入图片描述
结果是一样的,非常方便。注意 p 命名空间是基于 set 方法的,如果你没有写,那还是会出错的。

④ 注入特殊值

首先我先说一个比较专业一点的词,叫字面量。什么意思呢,其实就是设置属性的固定值。

比如我在 Book 中设置一个值,
在这里插入图片描述
那这个 “玫瑰之名” 就是字面量。

又或者我们在配置文件中设置的值
在这里插入图片描述
这也是字面量。

🦆 设置空值

我们先来看一下怎么设置属性为 null。

我先在 User 中添加一个属性 address

在这里插入图片描述
在配置文件中,只需要加一个标签 null,这样就可以设置 address 为空值了。

在这里插入图片描述
结果如下:
在这里插入图片描述

🐖 设置特殊符号

我先举个例子,比如我现在要设置姓名为 <name>

在这里插入图片描述
可以看到报错了,编译器以为我们加的 <> 是xml的符号,那我们应该怎么设置这种特殊符号呢?

第一个方法当然就是直接转义一下,xml 转义的符号我列在下面:
在这里插入图片描述
用法如下所示:
在这里插入图片描述
如果不想转义,还有一个方法,就是使用 CDATA

在这里插入图片描述

红框圈起来的就是 CDCDATAATE 结构,我们直接在 CDATA 后的括号里写我们要写的值就好了。
在这里插入图片描述
这个结构大家如果用IDEA的话,敲一个CD,直接回车就出来了。

整理到一行如下:
在这里插入图片描述
运行结果已经将 <> 加到了名字外面。

在这里插入图片描述

⑤ 外部 bean

什么叫外部 bean 呢,我们做开发 MVC 贫血模型大家很熟悉了,现在我写个简单的例子,就是 UserService 调用 UserDao 的方法,这个调用的过程其实就是在调用外部 bean。

在这里插入图片描述

Dao层我写了一个接口,一个实现类。
在这里插入图片描述

在这里插入图片描述

现在我想在 UserService 中调用一下 UserDao 的方法。我先写一个传统的写法。

在这里插入图片描述

现在我们来看怎么用 Spring 完成这个过程。

首先我们在 UserService 中建立一个 UserDao 的成员,然后通过 set 方法进行注入。
在这里插入图片描述

接着我们来看配置文件,首先先创建 UserService 和 UserDao 的对象

在这里插入图片描述
然后来注入 UserDao

在这里插入图片描述
这时候就不是 name 和 value 了,而是 ref,也就是引用的意思,代表它是一个引用类型。注意我红框圈起来的地方,UserService 的属性值,要和 UserDao 生成的对象名字相同,就是要让 UserService 找到要注入的对象。

如果我改了 UserDao 的 id,那么相应的,UserService 中的 ref 也就是属性值也要改。

在这里插入图片描述

我们来看运行结果。

在这里插入图片描述
可以看到,UserDao 和 UserService 中定义的方法它都输出了。

⑥ 内部 bean

我直接通过一个例子来说明内部 bean 和级联吧。

我先建立一个一对多的关系,比如一个部门对多个员工。我们用实体类来表示,假装建了一个数据库表。

部门类如下:
在这里插入图片描述

员工类如下:
在这里插入图片描述

现在我们来写配置文件。

在这里插入图片描述
Employee 的一个成员是 Department,并且还有对应的关系,所以我们可以直接把 Department 的对象创建写到 Employee 中,这就叫内部 bean。

大家可以看到 IDEA 给我了提示,因为这里 id 其实并不太需要,所以我们完全可以删掉。

在这里插入图片描述
同样的,我直接覆盖这两个类的 toString 方法,以便做一个测试。

在这里插入图片描述
在这里插入图片描述
测试结果如下:

在这里插入图片描述

⑦ 级联赋值

其实上面我们做的事情,就是级联赋值。现在我再做一个整体的演示。

还是刚才的case,第一种级联赋值我们可以通过外部bean的方式进行注入。

在这里插入图片描述

第二种方法,通过表达式进行赋值。

首先我们先在 Employee 类中写一个 getDepartment 的方法,以得到 Department 的对象。
在这里插入图片描述
然后我们的配置文件,可以直接通过Employee,给Department的dapartmentName属性进行赋值。

在这里插入图片描述
大家可以看到我下面对 Department 的赋值写的是 Security,但是上面在 Employee 中通过表达式赋值的是 HR。我们看一下结果。

在这里插入图片描述
很好理解,在前面我们在 Employee 中添加了一个 get 方法,如果没有这个方法这个赋值是会失败的,因为我们是先通过 Department 的配置文件生成了一个它的对象,再通过 Employee 的 get 方法取得这个对象,然后直接对它进行赋值,根据先后顺序,自然是显示最后更改后的结果。

⑧ 注入集合
🦆 基本类型注入

我写一个简单的集合类的case。定义数组,列表,集合和散列表四种结构,然后分别写出它们的构造方法。

在这里插入图片描述
现在我们来写配置文件。

数组可以通过 array 标签或者 list 标签来进行赋值。

在这里插入图片描述
同样其他几个也如此,都是通过各自的标签来进行注入的。

在这里插入图片描述
在这里插入图片描述
最后 map 有些不一样,它还需要再加一个标签 entry,来为它的键值对赋值,而不是通过 value。

在这里插入图片描述
在 Collection 中我写了一个方便测试的方法。在这里插入图片描述
我们来看结果。
在这里插入图片描述

🐖 引用类型注入

我先来定义一个简单的类,就是课程,属性是课程名和老师名。并且要加上 set 方法。

在这里插入图片描述
接着,在 Collection 中加入一个 List,泛型是 Course.
在这里插入图片描述

首先,我们先创建几个 Course 的对象。
在这里插入图片描述

那怎么把这几个对象放到 List 中呢?非常简单。

在这里插入图片描述
普通类型我们使用的是 value 标签,引用类型使用 ref 就好了,然后通过它的 bean 属性,将下面创建的几个对象的 id 放进去,这就可以了。

我们在Course类中覆盖一下 toString 方法,并修改Collection 中的测试方法。
在这里插入图片描述
在这里插入图片描述

结果如下:
在这里插入图片描述

🐕 提取集合注入部分

这里为了和前面的部分分开,我再写一个得到的类,得到课程列表还是用之前定义的 Course 做泛型,再写一个String类的得到图书列表,以做对比。

在这里插入图片描述

接着我们来看看配置文件。这里还需要一个命名空间,但是不同于前面提过的 p 命名空间,这次要使用 util 命名空间。

和 p 命名空间比较类似,我们需要将复制的那行里的 beans 改成 util

在这里插入图片描述

<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

</beans>

接下来我们就要使用 util 的标签对 List 进行提取了。

可以看到 util 后面还有很多东西,所以不仅可以提取出 List,也可以提 map等等,这里我用 List 做一个例子。

在这里插入图片描述
我们先提取一个普通类型的List,注意在list后面要写一个id,取个名字。赋值的方法和前面是一样的,用 value 标签增加元素就好。
在这里插入图片描述
对象也差不多,我们需要先创建几个对象。
在这里插入图片描述
然后用 util 把它们提取出来。
在这里插入图片描述
最后注入的时候就很简单了。
在这里插入图片描述
直接通过 ref 属性,用两个提取出来的 util 赋值就可以。

同样的,我们在 Dedao 类中创建一个用于测试的方法。

在这里插入图片描述
运行结果如下
在这里插入图片描述

c. FactoryBean

在 Spring 中,有两种类型的 bean,一种是我们自己定义的普通 bean,一种是工厂 bean,也就是 FactoryBean。它是 Spring 中内置的 bean 类型。

我们来看一下我们自己定义的普通 bean。

在这里插入图片描述
我们定义的什么类型,返回的也一定是这个类型的 bean,这个很显然吧。这就是普通 bean 和工厂 bean 的差别所在。

普通 bean 在配置文件中定义 bean 就是返回类型;
工厂 bean 在配置文件中定义 bean 类型可以和返回类型不一样。

普通 bean 我就不做演示了,上面全都是。我就来演示一下 工厂 bean 吧。

要定义一个工厂 bean,我们需要实现 FactoryBean 接口。我们需要实现两个方法。

在这里插入图片描述

我们来看一下这个最主要的方法。

在这里插入图片描述

getObject() 方法是定义返回的对象。

比如这里我让它返回一个 Course 的对象。

在这里插入图片描述注意哦,这里需要在 FactoryBean 后面加一个泛型。

然后我们来配置一下这个 bean。

在这里插入图片描述
运行一下

在这里插入图片描述
发现了吧,我们虽然取的是 myBean,但是生成的对象类型却可以是 Course,工厂 bean 就是借助这个方法实现的,返回类型可以和配置类型不一致。

d. Bean 的作用域与生命周期

① Bean 的作用域

什么叫 bean 的作用域呢?

在 Spring 中,创建 bean 实例可以的单例也可以是多例,这个就是 bean 的作用域。

默认创建 bean 都是单实例的,接下来我们看看怎么管理 Bean 的作用域。

我们先来做一个简单的验证吧,随便找一个上面定义过的 bean。就拿 Dedao 来验证好了。

在这里插入图片描述
我们知道 == 比较是比较的地址,返回为 true,说明这两个对象的地址是相等的,也就是说默认 bean 的构建是单例的。

在Spring 配置文件 bean 标签里有一个属性 scope,这个属性就是用来设置单例多例的。

scope 属性有两个最常用的值,第一个值默认值 singleton 表示单例,第一个值 prototype 表示多例。

在这里插入图片描述

现在我们来设置一下 bean 的多实例,还是用 Dedao 这个类,我们来配置一下它。

在这里插入图片描述

还是和之前一样的代码,这时候输出就是 false 了,所以这时候生成的对象是多例的。

在这里插入图片描述

除了 singleton 表示单例,prototype 表示多例以外,它们还有一个区别就是 singleton 实现的也是饿汉模式,在加载的时候就生成了对象,而 prototype 和前面的 BeanFactory 接口一样,是懒汉模式,只有调用的时候才会真正的去创建对象。这也很好理解,要做到多例,当然不能一开始就把对象创建好了,只能在调用时去创建,才能创建每次都不同的对象。

然后 scope 还有两个值,很不常用,但是我还是补充一下。一个是 request,一个是 session,就是 web 中的requestsession,用哪个值,在创建对象时就会把对象存在哪个里面。

② Bean 的生命周期

bean 的生命周期就是 对象从创建到销毁 的过程。接下来我简单描述一下 一个 bean 的整个周期。

  1. 通过构造器创建 bean 实例;
  2. 为 bean 的属性设置值,对其他 bean 引用(调用set方法);
  3. 调用 bean 的初始化方法(需要进行配置);
  4. 使用 bean;
  5. 在容器关闭时,调用 bean 的销毁方法(需要进行配置)。

现在我通过代码来演示一下整个过程。

首先我先创建一个 Book 类。

在这里插入图片描述
为了使得这个过程更加清晰,我在无参构造方法中和 set 方法中都加了一个输出,对应的周期的第一二步。

接着,我们再写一个初始化方法,也就是周期中的第三步。

这个方法什么也不做,就是输出这是第三步。
在这里插入图片描述
现在 init 方法只是个普通方法,我们怎么能让创建 bean 的时候执行它呢?这就需要再配置一个属性 init-method

在这里插入图片描述
现在到第四步,要获取对象,也就是使用 bean,我们在测试方法中写个输出。

在这里插入图片描述
第五步,执行销毁的方法,这步仍需要我们在 Book 类中写一下。

在这里插入图片描述
同样的,需要在配置bean时配置一下。

在这里插入图片描述
这个销毁的方法,不会自动执行,需要我们手动销毁一下,所以我们再去测试中销毁一下它。

在这里插入图片描述
这里的 close() 方法,并不是 ApplicationContext 类的方法,所以只能强转调用,我们干脆直接把上面的声明改成它的子类。

在这里插入图片描述
最后我们来执行一下这个测试。

在这里插入图片描述
这就是 Bean 生命周期的五个步骤。

但是其实,Bean 还有一个后置处理器,如果你再加上它,那 bean 的完整生命周期就是七步。

  1. 通过构造器创建 bean 实例;
  2. 为 bean 的属性设置值,对其他 bean 引用(调用set方法);
  3. 把 bean 实例传递给 bean 后置处理器方法;
  4. 调用 bean 的初始化方法(需要进行配置);
  5. 把 bean 实例传递给 bean 后置处理器方法;
  6. 使用 bean;
  7. 在容器关闭时,调用 bean 的销毁方法(需要进行配置)

看看后置处理器插入的地方,有没有想到 Proxy ?

好,现在我们再走一遍这个设置了后置处理器的生命周期。

首先我们要自己写一个后置处理器方法,要实现这个方法需要去实现一个接口 BeanPostProcessor,然后覆盖我左边圈起来的两个方法。

在这里插入图片描述
我们看这两个方法的名称,其实就可以猜到它是什么了。一个 before initialization 一个 after,显然就是要在初始化方法的前后分别执行。

在这里插入图片描述
那配置文件怎么写呢?也很简单,只要把这个类创建一下就ok了。
在这里插入图片描述
除此之外什么都不需要做,不用在上面的bean中再加什么属性。因为后置处理器的方法是实现了 BeanPostProcessor 接口的,Spring 会把当前配置文件里的所有 bean ,都设置配置的后置处理器的方法。

运行结果如下:

在这里插入图片描述
这就是一个 bean 短暂而辉煌的一生。

e. 自动装配

与自动装配相对的就是手动装配,就是我们上面做得那样,要自己写 property 标签,然后赋值。

什么是自动装配呢?

就是根据指定装配规则(属性名称或属性类型),Spring 自动将匹配的的属性值注入。

为和自动装配做一个对比,我们再来回顾一下手动装配的过程。

首先先定义两个类,Book 类依赖于 Author 类。

在这里插入图片描述
在这里插入图片描述
然后我们在配置文件中进行手动装配。
在这里插入图片描述
测试一下

在这里插入图片描述
这就是整个手动装配的过程,我们再来对比地看一下自动装配。

在bean 的属性中,有一个 autowire,它可以根据名称来装配,也可以根据类型来装配。
在这里插入图片描述
先来看 byName,根据名称来装配的话,我们创建的 Author Bean 的 id 必须和 Book 类的属性名相同。

在这里插入图片描述
在这里插入图片描述
第二种根据类型来装配,这里注意的是要装配的类型只能有一个,不然就会报错。
在这里插入图片描述
大家可以看到我的IDEA在byType的地方给了我提示,它说这个自动装配没有必要。
在这里插入图片描述
为什么呢?因为一般我们自动装配使用的都是注解方式,xml方式实现自动装配是很少使用的。

f. 引入外部属性文件

在注入属性的时候,我们每次都要写标签,要改要写都比较麻烦,所以我们可以把值都写到其他文件比如 properties 文件中,接下来我用一个配置数据库信息的例子,来演示 Spring 中引入外部属性文件的操作。

比如我们现在要配置一个德鲁伊连接池。

如果没有德鲁伊jar包的可以在这里下载👉 Druid,我们需要把这个 jar 包引入到我们的lib中。
在这里插入图片描述
我们可以直接在配置文件中配置数据库的连接池。这个是标准格式,我们需要完成四个属性的填写。

在这里插入图片描述
在这里插入图片描述
这个就是直接配置的方式,我们再来看看怎么通过外部文件配置。

首先我们先建立一个外部的 properties 文件,然后把这四个值写进去。
在这里插入图片描述
这样命名其实比较容易发生冲突,我们可以在前面再加一个前缀。

在这里插入图片描述
接着,我们需要把这个文件引入到Spring的配置文件中。

这里我们需要 context 的命名空间。
在这里插入图片描述

<?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:context="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 http://www.springframework.org/schema/context/spring-context.xsd">

然后,我们需要通过context引入外部文件

在这里插入图片描述
这里要写一个 location 的属性,值就是 classpath: 后面写你的配置文件的地址,因为这里我直接把配置文件写在src下了,所以可以直接写名字。

在这里插入图片描述
这里的${} 是Spring的一个表达式,我们直接在里面填properties文件中的键就可以了。

最终写下来就是这样👇
在这里插入图片描述

B. 基于注解的Bean管理

先来回顾一下Java基础,什么叫注解。

注解其实就是代码的特殊标记,格式形如:

@注解名称(属性名称=属性值, 属性名称=属性值, 属性名称=属性值...)

注解可以有属性也可以没有属性。像我们经常用的覆盖方法的标志,Override 就是一个注解。

在这里插入图片描述
注解可以使用在类上,也可以使用在方法、参数上。Spring 使用注解的目的就是为了简化 xml 文件的配置。

在Spring中创建对象有四个注解:@Component@Service@Controller@Repository

这四个注解的功能是一样的,都可以用来创建 bean 实例。

a. 创建对象

要通过注解创建对象,在xml引入的依赖的基础上,我们还需要再导入一个jar包。aop

在这里插入图片描述
接下来,我们需要开启组件扫描,这个需要在配置文件中配置一下,只有这样Spring才能知道它需要扫描哪个包,不然Spring是不知道你写了什么注解的。

要配置的话,需要先引入 context 命名空间,这个在上面已经用过一次了,我直接将配置文件代码贴上。

<?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:context="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 http://www.springframework.org/schema/context/spring-context.xsd">


</beans>

要让Spring进行包扫描,我们现在需要给Spring提供一个包名,告诉它需要扫什么。

比如我现在要扫 dao 和 service 两个包。
在这里插入图片描述
那么一共有两种写法。

第一个方法是直接用逗号隔开,写两个路径;

在这里插入图片描述
第二个方法,直接写上一级的包名,这样就会扫描这个包里的所有包了。
在这里插入图片描述
接下来我们就需要创建一个类了,然后给它添加注解。

在这里插入图片描述
由于我们注解是直接加在类上的,所以可以不用写class属性了,这就比xml文件方便了很多。

并且,用 Component 注解,value 都可以不写,如果你不写 value 的话,默认就是你的首字母小写的类名。

也就是说,这两个写法的结果是一样的,都会有一个 userService 的 id.

在这里插入图片描述
我们来测试一下。

在这里插入图片描述
上面我们说了,那四个注解的功能都是一样的,所以你当然也可以替换成其他几个注解,效果是相同的,不过我们一般习惯上还是写的 @Component

在这里插入图片描述

b. 组件扫描细节配置

在上面的组件扫描开启配置,我们只是简单地写了一下需要扫描的包,

在这里插入图片描述

其实我们也可以通过配置,再细化,配置出扫描或不扫描哪些类。

在这里插入图片描述
首先要把默认的过滤器关闭,意思是我们需要自己实现一个过滤器。
在这里插入图片描述
中间的两行就是自己实现的过滤器,我们做的就是让Spring只扫描被Controller 注解的类,其他的注解都不管。

同样,我们可以再设置一个扫描所有类,唯独不去扫描被 Controller 注解的类的配置。和上面的方法差不多,只不过这个我们就不需要再把默认的过滤器关掉了,因为默认的就是扫描所有的。

在这里插入图片描述
包含是 include,不包含就是 exclude,要写排除在外的就把 include 替换成 exclude 就可以了。

c. 属性注入

属性注入一共有三个常用的注解: @Autowired@Qualifier@Resource

@Autowired 表示根据属性的类型进行自动注入。

@Qualifier 表示根据属性名称进行注入。

@Resource 可以根据名称注入,也可以根据类型注入。

要说明的是,这三个注解都是注入的引用类型,也就是一个对象,而基本的八大类型的注入,需要的是 @Value 注解。

① Autowired

现在我们来看一个case,我建立两个类,一个UserService 一个 UserDao,然后在 UserService 中调用 UserDao 的方法。我们做一个 @Autowired 的演示。

先实现一个 UserDao 的接口。

在这里插入图片描述
然后在Dao层再写一个 UserDao 的实现类 UserDaoImpl.

在这里插入图片描述

要在 UserService 中 使用 UserDao,我们需要先创建它们的对象。那就用前面说的四个创建对象的注解就好了。

在这里插入图片描述
注意这里要创建的是 UserDao 的实现类,而不是接口,所以在实现类的上面加注解。
在这里插入图片描述
然后我们需要在 UserService 中加一个 UserDao 的成员,并对它进行注入。

在这里插入图片描述
这里IDE还给我了一个提示,

在这里插入图片描述
意思是不推荐这样注入属性。可以是可以,但是不推荐。

那怎么样好一点呢?只要用构造器注入就好了。作为一个菜鸟,大佬说什么好咱们听大佬的就好了。我们来改一下。

在这里插入图片描述
注入成功

在这里插入图片描述

② Qualifier

我们再来看看 @Qualifier,前面说了它可以根据 属性名称进行注入,要注意的是这个注解要和上面的 Autowired 一起使用。

就像在 xml 中配置一样,根据类型的话,如果 UserDao 接口有多个实现类,那 Autowired 是没办法找到的,但是根据名字就可以。

比如我现在给 UserDao 的实现类命名一下

在这里插入图片描述
注意,这个默认不写的话,就是类名并且首字母小写。这里为了演示我写一个名字。

@Qualifier 是不能注解到构造方法上的,所以为了配合它我还是直接写到外面,我们忽略一下 Autowired 注解。
在这里插入图片描述
运行结果和刚才一样,我就不再贴了,这里我们可以把 Autowired 删掉运行一下。

在这里插入图片描述
报了空指针异常。所以 @Qualifier 必须要和 Autowired 一起使用才可以,对应的情况就是可能 UserDao 有多个实现类,这时候就需要根据名字才能找到不同的实现类了。

③ Resource

如果什么都不写的话,默认 Resource 是根据类型来注入的。

在这里插入图片描述
运行正常。

在这里插入图片描述
Resource 还有一个 name 属性,是根据名称来进行注入的。

在这里插入图片描述
这里还有一个需要注意的地方,大家看我框的包路径

在这里插入图片描述
其他几个注解都是 springframework 里的,但是 Resource 注解是 Java 的扩展包 javax.annotation 里的,所以它并不是 Spring 的东西,虽然可以实现一样的功能,但是官方不建议使用。

④ Value

最后我们再来看一下普通类型的注入,非常简单。

在这里插入图片描述
直接在 Value 注解的 value 属性中填入值就好了。

在这里插入图片描述

d. 完全注解开发

在一开始,我们在xml文件中写了一个配置,就是开启Spring的包扫描。

在这里插入图片描述
要做到完全注解开发,我们一条xml配置都不用写。那怎么做?我们需要写一个配置类,用这个配置类来取代 xml 的配置文件。

在这里插入图片描述
配置类我们需要用 @Configuration 注解标明,表示这个类是做配置的。

同样,我们需要提供给 Spring 一个要扫描的包。
在这里插入图片描述
这样我们就可以直接不用 xml 文件了。这样在测试的时候需要改变一点。

在这里插入图片描述

在找上下文的时候我们用 AnnotationConfigApplicationContext方法,并把我们自己写的配置类传进去。可以看到运行的结果是一样的。

至此,Spring 关于 IOC 的一切就讲完了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值