从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • RSS
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金等
  • 职场规划
  • 关于离职
  • 杂谈
  • 📖 读书

    • 读书工具
    • 读书笔记
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • RSS
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金等
  • 职场规划
  • 关于离职
  • 杂谈
  • 📖 读书

    • 读书工具
    • 读书笔记
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

    • Spring 介绍
    • 程序中的耦合
    • IoC 的概念和作用
    • Spring 中的依赖注入
    • 基于注解的 IoC
      • 前言
      • 调整代码
      • 注解的分类
      • 用于创建对象的注解
      • 用于注入数据的注解
      • 用于改变作用范围的注解@Scope
      • 和生命周期相关的注解(了解即可)
      • 源码
    • 使用基于 XML 的 IoC 完成单表的 CRUD
    • 使用基于注解的 IoC 完成单表的 CRUD
    • IoC 的纯注解配置
    • Spring 整合 Junit
    • 事务问题
    • 代理模式
    • AOP 的概念和入门
    • 基于注解的 AOP
    • Spring 的 JdbcTemplate
    • JdBCDaoSupport
    • 基于 XML 的 AOP 实现事务控制
    • 基于注解的 AOP 实现事务控制
    • Spring 的事务控制
    • 基于 XML 的声明式事务控制
    • 基于注解的声明式事务控制
    • 纯注解实现事务控制
    • Spring 编程式事务控制
    • Spring5 新特性
  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java
  • Spring
2023-05-08
目录

基于注解的 IoC

# 40.基于注解的 IoC

本文就来讲解下,如何使用注解来完成 IoC。 ‍

# 前言

学习基于注解的 IoC 配置之前,大家脑海里首先得有一个认知,即注解配置和 项目 XML 配置要实现的功能都是一样的,都是要降低程序间的耦合,只是配置的形式不一样。

关于实际的开发中到底使用 XML 还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。

我们在讲解注解配置时,采用上一篇博客的案例,把 Spring 的 XML 配置内容改为使用注解逐步实现。

注解的优势:配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。

XML 的优势:修改时,不用改源码。不涉及重新编译和部署 ‍

# 调整代码

我们将界面层的代码还原成最开始的模样:

public class Client {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = (IAccountService)ac.getBean("accountService");
        System.out.println(as);
    }
}
1
2
3
4
5
6
7

‍ 调整 service 实现类,去掉构造函数:

public class AccountServiceImpl implements IAccountService {

    private String name;
    private Integer age;
    private Date birthday;

    @Override
    public void saveAccount() {
        System.out.println("service中的saveAccount方法执行了");
    }
}

1
2
3
4
5
6
7
8
9
10
11
12

‍ 接下来我们可以删除 bean.xml 中的配置的所有内容。 ‍

# 注解的分类

我们可以根据曾经的基于 XML 的配置:

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
         scope=""  init-method="" destroy-method="">
       <property name=""  value="" | ref=""></property>
</bean>
1
2
3
4

将注解分为 4 类:

  • 用于创建对象的:作用和在 XML 配置文件中,编写一个 <bean> 标签实现的功能是一样的
  • 用于注入数据的:作用和在 XML 配置文件中,bean 标签中写一个 <property> 标签的作用是一样的
  • 用于改变作用范围的:作用和在 bean 标签中,使用 scope 属性实现的功能是一样的
  • 和生命周期相关:作用和在 bean 标签中,使用 init-method 和 destroy-method 的作用是一样的

# 用于创建对象的注解

# @Component 注解

@Component:用于把当前类对象存入 spring 容器中,相当于:<bean id = "" class = "">

属性:value 用于指定 bean 的 id。当我们不写时,它的默认值是当前类名,首字母改小写。

书写位置:在类的定义前。由于我们是写在类的前面,所以 class 属性就不用写了

示例:

@Component("accountService")
public class AccountServiceImpl implements IAccountService {
    private String name;
    private Integer age;
    private Date birthday;

    @Override
    public void saveAccount() {
        System.out.println("service中的saveAccount方法执行了");
    }
}

1
2
3
4
5
6
7
8
9
10
11
12

# 在 bean.xml 中配置注解所在包

使用注解后,需要在配置文件里标明哪里用了注解,约束也要设置,否则是无法运行的。因此我们需要告知 Spring,在创建容器时要扫描的包。

配置所需要的标签,不在 beans 的名称空间和约束中,而是一个名称为 context 名称空间和约束中。

寻找约束:和昨天一样,我们可以在文档中寻找约束,在 Core 模块中搜索 xmlns:context:

‍

我们复制该约束,然后加上 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:component-scan base-package="com.peterjxl"/>
</beans>
1
2
3
4
5
6
7
8
9
10
11

‍ 再次运行 Client,可以看到能正常运行,说明使用注解改造成功了。

# @Component 的衍生注解

除了@Component 注解可以用来创建 bean 之外,还有 3 个标签可以表示创建 bean:

  • @Controller:一般用在表现层
  • @Service:一般用在业务层
  • @Repository:一般用在持久层

他们三个注解都是@Component 的衍生注解,他们的作用及属性都是一模一样的,只不过提供了更加明确的语义化。 ‍ 改造:我们将 service 层的代码改为@Service 注解

@Service("accountService")
public class AccountServiceImpl implements IAccountService 
1
2

‍ 同理,在 Dao 实现类使用@Repository

@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
    @Override
    public void saveAccount() {
        System.out.println("保存了账户");
    }
}
1
2
3
4
5
6
7

‍ 试着打印和获取:

public class Client {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = (IAccountService)ac.getBean("accountService");
        System.out.println(as);

        IAccountDao accountDao = ac.getBean("accountDao", IAccountDao.class);
        System.out.println(accountDao);
    }
}
1
2
3
4
5
6
7
8
9
10
11

‍ 运行结果:可以正常创建对象

com.peterjxl.service.impl.AccountServiceImpl@aecb35a
com.peterjxl.dao.impl.AccountDaoImpl@5fcd892a
1
2

# 用于注入数据的注解

接下来我们试着在 service 层中,调用 dao 层的方法,此时我们需要注入 dao 对象

# @Autowired

@Autowired 的作用,就和在 XML 配置文件中的 bean 标签中写一个 <property> 标签的作用是一样的。它会自动按照类型注入,只要容器中有唯一的一个 bean 对象类型和要注入的变量类型匹配(及时是实现类),就可以注入成功。此时会跳过 beanID,直接寻找容器中,哪个对象的类型匹配。

出现位置:可以是变量上,也可以是方法上 ‍ 我们可以试着在 service 层中,调用 dao 层的方法,并使用@Autowired 注解:

@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;

    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
1
2
3
4
5
6
7
8
9
10
11

然后我们试着调用,是可以正常运行的:

public class Client {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = (IAccountService)ac.getBean("accountService");
        as.saveAccount();
    }
}
1
2
3
4
5
6
7

# @Autowired 的细节

  1. 在使用注解注入时,set 方法就不是必须的了。
  2. 如果 IoC 容器中没有任何 bean 的类型和要注入的变量类型匹配,则报错。
  3. 如果 IoC 容器中有多个类型匹配时,首先按照类型选出所有匹配的,然后根据变量名和 BeanID 是否一样,都不一样则报错。 ‍ 我们可以演示下:修改 IAccountDao 的变量名为 dao:
@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao dao;

    @Override
    public void saveAccount() {
        dao.saveAccount();
    }
}
1
2
3
4
5
6
7
8
9
10
11

然后复制一个新的类:AccountDaoImpl2,并增加构造方法、修改 beanID:

@Repository("accountDao2")
public class AccountDaoImpl2 implements IAccountDao {
  
    public AccountDaoImpl2() {
        System.out.println("AccountDaoImpl2对象创建了");
    }
    @Override
    public void saveAccount() {
        System.out.println("保存了账户2222");
    }
}
1
2
3
4
5
6
7
8
9
10
11

再创建一个新的类:AccountDaoImpl3,,并增加构造方法、修改 beanID:

@Repository("accountDao3")
public class AccountDaoImpl3 implements IAccountDao {

    public AccountDaoImpl3() {
        System.out.println("AccountDaoImpl3对象创建了");
    }
    @Override
    public void saveAccount() {
        System.out.println("保存了账户3333");
    }
}
1
2
3
4
5
6
7
8
9
10
11

此时我们运行 Client,是会报错的:

No qualifying bean of type 'com.peterjxl.dao.IAccountDao' available: expected single matching bean but found 3: accountDao,accountDao2,accountDao3
1

大意:期望能找到一个匹配的,但是找到了 3 个。

如果我们修改 service 层中,变量名为 accountDao2:

@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao2;

    @Override
    public void saveAccount() {
        accountDao2.saveAccount();
    }
}
1
2
3
4
5
6
7
8
9
10
11

此时是可以正常运行的,并且输出语句为:保存了账户 2222 ‍ 也就是说,自动注入的逻辑

  1. 寻找容器中所有匹配的对象
  2. 如果有一个,则直接使用
  3. 如果有多个,则根据变量名和 key 名是否一样 ‍

# @Qualifier

作用:在按照类中注入的基础之上,再按照名称注入。它在给类成员注入时不能单独使用,需要和 Autowired 配合独立使用,但是在给方法参数注入时可以独立使用。

属性:value,用于指定注入 bean 的 id。 ‍ 例如,我们可以指定注入 accountDao3 对象:

@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    @Qualifier("accountDao3")
    private IAccountDao accountDao2;

    @Override
    public void saveAccount() {
        accountDao2.saveAccount();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

此时的运行结果:保存了账户 3333,也就是调用了 accountDao3 的方法。

# @Resource

@Autowired 自动注入的话得变量名和 beanid 一致,而@Qualifier 又不能独立使用,有没一个一步到位的注解呢?有的,那就是@Resource。

作用:直接按照 bean 的 id 注入。它可以独立使用

属性:name,用于指定 bean 的 id。

@Resource(name = "accountDao2")
private IAccountDao accountDao2;
1
2

# 基本类型的注入@Value

以上三个注解,都只能注入其他 bean 类型的数据,集合类型的注入只能通过 XML 来实现。基本类型和 String 类型无法使用上述注解实现的,我们得用@Value 注解。

作用:用于注入基本类型和 String 类型的数据

属性:value 用于指定数据的值。可使用 Spring 中 SpEL(也就是 Spring 的 el 表达式,SpEL 的写法:${表达式},JSP、Mybatis 也有 EL 表达式) ‍

# 用于改变作用范围的注解@Scope

作用:用于指定 bean 的作用范围,就和在 bean 标签中使用 scope 属性实现的功能是一样的,默认是单例

属性:value 指定范围的取值。常用取值:singleton prototype

@Scope("prototype")
1

‍

# 和生命周期相关的注解(了解即可)

作用:就和在 bean 标签中使用 init-method 和 destroy-method 的作用是一样的

@PreDestroy 作用:用于指定销毁方法

@PostConstruct 作用:用于指定初始化方法

@PostConstruct
public void  init(){
    System.out.println("初始化方法执行了");
}

@PreDestroy
public void  destroy(){
    System.out.println("销毁方法执行了");
}
1
2
3
4
5
6
7
8
9

# 源码

本项目已将源码上传到 GitHub (opens new window) 和 Gitee (opens new window) 上。并且创建了分支 demo4,读者可以通过切换分支来查看本文的示例代码。

上次更新: 2025/5/17 12:26:09
Spring 中的依赖注入
使用基于 XML 的 IoC 完成单表的 CRUD

← Spring 中的依赖注入 使用基于 XML 的 IoC 完成单表的 CRUD→

最近更新
01
吐槽一下《僵尸校园》
05-15
02
2025 年 4 月记
04-30
03
山西大同 “订婚强奸案” 将会给整个社会带来的影响有多严重? - 知乎 转载
04-26
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式