• Spring6(二):IoC容器


    文章目录

    3. 容器:IoC

    Inversion of Control ,“控制反转”。

    它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。

    Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。

    IoC 容器是 Spring 框架中最重要的核心组件之一,它贯穿了 Spring 从诞生到成长的整个过程。

    3.1 IoC容器

    3.1.1 控制反转(IoC)

    • 控制反转是一种思想。是为了降低程序耦合度,提高程序扩展力。
    • 控制反转,反转的是什么?
      • 将对象的创建权利交出去,交给第三方容器负责。
      • 将对象和对象之间关系的维护权交出去,交给第三方容器负责。
    • 控制反转这种思想如何实现呢?:DI(Dependency Injection),依赖注入

    3.1.2 依赖注入

    DI(Dependency Injection),指Spring创建对象的过程中,将对象依赖属性通过配置进行注入

    常见的实现方式:

    • set注入
    • 构造注入

    结论:IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。

    Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。

    3.1.3 IoC容器在Spring的实现

    Spring 的 IoC 容器就是 IoC思想的一个落地的产品实现。IoC容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建IoC 容器。Spring 提供了IoC 容器的两种实现方式:

    ①BeanFactory:这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。

    ②ApplicationContext:BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。

    ApplicationContext的主要实现类

    在这里插入图片描述

    类型名简介
    ClassPathXmlApplicationContext通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
    FileSystemXmlApplicationContext通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
    ConfigurableApplicationContextApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。
    WebApplicationContext专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。

    3.2 基于XML管理Bean

    3.2.1 搭建子模块spring6-ioc-xml

    ②引入配置文件

    引入spring-first模块配置文件:beans.xml、log4j2.xml

    ③添加依赖

    pom.xml

    <dependencies>
        
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>6.0.3version>
        dependency>
    
        
        <dependency>
            <groupId>org.junit.jupitergroupId>
            <artifactId>junit-jupiter-apiartifactId>
            <version>5.3.1version>
        dependency>
    
        
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-coreartifactId>
            <version>2.19.0version>
        dependency>
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-slf4j2-implartifactId>
            <version>2.19.0version>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    ④引入java类

    引入spring-first模块java及test目录下实体类

    HelloWorld.java

    package com.atguigu.spring6.bean;
    
    public class HelloWorld {
    
        public HelloWorld() {
            System.out.println("无参数构造方法执行");
        }
    
        public void sayHello(){
            System.out.println("helloworld");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    test/HelloWorldTest.java

    package com.atguigu.spring6.bean;
    
    import org.junit.jupiter.api.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class HelloWorldTest {
    
        private Logger logger = LoggerFactory.getLogger(HelloWorldTest.class);
    
        @Test
        public void testHelloWorld(){
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.2.2 获取bean

    方式一:根据id获取

    由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。上个实验中我们使用的就是这种方式。

    HelloWorldTest.java

    @Test
    public void testHelloWorld1(){
    	ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        HelloWorld bean = ac.getBean("helloworld");
        bean.sayHello();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    方式二:根据类型获取

    HelloWorldTest.java

    @Test
    public void testHelloWorld1(){
    	ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        HelloWorld bean = ac.getBean(HelloWorld.class);
        bean.sayHello();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意:当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个

    当IOC容器中一共配置了两个:

    <bean id="helloworldOne" class="com.atguigu.spring6.bean.HelloWorld">bean>
    <bean id="helloworldTwo" class="com.atguigu.spring6.bean.HelloWorld">bean>
    
    • 1
    • 2

    根据类型获取时会抛出异常:

    org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.atguigu.spring6.bean.HelloWorld’ available: expected single matching bean but found 2: helloworldOne,helloworldTwo

    方式三:根据id和类型

    HelloWorldTest.java

    @Test
    public void testHelloWorld2(){
    	ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        HelloWorld bean = ac.getBean("helloworld", HelloWorld.class);
        bean.sayHello();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.2.3 依赖注入

    ①创建学生类Student

    package com.atguigu.spring6.bean;
    
    public class Student {
    
        private Integer id;
    
        private String name;
    
        private Integer age;
    
        private String sex;
    	//省略无参,get,set,toString()     注意没有有参
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    方式一:setter注入

    ②配置bean时为属性赋值

    spring-di.xml

    <bean id="studentOne" class="com.atguigu.spring6.bean.Student">
        
        
        
        <property name="id" value="1001">property>
        <property name="name" value="张三">property>
        <property name="age" value="23">property>
        <property name="sex" value="">property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ③测试

    @Test
    public void testDIBySet(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-di.xml");
        Student studentOne = ac.getBean("studentOne", Student.class);
        System.out.println(studentOne);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    方式二:构造器注入

    ①在Student类中添加有参构造

    public Student(Integer id, String name, Integer age, String sex) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ②配置bean

    spring-di.xml

    <bean id="studentTwo" class="com.atguigu.spring6.bean.Student">
        <constructor-arg value="1002">constructor-arg>
        <constructor-arg value="李四">constructor-arg>
        <constructor-arg value="33">constructor-arg>
        <constructor-arg value="">constructor-arg>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意:

    constructor-arg标签还有两个属性可以进一步描述构造器参数:

    • index属性:指定参数所在位置的索引(从0开始)
    • name属性:指定参数名

    ③测试

    @Test
    public void testDIByConstructor(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-di.xml");
        Student studentOne = ac.getBean("studentTwo", Student.class);
        System.out.println(studentOne);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.2.4 特殊值处理

    (1)字面量赋值
    
    <property name="name" value="张三"/>
    
    • 1
    • 2
    (2)null值
    <property name="name">
        <null />
    property>
    
    • 1
    • 2
    • 3

    注意:

    <property name="name" value="null">property>
    
    • 1

    以上写法,为name所赋的值是字符串null

    (3)xml实体
    
    
    <property name="expression" value="a < b"/>
    
    • 1
    • 2
    • 3
    (4)CDATA节
    <property name="expression">
        
        
        
        
        <value>value>
    property>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.2.5 为对象类型属性赋值

    ①创建班级类Clazz

    怕和关键字class冲突,这里取名为Clazz

    package com.atguigu.spring6.bean
        
    public class Clazz {
    
        private Integer clazzId;
    
        private String clazzName;
    
        public Integer getClazzId() {
            return clazzId;
        }
    
        public void setClazzId(Integer clazzId) {
            this.clazzId = clazzId;
        }
    
        public String getClazzName() {
            return clazzName;
        }
    
        public void setClazzName(String clazzName) {
            this.clazzName = clazzName;
        }
    
        @Override
        public String toString() {
            return "Clazz{" +
                    "clazzId=" + clazzId +
                    ", clazzName='" + clazzName + '\'' +
                    '}';
        }
    
        public Clazz() {
        }
    
        public Clazz(Integer clazzId, String clazzName) {
            this.clazzId = clazzId;
            this.clazzName = clazzName;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    ②修改Student类

    在Student类中添加以下代码:

    //在Student类中添加了对象类型属性
    private Clazz clazz;
    
    public Clazz getClazz() {
    	return clazz;
    }
    
    public void setClazz(Clazz clazz) {
    	this.clazz = clazz;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    方式一:引用外部bean

    bean.xml

    配置Clazz类型的bean:

    <bean id="clazzOne" class="com.atguigu.spring6.bean.Clazz">
        <property name="clazzId" value="1111">property>
        <property name="clazzName" value="财源滚滚班">property>
    bean>
    
    • 1
    • 2
    • 3
    • 4

    为Student中的clazz属性赋值:

    <bean id="studentFour" class="com.atguigu.spring6.bean.Student">
        <property name="id" value="1004">property>
        <property name="name" value="赵六">property>
        <property name="age" value="26">property>
        <property name="sex" value="">property>
        
        <property name="clazz" ref="clazzOne">property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    错误演示:

    <bean id="studentFour" class="com.atguigu.spring6.bean.Student">
        <property name="id" value="1004">property>
        <property name="name" value="赵六">property>
        <property name="age" value="26">property>
        <property name="sex" value="">property>
        
        <property name="clazz" value="clazzOne">property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果错把ref属性写成了value属性,会抛出异常: Caused by: java.lang.IllegalStateException: Cannot convert value of type ‘java.lang.String’ to required type ‘com.atguigu.spring6.bean.Clazz’ for property ‘clazz’: no matching editors or conversion strategy found

    意思是不能把String类型转换成我们要的Clazz类型,说明我们使用value属性时,Spring只把这个属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到bean来赋值

    方式二:内部bean
    <bean id="studentFour" class="com.atguigu.spring6.bean.Student">
        <property name="id" value="1004">property>
        <property name="name" value="赵六">property>
        <property name="age" value="26">property>
        <property name="sex" value="">property>
        <property name="clazz">
            
            
            <bean id="clazzInner" class="com.atguigu.spring6.bean.Clazz">
                <property name="clazzId" value="2222">property>
                <property name="clazzName" value="远大前程班">property>
            bean>
        property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    方式三:级联属性赋值
    <bean id="studentFour" class="com.atguigu.spring6.bean.Student">
        <property name="id" value="1004">property>
        <property name="name" value="赵六">property>
        <property name="age" value="26">property>
        <property name="sex" value="">property>
        
        <property name="clazz" ref="clazzOne">property>
        <property name="clazz.clazzId" value="3333">property>
        <property name="clazz.clazzName" value="最强王者班">property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.2.6 为数组类型属性赋值

    ①修改Student类

    在Student类中添加以下代码:

    private String[] hobbies;
    
    public String[] getHobbies() {
        return hobbies;
    }
    
    public void setHobbies(String[] hobbies) {
        this.hobbies = hobbies;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ②配置bean

    <bean id="studentFour" class="com.atguigu.spring.bean6.Student">
        <property name="id" value="1004">property>
        <property name="name" value="赵六">property>
        <property name="age" value="26">property>
        <property name="sex" value="">property>
       
        <property name="hobbies">
            <array>
                <value>抽烟value>
                <value>喝酒value>
                <value>烫头value>
            array>
        property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3.2.7 为集合类型属性赋值

    (1)为List集合类型属性赋值

    在Clazz类中添加以下代码:

    private List<Student> students;
    
    public List<Student> getStudents() {
        return students;
    }
    
    public void setStudents(List<Student> students) {
        this.students = students;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    配置bean:

    <bean id="clazzTwo" class="com.atguigu.spring6.bean.Clazz">
        <property name="clazzId" value="4444">property>
        <property name="clazzName" value="Javaee0222">property>
        
        <property name="students">
            <list>
                <ref bean="studentOne">ref>
                <ref bean="studentTwo">ref>
                <ref bean="studentThree">ref>
            list>
        property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    若为Set集合类型属性赋值,只需要将其中的list标签改为set标签即可

    (2)为Map集合类型属性赋值

    创建教师类Teacher:

    package com.atguigu.spring6.bean;
    public class Teacher {
    
        private Integer teacherId;
    
        private String teacherName;
    	//此处省略了有参,无参构造,get、set、toString方法
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在Student类中添加以下代码:

    private Map<String, Teacher> teacherMap;
    
    public Map<String, Teacher> getTeacherMap() {
        return teacherMap;
    }
    
    public void setTeacherMap(Map<String, Teacher> teacherMap) {
        this.teacherMap = teacherMap;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    配置bean:

    <bean id="teacherOne" class="com.atguigu.spring6.bean.Teacher">
        <property name="teacherId" value="10010">property>
        <property name="teacherName" value="大宝">property>
    bean>
    
    <bean id="teacherTwo" class="com.atguigu.spring6.bean.Teacher">
        <property name="teacherId" value="10086">property>
        <property name="teacherName" value="二宝">property>
    bean>
    
    <bean id="studentFour" class="com.atguigu.spring6.bean.Student">
        <property name="id" value="1004">property>
        <property name="name" value="赵六">property>
        <property name="age" value="26">property>
        <property name="sex" value="">property>
        
        <property name="clazz" ref="clazzOne">property>
        <property name="hobbies">
            <array>
                <value>抽烟value>
                <value>喝酒value>
                <value>烫头value>
            array>
        property>
        
        <property name="teacherMap">
            <map>
                <entry>
                    <key>
                        <value>10010value>   
                    key>
                    <ref bean="teacherOne">ref>  
                entry>
                <entry>
                    <key>
                        <value>10086value>
                    key>
                    <ref bean="teacherTwo">ref>
                entry>
            map>
        property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    (3)引用集合类型的bean
    
    <util:list id="students">
        <ref bean="studentOne">ref>
        <ref bean="studentTwo">ref>
        <ref bean="studentThree">ref>
    util:list>
    
    
    <util:map id="teacherMap">
        <entry>
            <key>
                <value>10010value>
            key>
            <ref bean="teacherOne">ref>
        entry>
        <entry>
            <key>
                <value>10086value>
            key>
            <ref bean="teacherTwo">ref>
        entry>
    util:map>
    
    <bean id="clazzTwo" class="com.atguigu.spring6.bean.Clazz">
        <property name="clazzId" value="4444">property>
        <property name="clazzName" value="Javaee0222">property>
        <property name="students" ref="students">property>
    bean>
    <bean id="studentFour" class="com.atguigu.spring6.bean.Student">
        <property name="id" value="1004">property>
        <property name="name" value="赵六">property>
        <property name="age" value="26">property>
        <property name="sex" value="">property>
        
        
        <property name="clazz" ref="clazzOne">property>
        <property name="hobbies">
            <array>
                <value>抽烟value>
                <value>喝酒value>
                <value>烫头value>
            array>
        property>
        
        <property name="teacherMap" ref="teacherMap">property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    使用util:list、util:map标签必须引入相应的命名空间

    
    <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/util
        http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.2.8 p命名空间

    引入p命名空间

    
    <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"
           xmlns:p="http://www.springframework.org/schema/p"    
    <bean id="studentSix" class="com.atguigu.spring6.bean.Student"
        p:id="1006" p:name="小明" p:clazz-ref="clazzOne" p:teacherMap-ref="teacherMap">bean>
    
    • 1
    • 2
    • 3

    3.2.9 引入jdbc配置文件

    ①加入依赖

     
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>8.0.30version>
    dependency>
    
    
    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>druidartifactId>
        <version>1.2.15version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    ②创建外部属性文件

    在这里插入图片描述

    jdbc.user=root
    jdbc.password=atguigu
    jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
    jdbc.driver=com.mysql.cj.jdbc.Driver
    
    • 1
    • 2
    • 3
    • 4

    ③引入属性文件

    引入context 名称空间

    
    
    <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:property-placeholder location="classpath:jdbc.properties"/>
        
        
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="url" value="${jdbc.url}"/>
            <property name="driverClassName" value="${jdbc.driver}"/>
            <property name="username" value="${jdbc.user}"/>
            <property name="password" value="${jdbc.password}"/>
    	bean>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    注意:在使用 context:property-placeholder 元素加载外包配置文件功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。

    ⑤测试

    @Test
    public void testDataSource() throws SQLException {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-datasource.xml");
        DataSource dataSource = ac.getBean(DataSource.class);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.2.10 bean的作用域

    在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参加下表:

    取值含义创建对象的时机
    singleton(默认)在IOC容器中,这个bean的对象始终为单实例IOC容器初始化时
    prototype这个bean在IOC容器中有多个实例获取bean时

    如果是在WebApplicationContext环境下还会有另外几个作用域(但不常用):

    取值含义
    request在一个请求范围内有效
    session在一个会话范围内有效

    ②创建类User

    package com.atguigu.spring6.bean;
    public class User {
    
        private Integer id;
    
        private String username;
    
        private String password;
    
        private Integer age;
    	//省略了有参、无参构造、get、set、toString()方法
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ③配置bean

    
    
    <bean class="com.atguigu.spring6.bean.User" scope="prototype">bean>
    
    • 1
    • 2
    • 3

    ④测试

    @Test
    public void testBeanScope(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-scope.xml");
        //可以有两个实例
        User user1 = ac.getBean(User.class);
        User user2 = ac.getBean(User.class);
        System.out.println(user1==user2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.2.11 bean生命周期

    ①具体的生命周期过程

    • bean对象创建(调用无参构造器)

    • 给bean对象设置属性

    • bean的后置处理器(初始化之前)

    • bean对象初始化(需在配置bean时指定初始化方法)

    • bean的后置处理器(初始化之后)

    • bean对象就绪可以使用

    • bean对象销毁(需在配置bean时指定销毁方法)

    • IOC容器关闭

    ②修改类User

    public class User {
    
        private Integer id;
    
        private String username;
    
        private String password;
    
        private Integer age;
    
        public User() {
            System.out.println("生命周期:1、创建对象");
        }
    
        public User(Integer id, String username, String password, Integer age) {
            this.id = id;
            this.username = username;
            this.password = password;
            this.age = age;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            System.out.println("生命周期:2、依赖注入");
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public void initMethod(){
            System.out.println("生命周期:3、初始化");
        }
    
        public void destroyMethod(){
            System.out.println("生命周期:5、销毁");
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    注意其中的initMethod()和destroyMethod(),可以通过配置bean指定为初始化和销毁的方法

    ③配置bean

    
    
    <bean class="com.atguigu.spring6.bean.User" scope="prototype" init-method="initMethod" destroy-method="destroyMethod">
        <property name="id" value="1001">property>
        <property name="username" value="admin">property>
        <property name="password" value="123456">property>
        <property name="age" value="23">property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ④测试

    @Test
    public void testLife(){
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
        User bean = ac.getBean(User.class);
        System.out.println("生命周期:4、通过IOC容器获取bean并使用");
        ac.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ⑤bean的后置处理器

    bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行

    创建bean的后置处理器:

    package com.atguigu.spring6.process;
        
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class MyBeanProcessor implements BeanPostProcessor {
        
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("☆☆☆" + beanName + " = " + bean);
            return bean;
        }
        
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("★★★" + beanName + " = " + bean);
            return bean;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在IOC容器中配置后置处理器:

    
    <bean id="myBeanProcessor" class="com.atguigu.spring6.process.MyBeanProcessor"/>
    
    • 1
    • 2

    3.2.12 FactoryBean

    FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

    将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

    /*
     * Copyright 2002-2020 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package org.springframework.beans.factory;
    
    import org.springframework.lang.Nullable;
    
    /**
     * Interface to be implemented by objects used within a {@link BeanFactory} which
     * are themselves factories for individual objects. If a bean implements this
     * interface, it is used as a factory for an object to expose, not directly as a
     * bean instance that will be exposed itself.
     *
     * 

    NB: A bean that implements this interface cannot be used as a normal bean. * A FactoryBean is defined in a bean style, but the object exposed for bean * references ({@link #getObject()}) is always the object that it creates. * *

    FactoryBeans can support singletons and prototypes, and can either create * objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean} * interface allows for exposing more fine-grained behavioral metadata. * *

    This interface is heavily used within the framework itself, for example for * the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the * {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for * custom components as well; however, this is only common for infrastructure code. * *

    {@code FactoryBean} is a programmatic contract. Implementations are not * supposed to rely on annotation-driven injection or other reflective facilities. * {@link #getObjectType()} {@link #getObject()} invocations may arrive early in the * bootstrap process, even ahead of any post-processor setup. If you need access to * other beans, implement {@link BeanFactoryAware} and obtain them programmatically. * *

    The container is only responsible for managing the lifecycle of the FactoryBean * instance, not the lifecycle of the objects created by the FactoryBean. Therefore, * a destroy method on an exposed bean object (such as {@link java.io.Closeable#close()} * will not be called automatically. Instead, a FactoryBean should implement * {@link DisposableBean} and delegate any such close call to the underlying object. * *

    Finally, FactoryBean objects participate in the containing BeanFactory's * synchronization of bean creation. There is usually no need for internal * synchronization other than for purposes of lazy initialization within the * FactoryBean itself (or the like). * * @author Rod Johnson * @author Juergen Hoeller * @since 08.03.2003 * @param the bean type * @see org.springframework.beans.factory.BeanFactory * @see org.springframework.aop.framework.ProxyFactoryBean * @see org.springframework.jndi.JndiObjectFactoryBean */ public interface FactoryBean<T> { /** * The name of an attribute that can be * {@link org.springframework.core.AttributeAccessor#setAttribute set} on a * {@link org.springframework.beans.factory.config.BeanDefinition} so that * factory beans can signal their object type when it can't be deduced from * the factory bean class. * @since 5.2 */ String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; /** * Return an instance (possibly shared or independent) of the object * managed by this factory. *

    As with a {@link BeanFactory}, this allows support for both the * Singleton and Prototype design pattern. *

    If this FactoryBean is not fully initialized yet at the time of * the call (for example because it is involved in a circular reference), * throw a corresponding {@link FactoryBeanNotInitializedException}. *

    As of Spring 2.0, FactoryBeans are allowed to return {@code null} * objects. The factory will consider this as normal value to be used; it * will not throw a FactoryBeanNotInitializedException in this case anymore. * FactoryBean implementations are encouraged to throw * FactoryBeanNotInitializedException themselves now, as appropriate. * @return an instance of the bean (can be {@code null}) * @throws Exception in case of creation errors * @see FactoryBeanNotInitializedException */ @Nullable T getObject() throws Exception; /** * Return the type of object that this FactoryBean creates, * or {@code null} if not known in advance. *

    This allows one to check for specific types of beans without * instantiating objects, for example on autowiring. *

    In the case of implementations that are creating a singleton object, * this method should try to avoid singleton creation as far as possible; * it should rather estimate the type in advance. * For prototypes, returning a meaningful type here is advisable too. *

    This method can be called before this FactoryBean has * been fully initialized. It must not rely on state created during * initialization; of course, it can still use such state if available. *

    NOTE: Autowiring will simply ignore FactoryBeans that return * {@code null} here. Therefore it is highly recommended to implement * this method properly, using the current state of the FactoryBean. * @return the type of object that this FactoryBean creates, * or {@code null} if not known at the time of the call * @see ListableBeanFactory#getBeansOfType */ @Nullable Class<?> getObjectType(); /** * Is the object managed by this factory a singleton? That is, * will {@link #getObject()} always return the same object * (a reference that can be cached)? *

    NOTE: If a FactoryBean indicates to hold a singleton object, * the object returned from {@code getObject()} might get cached * by the owning BeanFactory. Hence, do not return {@code true} * unless the FactoryBean always exposes the same reference. *

    The singleton status of the FactoryBean itself will generally * be provided by the owning BeanFactory; usually, it has to be * defined as singleton there. *

    NOTE: This method returning {@code false} does not * necessarily indicate that returned objects are independent instances. * An implementation of the extended {@link SmartFactoryBean} interface * may explicitly indicate independent instances through its * {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean} * implementations which do not implement this extended interface are * simply assumed to always return independent instances if the * {@code isSingleton()} implementation returns {@code false}. *

    The default implementation returns {@code true}, since a * {@code FactoryBean} typically manages a singleton instance. * @return whether the exposed object is a singleton * @see #getObject() * @see SmartFactoryBean#isPrototype() */ default boolean isSingleton() { return true; } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146

    ②创建类UserFactoryBean

    package com.atguigu.spring6.bean;
    
    //实现FactoryBean
    public class UserFactoryBean implements FactoryBean<User> {
        @Override
        public User getObject() throws Exception {
            return new User();
        }
    
        @Override
        public Class<?> getObjectType() {
            return User.class;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ③配置bean

    <bean id="user" class="com.atguigu.spring6.bean.UserFactoryBean">bean>
    
    • 1

    ④测试

    @Test
    public void testUserFactoryBean(){
        //获取IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-factorybean.xml");
        User user = (User) ac.getBean("user");
        System.out.println(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.2.13 基于xml自动装配

    自动装配:根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值

    在这里插入图片描述

    ①场景模拟

    创建类UserController

    package com.atguigu.spring6.autowire.controller
    public class UserController {
    
        private UserService userService;
    
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    
        public void saveUser(){
            userService.saveUser();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    创建接口UserService

    package com.atguigu.spring6.autowire.service
    public interface UserService {
    
        void saveUser();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    创建类UserServiceImpl实现接口UserService

    package com.atguigu.spring6.autowire.service.impl
    public class UserServiceImpl implements UserService {
    
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void saveUser() {
            userDao.saveUser();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    创建接口UserDao

    package com.atguigu.spring6.autowire.dao
    public interface UserDao {
    
        void saveUser();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    创建类UserDaoImpl实现接口UserDao

    package com.atguigu.spring6.autowire.dao.impl
    public class UserDaoImpl implements UserDao {
    
        @Override
        public void saveUser() {
            System.out.println("保存成功");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ②配置bean

    (1)byType

    使用bean标签的autowire属性设置自动装配效果

    byType:根据类型匹配IOC容器中的某个兼容类型的bean,为属性自动赋值

    若在IOC中,没有任何一个兼容类型的bean能够为属性赋值,则该属性不装配,即值为默认值null

    若在IOC中,有多个兼容类型的bean能够为属性赋值,则抛出异常NoUniqueBeanDefinitionException

    <bean id="userController" class="com.atguigu.spring6.autowire.controller.UserController" autowire="byType">bean>
    
    <bean id="userService" class="com.atguigu.spring6.autowire.service.impl.UserServiceImpl" autowire="byType">bean>
    
    <bean id="userDao" class="com.atguigu.spring6.autowire.dao.impl.UserDaoImpl">bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    (2)byName

    byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的bean进行赋值

    <bean id="userController" class="com.atguigu.spring6.autowire.controller.UserController" autowire="byName">bean>
    
    <bean id="userService" class="com.atguigu.spring6.autowire.service.impl.UserServiceImpl" autowire="byName">bean>
    <bean id="userServiceImpl" class="com.atguigu.spring6.autowire.service.impl.UserServiceImpl" autowire="byName">bean>
    
    <bean id="userDao" class="com.atguigu.spring6.autowire.dao.impl.UserDaoImpl">bean>
    <bean id="userDaoImpl" class="com.atguigu.spring6.autowire.dao.impl.UserDaoImpl">bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ③测试

    @Test
    public void testAutoWireByXML(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("autowire-xml.xml");
        UserController userController = ac.getBean(UserController.class);
        userController.saveUser();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.3 基于注解管理Bean

    从 Java 5 开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。

    Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。

    Spring 通过注解实现自动装配的步骤如下:

    1. 引入依赖
    2. 开启组件扫描
    3. 使用注解定义 Bean
    4. 依赖注入

    3.3.1 搭建子模块spring6-ioc-annotation

    ②引入配置文件

    引入spring-ioc-xml模块日志log4j2.xml

    ③添加依赖

    pom.xml

    <dependencies>
        
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>6.0.3version>
        dependency>
    
        
        <dependency>
            <groupId>org.junit.jupitergroupId>
            <artifactId>junit-jupiter-apiartifactId>
        dependency>
    
        
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-coreartifactId>
            <version>2.19.0version>
        dependency>
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-slf4j2-implartifactId>
            <version>2.19.0version>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    3.3.2 开启组件扫描

    Spring 默认不使用注解装配 Bean,因此我们需要在 Spring 的 XML 配置中,通过 context:component-scan元素开启 Spring Beans的自动扫描功能。开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。

    bean.xml

    
    <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-3.0.xsd
        http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd">
        
        <context:component-scan base-package="com.atguigu.spring6">context:component-scan>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注意:在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。

    (1)情况一:最基本的扫描方式
    <context:component-scan base-package="com.atguigu.spring6">
    context:component-scan>
    
    • 1
    • 2
    (2)情况二:指定要排除的组件
    <context:component-scan base-package="com.atguigu.spring6">
        
        
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            
    context:component-scan>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    (3)情况三:仅扫描指定组件
    <context:component-scan base-package="com.atguigu" use-default-filters="false">
        
        
        
        
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    	
    context:component-scan>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.3.3 使用注解定义 Bean

    Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean。

    注解说明
    @Component该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。
    @Repository该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
    @Service该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
    @Controller该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

    3.3.4 @Autowired注入

    单独使用@Autowired注解,默认根据类型装配。【默认是byType】

    第一处:该注解可以标注在哪里?

      • 构造方法上
      • 方法上
      • 形参上
      • 属性上
      • 注解上

    第二处:该注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须是存在的,如果不存在则报错。如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错。

    方式一:属性注入

    代码结构如下:

    在这里插入图片描述

    创建UserDao接口

    package com.atguigu.spring6.dao;
    
    public interface UserDao {
    
        public void print();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    创建UserDaoImpl实现

    package com.atguigu.spring6.dao.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import org.springframework.stereotype.Repository;
    
    //Dao层
    @Repository
    public class UserDaoImpl implements UserDao {
    
        @Override
        public void print() {
            System.out.println("Dao层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    创建UserService接口

    package com.atguigu.spring6.service;
    
    public interface UserService {
    
        public void out();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    创建UserServiceImpl实现类

    package com.atguigu.spring6.service.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import com.atguigu.spring6.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    //业务层
    @Service
    public class UserServiceImpl implements UserService {
    
        //属性注入
        @Autowired
        private UserDao userDao;
    
        @Override
        public void out() {
            userDao.print();
            System.out.println("Service层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    创建UserController类

    package com.atguigu.spring6.controller;
    
    import com.atguigu.spring6.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    //控制层
    @Controller
    public class UserController {
    
        //属性注入
        @Autowired
        private UserService userService;
    
        public void out() {
            userService.out();
            System.out.println("Controller层执行结束。");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    测试一

    package com.atguigu.spring6.bean;
    
    import com.atguigu.spring6.controller.UserController;
    import org.junit.jupiter.api.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class UserTest {
    
        private Logger logger = LoggerFactory.getLogger(UserTest.class);
    
        @Test
        public void testAnnotation(){
            ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
            UserController userController = context.getBean("userController", UserController.class);
            userController.out();
            logger.info("执行成功");
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    以上构造方法和setter方法都没有提供,经过测试,仍然可以注入成功。

    方式二:set注入

    修改UserServiceImpl类

    package com.atguigu.spring6.service.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import com.atguigu.spring6.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        private UserDao userDao;
    
        //set注入
        @Autowired
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void out() {
            userDao.print();
            System.out.println("Service层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    修改UserController类

    package com.atguigu.spring6.controller;
    
    import com.atguigu.spring6.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserController {
    
        private UserService userService;
    
        //set注入
        @Autowired
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    
        public void out() {
            userService.out();
            System.out.println("Controller层执行结束。");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    方式三:构造方法注入

    修改UserServiceImpl类

    package com.atguigu.spring6.service.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import com.atguigu.spring6.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        private UserDao userDao;
    
        //构造方法注入
        @Autowired
        public UserServiceImpl(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void out() {
            userDao.print();
            System.out.println("Service层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    修改UserController类

    package com.atguigu.spring6.controller;
    
    import com.atguigu.spring6.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserController {
    
        private UserService userService;
    
        //构造方法注入
        @Autowired
        public UserController(UserService userService) {
            this.userService = userService;
        }
    
        public void out() {
            userService.out();
            System.out.println("Controller层执行结束。");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    方式四:形参上注入

    修改UserServiceImpl类

    package com.atguigu.spring6.service.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import com.atguigu.spring6.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        private UserDao userDao;
    
        //形参注入
        public UserServiceImpl(@Autowired UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void out() {
            userDao.print();
            System.out.println("Service层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    修改UserController类

    package com.atguigu.spring6.controller;
    
    import com.atguigu.spring6.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserController {
    
        private UserService userService;
    
        //形参注入
        public UserController(@Autowired UserService userService) {
            this.userService = userService;
        }
    
        public void out() {
            userService.out();
            System.out.println("Controller层执行结束。");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    场景五:只有一个构造函数,无注解

    修改UserServiceImpl类

    package com.atguigu.spring6.service.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import com.atguigu.spring6.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UserDao userDao;
    
        public UserServiceImpl(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void out() {
            userDao.print();
            System.out.println("Service层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    测试通过

    当有参数的构造方法只有一个时,@Autowired注解可以省略。

    说明:有多个构造方法时呢?大家可以测试(再添加一个无参构造函数),测试报错

    场景六:@Autowired注解和@Qualifier注解联合

    添加dao层实现

    package com.atguigu.spring6.dao.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDaoRedisImpl implements UserDao {
    
        @Override
        public void print() {
            System.out.println("Redis Dao层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    测试:测试异常

    错误信息中说:不能装配,UserDao这个Bean的数量等于2

    怎么解决这个问题呢?当然要byName,根据名称进行装配了。

    修改UserServiceImpl类

    package com.atguigu.spring6.service.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import com.atguigu.spring6.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        @Qualifier("userDaoImpl") // 指定bean的名字
        private UserDao userDao;
    
        @Override
        public void out() {
            userDao.print();
            System.out.println("Service层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    总结

    • @Autowired注解可以出现在:属性上、构造方法上、构造方法的参数上、setter方法上。
    • 当带参数的构造方法只有一个,@Autowired注解可以省略。()
    • @Autowired注解默认根据类型注入。如果要根据名称注入的话,需要配合@Qualifier注解一起使用。

    3.3.5 @Resource注入

    @Resource注解也可以完成属性注入。那它和@Autowired注解有什么区别?

    • @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
    • @Autowired注解是Spring框架自己的。
    • @Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
    • @Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。
    • @Resource注解用在属性上、setter方法上。
    • @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。

    @Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。

    <dependency>
        <groupId>jakarta.annotationgroupId>
        <artifactId>jakarta.annotation-apiartifactId>
        <version>2.1.1version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    场景一:根据name注入

    修改UserDaoImpl类

    package com.atguigu.spring6.dao.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import org.springframework.stereotype.Repository;
    
    @Repository("myUserDao")
    public class UserDaoImpl implements UserDao {
    
        @Override
        public void print() {
            System.out.println("Dao层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    修改UserServiceImpl类

    package com.atguigu.spring6.service.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import com.atguigu.spring6.service.UserService;
    import jakarta.annotation.Resource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Resource(name = "myUserDao")
        private UserDao myUserDao;
    
        @Override
        public void out() {
            myUserDao.print();
            System.out.println("Service层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    测试通过

    场景二:name未知注入

    修改UserDaoImpl类

    package com.atguigu.spring6.dao.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import org.springframework.stereotype.Repository;
    
    @Repository("myUserDao")
    public class UserDaoImpl implements UserDao {
    
        @Override
        public void print() {
            System.out.println("Dao层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    修改UserServiceImpl类

    package com.atguigu.spring6.service.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import com.atguigu.spring6.service.UserService;
    import jakarta.annotation.Resource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        //注意,这里没加name
        @Resource
        private UserDao myUserDao;
    
        @Override
        public void out() {
            myUserDao.print();
            System.out.println("Service层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    测试通过

    当@Resource注解使用时没有指定name的时候,还是根据name进行查找,这个name是属性名。

    场景三 其他情况

    修改UserServiceImpl类,userDao1属性名不存在

    package com.atguigu.spring6.service.impl;
    
    import com.atguigu.spring6.dao.UserDao;
    import com.atguigu.spring6.service.UserService;
    import jakarta.annotation.Resource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Resource
        private UserDao userDao1;
    
        @Override
        public void out() {
            userDao1.print();
            System.out.println("Service层执行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    测试异常

    根据异常信息得知:显然当通过name找不到的时候,自然会启动byType进行注入,以上的错误是因为UserDao接口下有两个实现类导致的。所以根据类型注入就会报错。

    @Resource的set注入可以自行测试

    总结:

    @Resource注解:默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。byType注入时,某种类型的Bean只能有一个

    3.3.6 Spring全注解开发

    全注解开发就是不再使用spring配置文件了,写一个配置类来代替配置文件

    在这里插入图片描述

    package com.atguigu.spring6.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    //@ComponentScan({"com.atguigu.spring6.controller", "com.atguigu.spring6.service","com.atguigu.spring6.dao"})
    @ComponentScan("com.atguigu.spring6")
    public class Spring6Config {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    测试类

    @Test
    public void testAllAnnotation(){
        //注意:使用AnnotationConfigApplicationContext
        ApplicationContext context = new AnnotationConfigApplicationContext(Spring6Config.class);
        UserController userController = context.getBean("userController", UserController.class);
        userController.out();
        logger.info("执行成功");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    基于C++实现⾃然连接操作算法
    手撸mybatis07: SQL执行器的定义和实现
    antd table表格支持多选框选择当前列,进行格式设置等
    免费的 Docker 镜像仓库,无需注册登录
    存储技术知识分享
    项目成功经验分享:做好项目承诺管理
    tensorflow数据类型转换
    【知识总结】金九银十offer拿到手软的前端面试题——HTML篇
    灵性图书馆:好书推荐-《当下的力量》
    企业注销的债权债务如何处理
  • 原文地址:https://blog.csdn.net/qq_39236499/article/details/134428753