Spring框架(第1天)-IoC容器和依赖注入

文章目录

Spring框架(第1天)-IoC容器和依赖注入

SSM框架阶段

  1. Spring(3天)
  2. SpringMVC(3天)
  3. maven高级(1天)
  4. Git(1天)

学习目标

  1. 能够描述Spring框架
  2. 能够理解Spring的IoC的容器
  3. 能够编写Spring的IoC的入门案例
  4. 能够说出Spring的bean标签的配置
  5. 能够理解Bean的实例化方法
  6. 能够理解Bean的属性注入方法
  7. 了解复杂(集合)类型的属性注入
  8. 使用注解代替相关XML配置
  9. 理解Spring相关注解的含义

1. IoC的概念

目标

IoC的概念

本质上就是工厂模式

原来

以前我们是由自己主动创建一个对象,主动获取一个资源
在这里插入图片描述

//Car是接口, 后面是实现类
Car bmw = new Bmw();
Car benZ = new BenZ();
Car audi = new Audi();

现在

使用工厂模式,我们需要的对象由工厂去创建,我们不再自己创建,我们只消费对象。
在这里插入图片描述

Car bmw = BeanFactory.getBean("bmw");
Car benZ = BeanFactory.getBean("benZ");
Car audi = BeanFactory.getBean("audi");

有什么好处?

  1. 降低类与类之间的耦合度
  2. 我们不再需要自己创建对象,只要使用对象就可以了
  3. 使用Spring框架就是使用工厂模式来创建对象,由Spring容器帮我们管理所有的Java对象

容器的概念

在项目开发中将对象存放在什么地方?

由于项目中对象是大量的, 所以考虑用集合 Map/List 存储。而在使用时需要根据名称查找, 所以通常使用 Map 存储

什么是工厂呢?

工厂就是负责创建对象,并且把对象放到容器中。在使用的时候,帮助我们从容器获取指定的对象。此时我们获取对象的方式就发生了改变。

概念:控制反转IoC(Inversion Of Control)

这种将创建对象的权利,由在程序代码中主动new对象的方式,转变为由工厂类创建并且提供给我们,我们使用的时候从容器中去取,变成被动接收的方式,称为控制反转。

控制反转也叫IoC(Inversion Of Control),即我们接下来要学习的Spring框架中的一个重要的特点。在这里我们首先明确一个事情:Spring的IoC解决的问题,就是工厂模式解耦解决的问题。

小结

IoC从哪里获取对象?与传统方式创建对象有什么区别?

从Spring容器中获取对象,本质上是Map集合,通过键获取值
以前是主动创建,现在是被动接收。这就是IoC

2. Spring框架介绍

目标

  1. 了解Spring框架的优点
  2. 了解Spring体系结构

Spring框架的由来

作者:Rod Johnson 是 Spring框架的创始人,著名作者。 Rod在悉尼大学不仅获得了计算机学位,更令人吃惊的是在回到软件开发领域之前,他还获得了音乐学的博士学位。 有着相当丰富的C/C++技术背景的Rod早在1996年就开始了对Java服务器端技术的研究。
在这里插入图片描述

Spring框架的介绍

  • Spring是分层的Java SE/EE应用的full-stack轻量级开源框架。
  • 它是以IOC(Inversion Of Control)控制反转和AOP(Aspect Oriented Programming)面向切面编程为核心
  • 提供了表现层springmvc和持久层Spring JDBC以及业务层的事务管理等企业级应用解决方案。
  • 将开源世界中众多优秀的第三方框架和类库整合,成为越来越受欢迎的Java EE企业级应用框架。

Spring体系结构

体系结构组成

Spring框架采用的是分层架构,它一系列功能要素被分成20个模块,以下列出了部分我们会用到的模块。

Core Container核心容器

  • Beans模块:提供了BeanFactory,工厂模式的实现,Spring将所有管理的对象称为Bean。
  • Core模块:提供了Spring框架的基本组成部分,包括IoC和DI依赖注入的功能。
  • Context模块:访问和配置Bean对象的上下文对象,核心容器。如:ApplicationContext

Data Access/Integration数据访问与集成

  • JDBC模块:提供了JDBC的支持,如:JdbcTemplate
  • ORM模块:对主流的ORM框架提供了支持,如:JPA,JDO,Hibernate等
  • Transaction模块:支持声明式事务的管理

Web

  • Servlet模块:包含了SpringMVC和REST Web Services实现的Web应用程序
  • Web模块:提供了基本的Web开发集成特性,如:文件上传,收发邮件等。

其他模块

  • AOP模块:提供了面向切面编程的功能
  • Aspects模块:提供了与AspectJ框架集成功能
  • Test模块:提供了对单元测试和集成测试的支持

小结

Spring的优点

  • IOC解耦,简化开发
  • AOP面向切面编程支持
  • 声明式事务支持
  • 方便程序测试
  • 集成第三方优秀框架

3. Spring入门案例

创建maven项目

检查工程结构
在这里插入图片描述
选择JDK1.8的版本
在这里插入图片描述

搭建Spring入门开发环境

添加Spring框架

项目结构

在这里插入图片描述

pom文件内容

部分内容:只是导入了依赖包

    <dependencies>
        <!--导入Spring基础框架-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>

        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

创建applicationContext.xml文件

  1. 该配置文件是Spring框架的主配置文件,文件位置为类的根路径下,文件名称可以修改。

  2. 在idea中使用菜单创建,会自动编写Schema的约束信息
    在这里插入图片描述

  3. 配置applicationContext.xml文件,让Spring框架管理对象

编写持久层

编写CustomerService接口,添加保存客户的方法 void saveCustomer();

CustomerService.java

package com.itheima.service;

/**
 * 业务层接口
 */
public interface CustomerService {
    
    

    /**
     * 保存客户对象
     */
    void saveCustomer();
}

编写客户业务层实现类,实现接口中的方法,直接输出一句话"保存客户"

CustomerServiceImpl.java

package com.itheima.service.impl;

import com.itheima.service.CustomerService;

/**
 * 业务层实现类
 */
public class CustomerServiceImpl implements CustomerService {
    
    
    /**
     * 保存客户对象
     */
    @Override
    public void saveCustomer() {
    
    
        System.out.println("保存了客户");
    }
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
    创建业务对象放到Spring容器中
    class:指定类全名,指定的是实现类,不是接口
    id:容器中唯一标识,通过id获取这个对象
     -->
    <bean class="com.itheima.service.impl.CustomerServiceImpl" id="customerService"/>
</beans>

测试类

package com.itheima.test;

import com.itheima.service.CustomerService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestCustomer {
    
    

    @Test
    public void testCustomer() {
    
    
        //1.创建Spring容器,参数:指定spring的配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.从容器中获取对象
        CustomerService customerService = (CustomerService) context.getBean("customerService");
        //3.调用对象的方法
        customerService.saveCustomer();
        //4.关闭容器
        context.close();
    }
}

小结

bean标签的作用是?它有哪两个属性?

在容器中创建一个对象
<bean>标签 说明
id 容器中唯一标识
class 类全名

4. IoC容器:创建容器三种方式

目标

  1. ClassPathXmlApplicationContext容器实现类【掌握】
  2. FileSystemXmlApplicationContext容器实现类【了解】
  3. AnnotationConfigApplicationContext容器实现类【掌握】

BeanFactory容器的类结构

在这里插入图片描述

案例演示

方式一:类路径配置文件创建容器

方式二:本地配置文件方式创建容器

方式三:注解的方式创建容器

代码

package com.itheima.test;

import com.itheima.service.CustomerService;
import com.itheima.service.impl.CustomerServiceImpl;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class TestCustomer {
    
    

    /**
     * 1.在类路径下读取配置文件,创建容器
     */
    @Test
    public void testCustomer() {
    
    
        //1.创建Spring容器,参数:指定spring的配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.从容器中获取对象
        CustomerService customerService = (CustomerService) context.getBean("customerService");
        //3.调用对象的方法
        customerService.saveCustomer();
        //4.关闭容器
        context.close();
    }


    /**
     * 2. 从文件的绝对路径中获取配置文件,来创建容器
     */
    @Test
    public void testFileSystem() {
    
    
        //1.创建Spring容器,参数:指定spring的配置文件
        FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("D:\\WorkSpace\\idea\\JavaEE148\\day45_01_HelloSpring\\src\\main\\resources\\applicationContext.xml");
        //2.从容器中获取对象
        CustomerService customerService = (CustomerService) context.getBean("customerService");
        //3.调用对象的方法
        customerService.saveCustomer();
        //4.关闭容器
        context.close();
    }

    /**
     * 3.读取注解的配置文件创建容器
     */
    @Test
    public void testAnnotationConfig() {
    
    
        //1.创建Spring容器,参数:类配置文件,通过注解去配置Spring
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CustomerServiceImpl.class);
        System.out.println(context);
    }
}

小结

  1. ClassPathXmlApplicationContext的作用?

    从类路径下读取配置文件创建容器
    
  2. FileSystemXmlApplicationContext的作用?

    从文件系统的绝对路径中通过配置文件,创建容器
    
  3. AnnotationConfigApplicationContext的作用?

    读取类配置文件,使用注解的方式创建容器
    

5. bean标签的配置细节【重点】

目标

bean标签作用

作用:在Spring容器中创建一个对象

bean标签

bean标签的属性说明

属性 说明
id 容器中唯一的标识
name 还可以有多个名字,使用逗号,空格,分号隔开都可以
class 指定类全名,指定的是实现类,不是接口
scope 指定bean在容器中作用范围
singleton:默认值,表示这个是单例对象,整个容器中只会创建一个对象
prototype:这是多例对象,每次获取一个新的对象
request:用于请求域
session:用于会话域
application: 用于上下文域
globalsession:用于全局的会话,用在分布式开发中
init-method 创建对象时,执行的初始化的方法
destroy-method 销毁对象时,执行的方法
lazy-init 是否使用延迟加载,默认是不使用

关于GlobalSession的理解

在这里插入图片描述

6. bean的生命周期案例【重点】

目标

  1. scope属性中单例和多例的配置
  2. 生命周期的配置

scope属性【掌握】

bean的作用范围和生命周期的说明

scope取值 作用范围 生命周期
singleton 单例对象 容器一创建就创建这个对象,只要容器不销毁就一直存在 出生:容器创建就出生
活着:只要容器没有销毁就一直存在
死亡:容器关闭的时候
prototype 多例对象 每次获取对象就创建一个新的对象,使用完毕会被GC回收 出生:获取对象的时候
活着:使用过程中
死亡:由GC去回收

scope取值

  1. 在applicationContext.xml中配置customerService,指定scope属性为singleton
  2. 在测试方法中得到2次customerService对象,判断是否是同一个对象
  3. 输出两个对象的地址和hashCode()

结果

singleton:
com.itheima.service.impl.CustomerServiceImpl@7a187f14
com.itheima.service.impl.CustomerServiceImpl@7a187f14
true

prototype:
com.itheima.service.impl.CustomerServiceImpl@7d70d1b1
com.itheima.service.impl.CustomerServiceImpl@2a742aa2
false

scope取值prototype

  1. 在applicationContext.xml中配置customerService,指定scope属性为prototype
  2. 在测试方法中得到2次customerService对象,判断是否是同一个对象
  3. 输出两个对象的地址和hashCode()

代码

applicationContext.xml

<!--
scope取值:
    singleton:这是默认值,容器一创建就创建这个对象,只要容器不销毁就一直存在
    prototype:每次获取对象就创建一个新的对象,使用完毕会被GC回收
-->
<bean class="com.itheima.service.impl.CustomerServiceImpl" id="customerService" scope="prototype"/>

测试类

@Test
public void testScope() {
    
    
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //获取2次
    CustomerService c1 = (CustomerService) context.getBean("customerService");
    CustomerService c2 = (CustomerService) context.getBean("customerService");
    System.out.println(c1);
    System.out.println(c2);
    System.out.println(c1 == c2);
}

init-method和destroy-method属性【了解】

步骤

  1. 改造客户dao实现类,增加初始化和销毁方法,名字随意。
  2. 配置applicationContext.xml文件,增加init-method和destroy-method属性。
    1. 每个方法中各输出一句话
    2. 注:销毁的方法只在单例模式下起作用。
  3. 编写测试类
    1. 创建容器对象
    2. 得到customerService对象
    3. 调用DAO中的方法
    4. 调用容器关闭的方法(需要转成子类)

CustomerServiceImpl

package com.itheima.service.impl;

import com.itheima.service.CustomerService;

/**
 * 业务层实现类
 */
public class CustomerServiceImpl implements CustomerService {
    
    
    /**
     * 保存客户对象
     */
    @Override
    public void saveCustomer() {
    
    
        System.out.println("保存了客户");
    }

    public void init() {
    
    
        System.out.println("初始化的方法");
    }

    public void destroy() {
    
    
        System.out.println("销毁的方法");
    }
}

applicationContext.xml

    <!--
    scope取值:
        singleton:这是默认值,容器一创建就创建这个对象,只要容器不销毁就一直存在
        prototype:每次获取对象就创建一个新的对象,使用完毕会被GC回收
        init-method:指定创建对象以后,在构造方法之后执行,调用的方法
        destroy-method: 对象销毁前执行的方法
    -->
    <bean class="com.itheima.service.impl.CustomerServiceImpl" id="customerService" init-method="init" destroy-method="destroy"/>

单例的延迟加载

步骤

  1. 在业务对象中添加构造方法,输出一句话
  2. 修改applicationContext.xml文件,设置成单例模式
  3. 测试方法
    1. 未设置延迟加载,创建容器,不获取对象,也会实例化
    2. 使用lazy-init设置为true,在得到对象的时候才实例化

代码

  1. 改造实现类,增加初始化和销毁方法,名字随意

    package com.itheima.service.impl;
    
    import com.itheima.service.CustomerService;
    
    /**
     * 业务层实现类
     */
    public class CustomerServiceImpl implements CustomerService {
          
          
    
        public CustomerServiceImpl() {
          
          
            System.out.println("无参构造方法");
        }
    
        /**
         * 保存客户对象
         */
        @Override
        public void saveCustomer() {
          
          
            System.out.println("保存了客户");
        }
    }
    
  2. 添加lazy-init属性

    <!-- lazy-init 默认是及时加载,不使用延迟加载。延迟加载只用于单例对象 -->
    <bean class="com.itheima.service.impl.CustomerServiceImpl" id="customerService" lazy-init="true"/>
    
  3. 编写测试类

    @Test
    public void testLazy() {
          
          
        //创建了容器就会创建所有的对象
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取对象的时候才去创建
        CustomerService customerService = (CustomerService) context.getBean("customerService");
    }
    

小结

在这里插入图片描述

7. 创建对象的三种方式

创建对象方式1:无参构造方法

目标

  1. 掌握使用无参构造方法创建对象
  2. 使用静态工厂方法
  3. 使用实例工厂方法

步骤

  1. 复制maven项目
  2. CustomerServiceImpl有保存客户的方法
  3. 在applicationContext.xml中customerService配置id和class的配置
  4. 在测试类中得到customerService,打印对象
  5. 添加1个有参的构造方法,不写无参的构造方法,再次测试

注:默认使用无参数构造方法创建对象。如果此时没有无参构造方法,创建对象会失败

代码

  1. CustomerServiceImpl
package com.itheima.service.impl;

import com.itheima.service.CustomerService;

/**
 * 业务层实现类
 */
public class CustomerServiceImpl implements CustomerService {
    
    

    public CustomerServiceImpl() {
    
    
    }

    public CustomerServiceImpl(String name) {
    
    
        System.out.println("有参的构造方法");
    }

    /**
     * 保存客户对象
     */
    @Override
    public void saveCustomer() {
    
    
        System.out.println("保存了客户");
    }

}
  1. applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 默认使用无参的构造方法,如果没有无参构造方法,会出错:No default constructor found  -->
    <bean class="com.itheima.service.impl.CustomerServiceImpl" id="customerService"/>
</beans>

创建对象方式2:静态工厂方法【了解】

概述

​ 该方式要求开发者创建一个静态工厂的方法来创建Bean的实例

  1. bean配置中的class属性所指定的不再是bean实例的类,而是静态工厂类
  2. 需要使用factory-method属性来指定创建对象的静态工厂方法。

创建静态工厂类

  1. 创建一个静态工厂类
  2. 创建一个静态方法,返回一个customerService对象
package com.itheima.utils;

import com.itheima.service.CustomerService;
import com.itheima.service.impl.CustomerServiceImpl;

/**
 * 使用自己的静态工厂创建对象,而不使用Spring容器创建
 */
public class StaticBeanFactory {
    
    

    /**
     * 创建对象的静态方法
     */
    public static CustomerService getBean() {
    
    
        return new CustomerServiceImpl();
    }
}

配置文件

  1. id为customerService,class属性指定为静态工厂类
  2. 指定factory-method属性为静态方法
<!--
2.使用静态工厂创建
class: 静态工厂类
factory-method:静态的获取对象的方法
id:对象的id  -->
<bean class="com.itheima.utils.StaticBeanFactory" factory-method="getBean" id="customerService"/>

创建对象方式3:实例工厂方法【了解】

步骤

  1. 需要在容器中使用bean配置实例工厂对象,并且指定id。
  2. 在配置文件中,需要实例化的Bean通过factory-bean属性指向配置的实例工厂的id
  3. 使用factory-method属性指定使用工厂中的哪个方法

实例工厂创建对象

  1. 创建一个工厂类
  2. 通过成员方法创建customerService对象
package com.itheima.utils;

import com.itheima.service.CustomerService;
import com.itheima.service.impl.CustomerServiceImpl;

/**
 * 实例工厂
 */
public class InstanceBeanFactory {
    
    

    /**
     * 不是静态方法
     */
    public CustomerService getBean() {
    
    
        return new CustomerServiceImpl();
    }
}

配置文件

  1. 在容器中配置实例工厂对象,指定class和id属性
  2. 通过factory-bean指定实例工厂对象的id
  3. 通过factory-method指定实例工厂的方法
<!--
3. 使用实例工厂来创建
    3.1 先创建实例工厂对象
    3.2 调用实例工厂的方法创建我们所需的对象
 -->
<bean class="com.itheima.utils.InstanceBeanFactory" id="instanceBeanFactory"/>
<bean id="customerService" factory-bean="instanceBeanFactory" factory-method="getBean"/>

小结

  1. 如果没有无参的构造方法,创建对象会如何?

  2. 使用静态工厂创建对象

    1. class属性:指定为静态工厂对象
    2. factory-method属性:指定工厂中静态方法
  3. 使用实例工厂创建对象

    1. factory-bean:指定为实例工厂对象
    2. factory-method:指定工厂中创建对象的方法

8. 依赖注入:构造函数

目标

使用构造函数注入Bean的属性

依赖注入介绍

​ 依赖注入(Dependency Injection,简称DI)。当某个Java对象(调用者)需要调用另一个Java对象(被调用者,即被依赖对象)时,以前调用者通常采用"new 被调用者"的代码方式来创建对象,这种方式会导致调用者与被调用者之间的耦合性增加,不利于后期项目的升级和维护。
在这里插入图片描述
使用Spring框架之后,对象的实例不再由调用者来创建,而是由Spring容器来创建,由容器控制程序之间的关系,而不是由调用者的程序代码直接控制。由容器负责将被依赖对象赋值给调用者的成员变量,相当于为调用者注入了它的依赖的实例,这就是Spring的依赖注入。
在这里插入图片描述
简单理解:依赖注入就是由Spring创建对象,并且给成员变量赋值。

UserService由容器创建,并且注入给依赖它的对象

构造方法注入

开发步骤

  1. 创建项目
  2. 编写类Customer
    1. 包含属性( int id; String name;boolean male;Date birthday;)
    2. 编写无参的构造方法
    3. 添加全参的构造方法
  3. 生成toString()方法,不用创建set和get方法
  4. 配置applicationContext.xml
    1. 使用bean的constructor-arg子元素
    2. 编号属性使用index指定值
    3. 名字属性使用name和type指定
    4. 性别使用name属性指定
    5. 日期使用ref引用另一个日期对象,日期使用bean在容器中声明为java.util.Date对象
  5. 在测试类中得到客户对象,输出客户对象

代码

  1. Customer

    package com.itheima.entity;
    
    import java.util.Date;
    
    /**
     * 客户对象
     */
    public class Customer {
          
          
    
        private int id;
        private String name;
        private boolean male;
        private Date birthday;
    
        public Customer() {
          
          
        }
    
        public Customer(int id, String name, boolean male, Date birthday) {
          
          
            this.id = id;
            this.name = name;
            this.male = male;
            this.birthday = birthday;
        }
    
        @Override
        public String toString() {
          
          
            return "Customer{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", male=" + male +
                    ", birthday=" + birthday +
                    '}';
        }
    }
    
    
  2. applicationContext.xml

    <!--
    1. 使用构造方法注入
    子元素:constructor-arg 构造方法注入
        index:每几个位置,从0开始
        name:指定形参的名字
        type:指定参数的类型
        value:简单类型的值:8种基本类型+String类型
        ref:赋值引用类型,指定对象的id
     -->
    <bean class="com.itheima.entity.Customer" id="customer">
        <constructor-arg name="id" value="100"/>
        <constructor-arg name="name" value="白骨精"/>
        <constructor-arg name="male" value="true"/>
        <constructor-arg name="birthday" ref="birthday" type="java.util.Date"/>
    </bean>
    
    <!-- 引用类型:现在的时间 -->
    <bean class="java.util.Date" id="birthday"/>
    

小结

constructor-arg标签的属性 描述
index 构造方法参数的位置
name 参数名
type 指定参数的类型
value 赋值简单类型=基本类型+String类型
ref 赋值引用类型

9. 依赖注入:set方法【重点】

目标

就是通过类中的set方法,给成员变量赋值

步骤

  1. 给Customer添加set注入
  2. 配置applicationContext.xml,通过property元素给所有的属性赋值
  3. 运行测试类,输出customer对象

代码

  1. Customer

    package com.itheima.entity;
    
    import java.util.Date;
    
    /**
     * 客户对象
     */
    public class Customer {
          
          
    
        private int id;
        private String name;
        private boolean male;
        private Date birthday;
    
        public Customer() {
          
          
        }
    
        public Customer(int id, String name, boolean male, Date birthday) {
          
          
            this.id = id;
            this.name = name;
            this.male = male;
            this.birthday = birthday;
        }
    
        public void setId(int id) {
          
          
            this.id = id;
        }
    
        public void setName(String name) {
          
          
            this.name = name;
        }
    
        public void setMale(boolean male) {
          
          
            this.male = male;
        }
    
        public void setBirthday(Date birthday) {
          
          
            this.birthday = birthday;
        }
    
        @Override
        public String toString() {
          
          
            return "Customer{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", male=" + male +
                    ", birthday=" + birthday +
                    '}';
        }
    }
    
    
  2. applicationContext.xml

    <!-- 2.使用set方法注入 -->
    <bean class="com.itheima.entity.Customer" id="customer">
        <!-- 属性名:为id value为200 -->
        <property name="id" value="200"/>
        <property name="name" value="猪八戒"/>
        <property name="male" value="true"/>
        <property name="birthday" ref="birthday"/>
    </bean>
    
    <!-- 引用类型:现在的时间 -->
    <bean class="java.util.Date" id="birthday"/>
    

结果

默认的构造方法
Customer{id=200, name='猪八戒', male=true, birthday=Mon Dec 28 10:45:05 CST 2020}

小结

<property>的属性 描述
name 属性名
value 简单类型的值
ref 引用类型的值

10. 依赖注入:p命名空间

目标

使用p命名空间注入

概述

注:先要在xml中导入p命名空间,本质仍然是调用类中的set方法实现注入功能。

步骤

  1. 配置applicationContext.xml
    1. 导入p命名空间(xmlns:p=“http://www.springframework.org/schema/p”)
    2. 通过"p:属性名"或者"p:属性名-ref"注入属性
  2. 测试类,从容器中得到客户对象输出

代码

applicationContext.xml

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
  
    <!-- 使用p命名空间注入,本质上还是set注入
      1. 导入p命名空间
      2. 简单类型的格式:p:属性名="值"
         引用类型的格式:p:属性名-ref="id"
       -->
    <bean class="com.itheima.entity.Customer" id="customer" p:id="300" p:name="孙悟空" p:male="false" p:birthday-ref="birthday"/>

    <!-- 引用类型:现在的时间 -->
    <bean class="java.util.Date" id="birthday"/>
</beans>

小结

p名称空间 描述
p:属性名 注入简单类型
p:属性名-ref 注入引用类型

11. 集合属性的注入

目标

各种集合属性的注入

概述

顾名思义,就是给类中的集合成员变量赋值,只不过变量的数据类型都是集合。

我们这里介绍注入数组,List,Set,Map,Properties。

执行效果

在这里插入图片描述

编写集合属性注入类

  1. 创建实体类,Person
  2. 创建以下属性:
    1. 字符串数组:array
    2. 字符串类型的List集合:list
    3. 字符串类型的Set集合:set
    4. 字符串的键和值Map集合:map
    5. Properties属性集合
  3. 生成toString方法
package com.itheima.entity;

import java.util.*;

public class Person {
    
    

    //字符串数组
    private String[] array;
    //字符串类型的List集合
    private List<String> list;
    //字符串类型的Set集合
    private Set<String> set;
    //字符串的键和值Map集合
    private Map<String, String> map;
    //Properties属性集合
    private Properties prop;

    public String[] getArray() {
    
    
        return array;
    }

    public void setArray(String[] array) {
    
    
        this.array = array;
    }

    public List<String> getList() {
    
    
        return list;
    }

    public void setList(List<String> list) {
    
    
        this.list = list;
    }

    public Set<String> getSet() {
    
    
        return set;
    }

    public void setSet(Set<String> set) {
    
    
        this.set = set;
    }

    public Map<String, String> getMap() {
    
    
        return map;
    }

    public void setMap(Map<String, String> map) {
    
    
        this.map = map;
    }

    public Properties getProp() {
    
    
        return prop;
    }

    public void setProp(Properties prop) {
    
    
        this.prop = prop;
    }

    @Override
    public String toString() {
    
    
        return "Person{" +
                "array=" + Arrays.toString(array) +
                ", list=" + list +
                ", set=" + set +
                ", map=" + map +
                ", prop=" + prop +
                '}';
    }
}

配置applicationContext.xml

  1. 标签使用说明:

    1. 单列集合:使用标签 array/list/set
    2. 双列集合:使用标签 map/prop
    3. 只要数据结构相同,标签可以互用
  2. 使用set注入,给所有的属性使用相应的标签赋值

    1. 数组:array,每个元素是value或ref
    2. List集合:list,每个元素是value或ref
    3. set集合:set,每个元素是value或ref
    4. map集合:map,其中每个元素是entry,entry再指定key和value
    5. prop集合:props,其中每个元素是prop,包含key属性,没有value属性,标签体的内容是值
<!--  注入属性集合:所有的单列集合可以通用,只是语义上区别 -->
<bean class="com.itheima.entity.Person" id="person">
    <!--数组类型-->
    <property name="array">
        <array>
            <value>孙悟空</value>
            <value>猪八戒</value>
            <value>白骨精</value>
        </array>
    </property>

    <!-- list类型 -->
    <property name="list">
        <list>
            <value>张飞</value>
            <value>关羽</value>
            <value>刘备</value>
        </list>
    </property>

    <!-- set类型 -->
    <property name="set">
        <set>
            <value>贾宝玉</value>
            <value>林黛玉</value>
            <value>薛宝钗</value>
        </set>
    </property>

    <!-- map集合,所有的双引集合也是可以通用的 -->
    <property name="map">
        <map>
            <entry key="cn" value="中国"/>
            <entry key="usa" value="美国"/>
            <entry key="jp" value="日本"/>
        </map>
    </property>

    <!-- property集合 -->
    <property name="prop">
        <props>
            <prop key="gz">广州</prop>
            <prop key="sh">上海</prop>
            <prop key="bj">北京</prop>
        </props>
    </property>
</bean>

测试

得到person对象,输出对象

/**
 * 集合注入
 */
@Test
public void testPerson() {
    
    
    //1.创建Spring容器,参数:指定spring的配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //2.从容器中获取对象,指定对象类型
    Person person = context.getBean("person", Person.class);
    System.out.println(person);
    //3.关闭容器
    context.close();
}

小结

标签 描述
<array> 注入数组
<list> 注入List
<set> 注入set
<map> Map集合
<props> properties集合

12. @Component注解【重点】

目标

  1. 使用注解配置对象
  2. 从Spring容器中得到对象并输出

关于IoC配置说明

注解配置和 xml 配置要实现的目的是一样的,都是要降低程序间的耦合。只是配置的形式不一样。关于实际的开发中到底使用 xml 还是注解,每家公司有不同的习惯。所以这两种配置方式我们都需要掌握。

注意:SpringIoC容器中XML配置与注解可以混合使用。即:如果Dao用注解创建的对象;service用xml创建的对象一样可以注入DAO。

技术点

标签的作用

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

案例:使用注解的IoC配置

创建新项目

在这里插入图片描述

需求

  1. 使用注解配置Account对象
  2. 从Spring容器中得到Account对象并且输出

步骤

  1. 创建项目:添加依赖spring-context和junit
  2. 创建实体类Account,使用@Component注解。
  3. 在applicationContext.xml 配置中开启注解扫描的基包
  4. 在测试类中得到Account类并且输出

代码

pom.xml

  1. 导入spring-context包,使用5.2.0.RELEASE
  2. 导入junit 4.12
    <dependencies>
        <dependency>
            <!--spring的上下文-->
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

Account实体类

  1. 创建实体类Account,属性(Integer id, String name,Double money)
  2. 只有toString()方法,没有get和set,没有构造方法。
  3. 使用@Component注解
package com.itheima.entity;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

/**
 * 被扫描到以后,由Spring创建这个对象放到容器中去,默认是类名首字母小写做为名字
 * 也可以指定value属性,就是它的名字
    @Component:用于普通的类
    @Service:用于业务层的类
    @Repository:用于持久层
    @Controller:用于控制器
    注:以上四个注解的功能一样,只是语义上区别
 */
@Component
public class Account {
    
    

    private Integer id;
    private String name;
    private Double money;

    public Account() {
    
    
    }

    public Account(Integer id, String name, Double money) {
    
    
        this.id = id;
        this.name = name;
        this.money = money;
    }

    @Override
    public String toString() {
    
    
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

applicationContext.xml

  1. 使用context命名空间
  2. 配置扫描哪个基包
<?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">

    <!-- 扫描哪个包下所有的类,指定基包的名字,使用context命名空间 -->
    <context:component-scan base-package="com.itheima"/>
</beans>

测试类

  1. 从容器中得到对象
  2. 输出对象,看对象是否为空
package com.itheima.test;

import com.itheima.entity.Account;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAccount {
    
    

    @Test
    public void testAccount() {
    
    
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从容器中获取对象
        Account account = (Account) context.getBean("account");
        System.out.println(account);
    }
}

小结

  1. @Component注解的作用是什么?

    放在类上,一个标记,表示这个类会被创建,将对象放在容器中
    
  2. <context:component-scan base-package=“com.itheima.entity”/> 有什么用?

    指定扫描哪个基包,它的子包全部会被扫描,可以使用逗号分隔多个基包
    

13. @Autowired注解【重点】

目标

  1. 掌握@Autowired注解的作用
  2. 使用@Autowired修饰属性和方法

@Autowired介绍

  • 位置: 用于成员变量和成员方法上
  • 作用: 将属性注入相应的值
    • 按类型匹配的方式从容器中去查找对应的值注入
    • 如果有多个匹配的类型,按名字匹配的方式注入
    • 如果找不到匹配的名字,就会抛出异常

@Autowired修饰属性

案例演示

步骤

  1. 创建User1对象,包含字符串的属性username,有toString()方法
  2. 给User1添加@Component注解,指定注解的值为user,后面这个名字要重复使用。
  3. 给username添加@Autowired注解
  4. 在applicationContext.xml中添加一个id为man的字符串。在子标签中,使用构造器注入值。
  5. 运行测试,从容器中得到User1对象,并且输出对象。
  6. 在applicationContext.xml中再添加一个id为username的字符串,再运行测试,则注入新的值。

@Autowired修饰方法

在这里插入图片描述

演示案例

步骤:@Autowired修饰方法

  1. 将User1复制成User2对象,去掉User1中的@Component
    1. 包含字符串的属性username
    2. 有方法input(String username),接收注入的值
    3. 有toString()方法。
  2. 给input方法添加@Autowired注解
  3. 在applicationContext.xml中添加一个id为man的字符串,使用构造器注入值
  4. 运行测试,从容器中得到User2对象,并且输出对象。
  5. 在applicationContext.xml中再添加一个id为username的字符串,再运行测试,则注入新的值。

@Autowired的属性required

作用:默认为true,这个属性是否必须,如果为false,则在容器中找不到这个属性匹配的类型,也不抛出异常,属性值为空

步骤:required属性

  1. 去掉applicationContext.xml中所有字符串的配置
  2. 运行测试,找不到所需的字符串,则注入失败出现错误。因为@Autowired的required属性默认为true。
  3. 将User2中的@Autowired的requried属性设置为false,则注入失败没有错误。输出User2的username为null。

代码

User1.java

package com.itheima.entity;

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

@Component("user")
public class User1 {
    
    

    private String username;

    /**
     * 在默认的情况下:@Autowired的值必须要注入,否则抛出异常
     * 属性:required 默认是true,设置为false表示这个属性不是必须注入的
     */
    @Autowired(required = false)
    private void inputUsername(String username) {
    
    
        this.username = username;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }
}

applicationContext.xml

<?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">

    <!-- 扫描哪个包下所有的类,指定基包的名字,使用context命名空间 -->
    <context:component-scan base-package="com.itheima"/>

    <!-- 有一个字符串类型要注入 -->
    <bean class="java.lang.String" id="man">
        <!-- 相当于 new String("NewBoy") -->
        <constructor-arg value="NewBoy"/>
    </bean>

    <!--
    <bean class="java.lang.String" id="username">
        <constructor-arg value="Rose"/>
    </bean>
    -->
</beans>

小结

  1. @Autowired注入方法按什么去匹配的?

    按类型去匹配
    
  2. 如果出现参数类型相同,则按什么匹配?

    按名字去匹配
    
  3. 如果设置@Autowired属性为false会怎么样?

    这个属性可以不注入
    

14. @Qualifier注解

目标

学习@Qualifier的作用

作用

  • 必须与@Autowired配置使用,不能单独使用
  • 位置: 放在成员变量或成员方法上
  • 作用: 按名字匹配的方式注入
  • 属性: value指定要注入的名字名
    在这里插入图片描述

案例演示

User2.java

package com.itheima.entity;

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

@Component("user")
public class User2 {
    
    

    @Autowired
    @Qualifier("woman")  //指定bean的id进行入
    private String username;


    @Override
    public String toString() {
    
    
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }
}

applicationContext.xml

<?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">

    <!-- 扫描哪个包下所有的类,指定基包的名字,使用context命名空间 -->
    <context:component-scan base-package="com.itheima"/>

    <!-- 有一个字符串类型要注入 -->
    <bean class="java.lang.String" id="man">
        <!-- 相当于 new String("NewBoy") -->
        <constructor-arg value="NewBoy"/>
    </bean>

    <bean class="java.lang.String" id="woman">
        <constructor-arg value="Rose"/>
    </bean>
</beans>

小结

@Qualifier注解的作用是什么?

1. 必须与@Autowired配合使用
2. 按名字匹配的方式注入

15. @Value注解

目标

学习@Value注解的作用

作用

用于一些简单类型的注入,也可以注入日期类型

以后主要用于从Java属性文件中读取键,将值注入到成员变量

案例演示

步骤

  1. 复制User3,删除User2的@Component注解
  2. 给对象添加String name,boolean sex,java.util.Date birthday三个属性
  3. 使用@Value注入不同类型的值,其中生日使用"yyyy/MM/dd"的格式
  4. 重写toString()方法,输出结果。
  5. 执行测试方法,得到User3对象,并且输出对象。

代码

User3.java

package com.itheima.entity;

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

import java.util.Date;

@Component("user")
public class User3 {
    
    

    @Value("嫦娥")
    private String name;

    @Value("true")
    private boolean sex;

    @Value("2000/11/11")
    private Date birthday;

    @Override
    public String toString() {
    
    
        return "User{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", birthday=" + birthday +
                '}';
    }
}

16. 对象范围与生命周期的注解

目标

  1. @Scope注解
  2. @PostConstruct注解
  3. @PreDestroy注解
  4. @Lazy注解

注解说明

在这里插入图片描述

@Scope

作用:用在类上,用来指定当前对象在容器中是单例对象还是多例对象

步骤

  1. 复制User4,删除类中所有的内容。删除其它类的@Component注解
  2. 使用@Scope属性,分别设置为singleton和prototype属性,在测试类中得到两次User4对象,输出对象。看是否是同一个对象。

@Lazy

作用:用在类上,用来指定当前对象是否延迟加载,在默认的情况下,容器加载对象就加载了。如果为true表示延迟加载。

相当于:

<bean lazy-init="true/false"/>

步骤

  1. 给User4添加无参构造方法,输出一句话。
  2. 只加载容器,不从容器中得到对象。默认单例模式,只要容器加载就会实例化所有的对象。
  3. 给User4设置@Lazy注解。加载容器看是否实例化对象,然后再从容器中得到对象。

注:@Lazy只在单例模式下有效

@PostConstruct

作用:用在方法上,指定这是一个初始化的方法

相当于:

<bean init-method="方法名"/>

@PreDestroy

作用: 用在方法上,指定这是对象销毁前执行的方法,只用于单例对象。

相当于:

<bean destroy-method="方法名"/>

步骤

  1. 在User4中创建init()方法,使用@PostConstruct注解,看这个方法什么时候执行。
  2. 创建方法destroy(),使用@PreDestroy注解,看这个方法什么时候执行
  3. 注:测试的时候需要关闭容器

代码

User4.java

package com.itheima.entity;

import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component("user")
@Scope("singleton")
@Lazy  //延迟加载
public class User4 {
    
    

    public User4() {
    
    
        System.out.println("user4被创建了");
    }

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

    @PreDestroy
    public void destroy() {
    
    
        System.out.println("销毁的方法");
    }
}

测试类

/**
 * 与生命周期有关的注解
 */
@Test
public void testLifeCycle() {
    
    
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //从容器中获取对象
    User4 user1 = (User4) context.getBean("user");
    User4 user2 = (User4) context.getBean("user");
    System.out.println(user1);
    System.out.println(user2);
    System.out.println(user1 == user2);
}

@Test
public void testLazy() {
    
    
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //从容器中获取对象
    User4 user1 = (User4) context.getBean("user");
    System.out.println(user1);
    //必须关闭容器才会看到销毁的方法
    context.close();
}

小结

注解 作用
@Scope 单例或多例
@PostConstruct 初始化的方法
@PreDestory 销毁的方法
@Lazy 延迟加载

学习总结

  1. 能够描述Spring框架

    1. Spring是分层的Java SE/EE应用的full-stack轻量级开源框架。
    2. 它是以IOC(Inversion Of Control)控制反转和AOP(Aspect Oriented Programming)面向切面编程为核心
    3. 提供了表现层springmvc和持久层spring JDBC以及业务层的事务管理等企业级应用解决方案。
    4. 将开源世界中众多优秀的第三方框架和类库整合,成为越来越受欢迎的Java EE企业级应用框架。
  2. 能够理解Spring的IoC的容器

    以前的主动获取变成了现在的被动接收,对象从Spring容器中拿
    在这里插入图片描述

  3. 能够说出Spring的bean标签的配置

    属性 说明
    id 指定唯一标识
    name 可以多个名字,使用逗号,空格或分号分隔
    class 指定类全名
    scope singleton:单例对象
    prototype:多例对象
  4. 能够理解Bean的实例化方法

    1. 无参的构造方法
    2. 静态工厂对象
    3. 实例工厂对象
  5. 能够理解Bean的属性注入方法

    <property>的属性 描述
    name 属性名字
    value 属性值
    ref 注入引用类型
  6. 使用注解代替相关的XML配置
    在这里插入图片描述

  7. 理解Spring相关注解的含义

    将对象实例化以后放在容器中,要配置扫描基包:context:component-scan base-package="基包名"

创建对象的注解 说明
@Component 放在普通的类上
@Controller 放在控制器
@Service 放在业务对象
@Repository 放在持久层
依赖注入的注解 说明
@Autowired 1. 按类型匹配的方式
2.如果有多个按名字
3.如果找不到抛出异常
@Qualifier 按名字匹配
@Value 注入一些简单类型,主要用于读取配置文件中值注入
对象范围与生命周期 说明
@Scope 对象类型,单例或多例
@Lazy 延迟加载
@PostConstruct 初始化方法
@PreDestroy 销毁的方法

Memorial Day is 513 days
I miss you
xiaokeai

猜你喜欢

转载自blog.csdn.net/weixin_42914989/article/details/111937018