【Spring】(4)IoC控制反转:一文看懂如何使用注解进行依赖注入(DI)

本文详细介绍了如何通过Spring框架的注解如@Component、@Autowired、@Value实现基于注解的依赖注入,包括组件扫描器的配置、不同注解的使用场景和对比。适合初学者通过代码实践理解Spring的DI机制。

两个月前跟着b站动力节点王鹤老师的Spring框架教程视频学习完了Spring框架,在学习过程中我将视频中涉及的代码都一行一行手敲了一遍,并且把Spring入门相关的资料整理了一下,在这里记录一下我在Spring框架学习过程中的笔记和一些心得,希望能帮助到想要通过文字教程入门Spring这一框架的小伙伴哦!

视频地址:2020最新Spring框架教程【IDEA版】-Spring框架从入门到精通

基于注解的 DI

通过注解完成 java 对象创建,属性赋值。

使用注解的步骤:

  1. 加入maven的依赖 spring-context ,在加入 spring-context 的同时,会间接加入 spring-aop 的依赖。使用注解必须使用到 spring-aop 依赖

  2. 在类中加入 spring 的注解(有多个不同功能的注解)

  3. 在 spring 的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置

学习的注解:
1.@Component
2.@Repository
3.@Service
4.@Controller
5.@Value
6.@Autowired
7.@Resource

1. 组件扫描器

对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。Spring 中使用注解,需要在原有 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: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 											https://www.springframework.org/schema/context/spring-context.xsd">

    <!--声明组件扫描器(component-scan),组件就是java对象
        base-package:指定项目中注解所在的包的包名
        component-scan工作方式 : spring会扫描遍历base-package指定的包,找到包和子包中的所有类中的注解,按照注解的功能创建对象,或给属性赋值。

		加入了component-scan标签,配置文件的变化:
        1.加入一个新的约束文件spring-context.xsd
        2.给这个新的约束文件起个命名空间的名称
    -->
    <context:component-scan base-package="com.kaho.di01" />
</beans>

指定多个包的三种方式:

1)使用多个 context:component-scan 指定不同的包路径

    <context:component-scan base-package="com.kaho.beans" />
    <context:component-scan base-package="com.kaho.vo" />

2)指定 base-package 的值使用分隔符

分隔符可以使用逗号分号)还可以使用空格,不建议使用空格。

逗号分隔:

    <context:component-scan base-package="com.kaho.beans,com.kaho.vo" />

分号分隔:

    <context:component-scan base-package="com.kaho.beans;com.kaho.vo" />

3)base-package 是指定到父包名

base-package 的值表示基本包,容器启动会扫描包及其子包中的注解,当然也会扫描到子包下级的子包。所以 base-package 可以指定一个父包就可以。

    <context:component-scan base-package="com.kaho" />

或者最顶级的父包

    <context:component-scan base-package="com" />

但不建议使用顶级的父包,扫描的路径比较多,导致容器启动时间变慢。

以下每一个 di 包用于讲解一种注解

2. 定义 Bean 的注解 @Component

需要在类上使用注解**@Component**,该注解的 value 属性用于指定该 bean 的 id 值

Student 类:

package com.kaho.di01;

import org.springframework.stereotype.Component;

/**
 * @Component : 创建对象的,等同于<bean>的功能
 *      属性 : value 就是对象的名称,也就是 bean 的 id 值
 *            value 的值是唯一的,创建的对象在整个spring容器中就只有一个
 *      位置 : 在类的上面
 *
 * @Component(value = "myStudent") 等同于
 *  <bean id="myStudent" class="com.kaho.di01.Student" />
 *
 * 三种写法:
 * 1.@Component(value = "myStudent")
 * 2.@Component("myStudent")  只有一个value属性是可以省略 value= 的
 * 3.@Component  不指定对象名称,由spring提供默认名称:首字母小写的类名(即student)
 */
@Component("myStudent")
public class Student {
    private String name;
    private Integer age;
    public Student(){
        System.out.println("====Student无参构造方法====");
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

applicationContext.xml :

    <context:component-scan base-package="com.kaho.di01" />

测试方法:

    @Test
    public void test(){
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取对象
        Student student = (Student) ctx.getBean("myStudent");

        System.out.println("student=" + student);
    }

输出结果:

====Student无参构造方法====
student=Student{name='null', age=null}

Process finished with exit code 0

调用的是类的无参构造方法创建对象

spring 中和 @Component 功能一致,用于创建对象的注解还有:

  1. @Repository(用在持久层类的上面):放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的

  2. @Service(用在业务层类的上面):放在service的实现类上面,创建service对象,service对象是做业务处理,可以有事务等功能的

  3. @Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,控制器对象能够接受用户提交的参数,显示请求的处理结果

以上三个注解的使用语法和 @Component 一样,都能创建对象,但是这三个注解还有额外的功能。

@Repository@Service@Controller给项目的对象分层的。

3. 简单类型属性注入 @Value

需要在属性上使用注解 @Value,该注解的 value 属性用于指定要注入的值

使用该注解完成属性注入时,类中无需 setter,直接将注解写在属性上面即可。当然,若属性有 setter,则也可将其加到 setter 上。

Student 类:

package com.kaho.di02;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myStudent")
public class Student {

    /**
     * @Value : 简单类型的属性赋值
     *    属性 : value 是String类型的,表示简单类型的属性值
     *    位置 : 1.在属性定义的上面,无需 set 方法,推荐使用
     *          2.在 set 方法的上面
     */
    @Value(value = "张飞")
    private String name;
    @Value(value = "29")
    private Integer age;
    
    public Student(){
        System.out.println("====Student无参构造方法====");
    }

//    public void setName(String name) {
//        this.name = name;
//    }
//
//    public void setAge(Integer age) {
//        this.age = age;
//    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

applicationContext.xml :

    <context:component-scan base-package="com.kaho.di02" />

测试方法:

    @Test
    public void test(){
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取对象
        Student student = (Student) ctx.getBean("myStudent");

        System.out.println("student=" + student);
    }

输出结果:

====Student无参构造方法====
student=Student{name='张飞', age=29}

Process finished with exit code 0

4. byType 自动注入 @Autowired

需要在引用属性上使用注解 @Autowired,该注解默认使用按类型自动装配 Bean 的方式。

使用该注解完成属性注入时,类中无需 setter,直接将注解写在属性上面即可。当然,若属性有 setter,则也可将其加到 setter 上。

Student 类:

package com.kaho.di03;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myStudent")
public class Student {
    @Value(value = "关羽")
    private String name;
    @Value(value = "30")
    private Integer age;

    /**
     * 引用类型
     * @Autowired : spring框架提供的注解,实现引用类型的赋值
     * spring中通过注解给引用类型赋值,使用的是自动注入原理,支持 byName、byType
     * @Autowired 默认使用的是 byType 自动注入
     *
     *   属性:required 是一个boolean类型的,默认为true —> @Autowired(require = true)
     *        required=true 表示引用类型赋值(匹配)失败后程序报错并终止执行  【推荐使用】
     *        required=false 表示引用类型赋值(匹配)失败后,程序正常执行,引用类型赋为 null
     *
     *   位置 : 1.在属性定义的上面,无需set方法,推荐使用
     *         2.在set方法的上面
     */
    //byType
    @Autowired
    private School school;

    public Student(){
        System.out.println("====Student无参构造方法====");
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

School 类:

package com.kaho.di03;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mySchool")
public class School {

    @Value("深圳大学")
    private String name;
    @Value("深圳市南山区")
    private String address;
//    public void setName(String name) {
//        this.name = name;
//    }
//    public void setAddress(String address) {
//        this.address = address;
//    }
    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

applicationContext.xml :

    <context:component-scan base-package="com.kaho.di03" />

测试方法:

    @Test
    public void test(){
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取对象
        Student student = (Student) ctx.getBean("myStudent");

        System.out.println("student=" + student);
    }

输出结果:

====Student无参构造方法====
student=Student{name='关羽', age=30, school=School{name='深圳大学', address='深圳市南山区'}}

Process finished with exit code 0

5. byName 自动注入 @Autowired 与 @Qualifier

需要在引用属性上联合使用注解 @Autowired@Qualifier@Qualifiervalue 属性用于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。

Student 类:

package com.kaho.di04;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myStudent")
public class Student {
    @Value(value = "刘备")
    private String name;
    @Value(value = "31")
    private Integer age;
    /**
     *   如果要使用 byName 方式,需要做的是:
     *   1.在属性上面加入 @Autowired
     *   2.在属性上面加入 @Qualifier(value="bean的id") : 表示使用指定名称的bean完成赋值
     */
    //byName
    @Autowired
    @Qualifier(value = "mySchool")
    private School school;

    public Student(){
        System.out.println("====Student无参构造方法====");
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

School 类:

package com.kaho.di04;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mySchool")
public class School {

    @Value("广州大学")
    private String name;
    @Value("广州市番禺区")
    private String address;

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

applicationContext.xml :

    <context:component-scan base-package="com.kaho.di04" />

测试方法:

    @Test
    public void test(){
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取对象
        Student student = (Student) ctx.getBean("myStudent");

        System.out.println("student=" + student);
    }

输出结果:

====Student无参构造方法====
student=Student{name='刘备', age=31, school=School{name='广州大学', address='广州市番禺区'}}

Process finished with exit code 0

6. JDK 注解 @Resource 自动注入

Spring 提供了对 jdk 中 @Resource 注解的支持。@Resource 注解既可以按名称匹配Bean,也可以按类型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。@Resource 可在属性上,也可在 set 方法上。

1)byType 注入引用类型属性

@Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,则会按照类型进行 Bean 的匹配注入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MzBX2ZVb-1633536701251)( https://static01.imgkr.com/temp/d660ea55ea7e42ada73249fc4865c403.png )]

2)byName 注入引用类型属性

@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xl3AuObm-1633536701255)( https://static01.imgkr.com/temp/0a641c02b4a3416f8c85ac6a0a96f62b.png )]

7. 注解与 XML 的对比

注解优点是:

⚫ 方便

⚫ 直观

⚫ 高效(代码少,没有配置文件的书写那么复杂)。

缺点:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。

XML 方式优点是:

⚫ 配置和代码是分离的

⚫ 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。

缺点:编写麻烦,效率低,大型项目过于复杂。

需要常改动的用 xml,不经常改动的用 注解

拓展:

注解 + properties配置文件 方式进行注入

假设在resources目录下有用于存放 属性与其值(key = value) 的配置文件 test.properties

myname=张三
myage=20

在 applicationContext.xml 中加上:

	<context:property-placeholder location="classpath:test.properties" />

在类中使用注解进行赋值:

@Value("${myname}")
private String name;
@Value("${myage}")
private Integer age;
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kaho Wang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值