> 技术文档 > spring全面详解-最全最详细的spring基本认识和入门使用

spring全面详解-最全最详细的spring基本认识和入门使用


文章目录

  • Spring
    • spring概述
      • 1 Spring定义
      • 2 Spring核心
      • 3 Spring Framework的特点
    • 入门案例
      • 1 环境要求
      • 2 构建工程
        • 2.1 构建子工程first-spring
        • 2.2 入门案例
        • 2.3 对象存储
    • IoC容器
      • 1 控制反转IoC
      • 2 依赖注入DI
      • 3 IoC容器实现
      • 4 基于XML管理bean
        • 4.1 环境准备
        • 4.2 获取bean方式
        • 4.3 基于setter依赖注入
        • 4.4 基于构造器依赖注入
        • 4.5 特殊值处理注入
          • 4.5.1 字面量赋值
          • 4.5.2 null值
          • 4.5.3 xml实体
          • 4.5.4 CDATA区
        • 4.6 对象类型属性注入
          • 4.6.1 引用外部bean
          • 4.6.2 内部bean
          • 4.6.3 级联属性赋值(了解)
        • 4.7 数组类型属性注入
        • 4.8 集合类型属性注入
          • 4.8.1 List集合属性注入
          • 4.8.2 Map集合属性注入
          • 4.8.3 引用集合类型bean注入
        • 4.9 p命名空间
        • 4.10 引入外部属性文件
        • 4.11 bean的作用域
        • 4.12 bean的生命周期
          • 4.12.1 bean的完整生命周期
          • 4.12.2 生命周期验证
          • 4.12.3 bean生命周期扩展
        • 4.13 基于xml自动装配
      • 5 基于注解管理bean
        • 5.1 创建子工程
        • 5.2 开启组件扫描
        • 5.3 使用注解定义Bean
        • 5.4 @Autowired注入
          • 5.4.1 属性注入
          • 5.4.2 set注入
          • 5.4.3 构造方法注入
          • 5.4.4 形参上注入
          • 5.4.5 只有一个构造函数,无注解
          • 5.4.6 @Autowire注解和@Qualifier注解联合
        • 5.5 @Resource注入
        • 5.6 Spring全注解开发

Spring

spring全面详解-最全最详细的spring基本认识和入门使用

spring概述

1 Spring定义

​ Spring是一款主流的Java EE 轻量级开源框架,目的是用于简化Java企业级引用的开发难度和开发周期。从简单性、可测试性和松耦合度的角度而言,任何Java应用都可以从Spring中受益。Spring框架提供自己提供功能外,还提供整合其他技术和框架的能力。

​ Spring自诞生以来备受青睐,一直被广大开发人员作为Java企业级应用程序开发的首选。时至今日,Spring俨然成为了Java EE的代名词,成为了构建Java EE 应用的事实标准。

​ 自2004年4月,Spring1.0 版正式发布以来,Spring已经步入到了第6个大版本,即 Spring6,以下内容采用Spring5.3.24正式版本。

spring全面详解-最全最详细的spring基本认识和入门使用

2 Spring核心

​ Spring指的是Spring Framework,通常我们称之为Spring框架。Spring框架是一个分层的面向切面的Java应用程序的一站式解决框架,它是Spring技术栈的核心和基础,是为了解决企业级引用开发的复杂性而创建的。

​ Spring有两个核心模块:IoC和AOP。

​ Ioc:Inverse of Control的简写,为 控制反转,指把创建对象交给Spring进行管理。

​ AOP:Aspect Oriented Programming 的简写,为 面向切面编程。AOP用来封装多个类的公共行为,将那些与业务无关,却为业务模块共同调用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外,AOP还解决一些系统层面上的问题,比如日志、事务、权限等。

3 Spring Framework的特点

  • 控制反转:IoC,反转资源获取方向;把自己创建的资源、向环境索取资源变为环境将资源准备好,我们享受资源注入。

  • 面向切面编程:AOP,在不修改源代码的基础上增强代码功能。

  • 容器:Spring IoC是一个容器,因为它包含并管理组件对象的生命周期;组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大降低了使用门槛,大幅度提高了开发效率。

  • 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方库,而且在Spring旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在Spring Framework 的基础上全部使用Spring来实现。

入门案例

1 环境要求

  • JDK:Java8-15
  • Spring:5.3.24

2 构建工程

2.1 构建子工程first-spring

​ 在jsd2303-spring中创建子工程 spring-first

  1. New - Module

    spring全面详解-最全最详细的spring基本认识和入门使用

  2. 点击 Next

    spring全面详解-最全最详细的spring基本认识和入门使用

  3. Name为spring-first,点击 Finish

    spring全面详解-最全最详细的spring基本认识和入门使用

2.2 入门案例

① 在spring-first/pom.xml中引入相关依赖,并 刷新maven

<dependencies>  <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.24</version> </dependency></dependencies>

② 在工程中创建包 cn.tedu.spring.begin

spring全面详解-最全最详细的spring基本认识和入门使用

③ 创建类 User

spring全面详解-最全最详细的spring基本认识和入门使用

④ User类中定义方法

public class User { public void add(){ System.out.println(\"添加方法...\"); }}

⑤ 创建spring配置文件:resources目录下创建bean.xml

spring全面详解-最全最详细的spring基本认识和入门使用

spring全面详解-最全最详细的spring基本认识和入门使用

spring全面详解-最全最详细的spring基本认识和入门使用

⑥ 在bean.xml中用标签完成对象创建

<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\">  <bean id=\"user\" class=\"cn.tedu.spring.User\"></bean> </beans>

⑦ 创建测试类cn.tedu.spring.TestUser进行测试

public class TestUser { public static void main(String[] args) { // 1.加载spring配置文件,进行对象创建 ApplicationContext context = new ClassPathXmlApplicationContext(\"bean.xml\"); // 2.获取spring创建好的对象 User user = (User) context.getBean(\"user\"); // 3.使用对象调用方法测试 System.out.println(\"user = \" + user); user.add(); }}
2.3 对象存储

存放到容器中,查看源码,DefaultListableBeanFactory.java,第164行

spring全面详解-最全最详细的spring基本认识和入门使用

key:唯一标识

value:类的定义(描述信息)

可以查看 BeanDefinition 的源码,有类的描述信息,是否初始化的状态等等

IoC容器

​ IoC 是 Inversion of Control 的简写,译为 控制反转。

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

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

1 控制反转IoC

  • 控制反转是一种思想

    • 将对象的创建权利交出去,交给第三方容器负责
    • 将对象和对象之间的关系维护权交出去,交给第三方容器负责
  • 如何实现

    通过依赖注入DI的方式实现

2 依赖注入DI

DI (Dependency Injection):依赖注入,依赖注入实现了控制反转的思想,是指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。

依赖注入常见的实现方式有两种:

  1. set注入
  2. 构造注入

所以 IoC 是一种控制反转的思想,而 DI 是对IoC的一种具体实现。

Bean管理:指Bean对象的创建,以及Bean对象中属性的赋值(或Bean对象之间关系的维护)

3 IoC容器实现

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

① BeanFactory

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

② ApplicationContext

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

源码说明:

spring全面详解-最全最详细的spring基本认识和入门使用

③ ApplicationContext的主要实现类

类型 说明 ClassPathXmlApplicationContext 通过读取类路径下的xml格式配置文件创建IoC容器对象 FileSystemApplicationContext 通过文件系统路径读取xml格式配置文件创建IoC容器对象

4 基于XML管理bean

4.1 环境准备

① 创建子工程 spring-ioc-xml

spring全面详解-最全最详细的spring基本认识和入门使用

② pom.xml中引入spring依赖,并刷新maven

<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.24</version> </dependency></dependencies>

③ 创建spring配置文件:resources/bean.xml

spring全面详解-最全最详细的spring基本认识和入门使用

④ 在工程目录java下创建类 cn.tedu.spring.iocxml.User

public class User { private String username; private String password; public void userMethod(){ System.out.println(\"userMethod执行~~\"); }}
4.2 获取bean方式
  • 根据id获取

    id属性是bean的唯一标识,所以根据bean标签的id属性可以精确获取到一个组件对象。

    ① bean.xml

    <bean id=\"user\" class=\"cn.tedu.spring.iocxml.User\"></bean>

    ② 创建测试类UserTest

    public class UserTest { public static void main(String[] args) { // 1.加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext(\"bean.xml\"); // 2.根据id获取bean User user1 = (User) context.getBean(\"user\"); System.out.println(\"1-根据id获取对象:\" + user1); user1.userMethod(); }}
  • 根据类型获取

    User user2 = context.getBean(User.class);System.out.println(\"2-根据类型获取bean:\" + user2);user2.userMethod();
  • 根据id和类型获取

    User user3 = context.getBean(\"user\", User.class);System.out.println(\"3-根据id和类型获取bean:\" + user3);user3.userMethod();
  • 注意

    当根据类型获取bean时,要求IoC容器中指定类型的bean只能有一个,当配置两个时会抛出异常

    <bean id=\"user\" class=\"cn.tedu.spring.iocxml.User\"></bean><bean id=\"user2\" class=\"cn.tedu.spring.iocxml.User\"></bean>

    spring全面详解-最全最详细的spring基本认识和入门使用

4.3 基于setter依赖注入

类有属性,创建对象过程中,向属性注入具体的值

方式1:使用set方法完成(使用xml中的标签实现

方式2:基于构造器完成

案例

① 创建Package名为dibase,创建Book类

package cn.tedu.spring.DI;public class Book { private String bookName; private String bookAuthor; // 无参构造函数 public Book() {} // 全参构造函数 public Book(String bookName, String bookAuthor) { this.bookName = bookName; this.bookAuthor = bookAuthor; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public String getBookAuthor() { return bookAuthor; } public void setBookAuthor(String bookAuthor) { this.bookAuthor = bookAuthor; } @Override public String toString() { return \"Book{\" + \"bookName=\'\" + bookName + \'\\\'\' + \", bookAuthor=\'\" + bookAuthor + \'\\\'\' + \'}\'; }}

② 创建spring配置文件:resources目录下创建 bean-di.xml

<bean id=\"book\" class=\"cn.tedu.spring.DI.Book\">  <property name=\"bookName\" value=\"java\"></property> <property name=\"bookAuthor\" value=\"tedu\"></property></bean>

③ 创建测试类TestBook进行测试

public class BookTest { // spring的set方法注入 @Test public void springSetTest(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-di.xml\"); Book book = context.getBean(\"book\", Book.class); System.out.println(\"book = \" + book); }}
4.4 基于构造器依赖注入
  • 说明

    通过构造器方式实现依赖注入

  • 操作步骤说明

    1. 创建类,定义属性,生成有参数构造方法
    2. 进行xml配置
    3. 创建测试类测试

① 创建电影信息类Film,定义属性并生成全参构造方法

public class Film { // 电影名称、主演 private String title; private String actor; // 全参构造 public Film(String title, String actor) { System.out.println(\"Film的有参构造已经执行~~\"); this.title = title; this.actor = actor; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getActor() { return actor; } public void setActor(String actor) { this.actor = actor; } @Override public String toString() { return \"Film{\" + \"title=\'\" + title + \'\\\'\' + \", actor=\'\" + actor + \'\\\'\' + \'}\'; }}

② 在bean-di.xml中进行注入配置

<bean id=\"film\" class=\"cn.tedu.spring.DI.Film\"> <constructor-arg name=\"title\" value=\"霸王别姬\"></constructor-arg> <constructor-arg name=\"actor\" value=\"张国荣\"></constructor-arg></bean>

③ 创建测试类TestFilm测试

public class FilmTest { @Test public void FilmConsDITest(){ // 1.加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-di.xml\"); // 2.获取指定bean Film film = context.getBean(\"film\", Film.class); // 3.输出测试 System.out.println(\"film = \" + film); }}
4.5 特殊值处理注入
4.5.1 字面量赋值

string number = 10;

声明一个变量number,初始化为 10,此时number就不代表字符number了,而是作为一个变量的名字。当引用number时,实际拿到的结果是 10。

而如果number是带引号的 “number” ,则它不是一个变量,而代表 number 本身这个字符串。

这就是字面量,所以字面量没有引申含义,就是我们看到的这个数据本身。

<property name=\"number\" value=\"1016\"></property>
4.5.2 null值

使用 标签,或者 标签 实现注入。

① Film类中增加电影描述属性

// 1.电影描述private String description;// 2.生成对应的 set() get() 方法,重新生成toString()方法// 3.重新生成全参构造方法

② bean-di.xml配置文件调整

<bean id=\"film\" class=\"cn.tedu.spring.DI.Film\"> <constructor-arg name=\"title\" value=\"霸王别姬\"></constructor-arg> <constructor-arg name=\"actor\" value=\"张国荣\"></constructor-arg>  <constructor-arg name=\"description\"> <null></null> </constructor-arg></bean>

③ 执行测试类进行测试

  • 课堂练习

    cn.tedu.spring下创建包exercise,在包下创建商品表 Product,类属性如下:

    商品标题:title

    商品库存:num

    商品销量:sales

    商品描述:comment

    1. 实现 商品Product类的创建,setter() getter() toString(),

    2. 通过配置文件bean-product.xml

      通过set方式注入一组数据(商品描述为null值);

      通过构造参数方式注入一组数据(商品描述为null值);

    3. 创建测试类TestProduct进行测试。

  • 练习答案

① Product类

public class Product { private String title; private Integer num; private Integer sales; private String comment; // 无参构造函数、有参构造函数 setter() getter() toString() }

② bean-product.xml

<bean id=\"product\" class=\"cn.tedu.spring.exercise.Product\"> <property name=\"title\" value=\"手机\"></property> <property name=\"num\" value=\"100\"></property> <property name=\"sales\" value=\"1000\"></property> <property name=\"comment\"> <null></null> </property></bean><bean id=\"productCons\" class=\"cn.tedu.spring.exercise.Product\"> <constructor-arg name=\"title\" value=\"电脑\"/> <constructor-arg name=\"num\" value=\"2\"/> <constructor-arg name=\"sales\" value=\"3\"/> <constructor-arg name=\"comment\"> <null></null> </constructor-arg></bean>

③ ProductTest测试类

@Testpublic void testProduct(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-product.xml\"); Product product = context.getBean(\"product\", Product.class); System.out.println(\"product = \" + product);}
4.5.3 xml实体
  • 说明

    小于号、大于号在XML文档中用来定义标签的开始,具有特殊含义,在注入属性值时不能够随便使用,

    可以使用XML实体 < > 来代替

  • 表示方式

    普通字符 xml实体 < < > >

查看示例 bean-di.xml:

<bean id=\"filmEntity\" class=\"cn.tedu.spring.DI.Film\"> <constructor-arg name=\"title\" value=\"霸王别姬\"></constructor-arg> <constructor-arg name=\"actor\" value=\"张国荣\"></constructor-arg>  <constructor-arg name=\"description\" value=\"<span class=\"token entity named-entity\" title=\"<真好看啊电影\">>\"></constructor-arg></bean>
4.5.4 CDATA区

CDATA区,是xml中一种特有的写法,在CDATA区中可以包含特殊符号

表示方式:

,在内容区域可以存放普通字符和特殊符号

CDATA区存放特殊符号演示

<bean id=\"filmCdata\" class=\"cn.tedu.spring.DI.Film\"> <constructor-arg name=\"title\" value=\"霸王别姬\"></constructor-arg> <constructor-arg name=\"actor\" value=\"张国荣\"></constructor-arg>  <constructor-arg name=\"description\">  <value><![CDATA[]]></value> </constructor-arg></bean>
4.6 对象类型属性注入

需要注入的数据类型为对象,而不是一个字面量。

  • 环境准备

    准备一个一对多案例,比如部门Dept和员工Emp是一对多的关系。

① 创建包diobj,并创建部门类Dept

public class Dept { // 部门名称 private String dName; // 定义方法,用于测试输出 public void deptFunc(){ System.out.println(\"Dept部门名称:\" + dName); } public void setdName(String dName) { this.dName = dName; } public String getdName() { return dName; } @Override public String toString() { return \"Dept{\" + \"dName=\'\" + dName + \'\\\'\' + \'}\'; }}

② 创建员工类Emp,创建setter() getter() 和 toString()方法

public class Emp { // 员工所属部门的对象、姓名、工资 private Dept dept; private String eName; private Double salary; // 定义方法测试 public void work(){ System.out.println(eName + \"薪资:\" + salary); dept.deptFunc(); } public void setDept(Dept dept) { this.dept = dept; } public void seteName(String eName) { this.eName = eName; } public void setSalary(Double salary) { this.salary = salary; } public Dept getDept() { return dept; } public String geteName() { return eName; } public Double getSalary() { return salary; } @Override public String toString() { return \"Emp{\" + \"dept=\" + dept + \", eName=\'\" + eName + \'\\\'\' + \", salary=\" + salary + \'}\'; }}
4.6.1 引用外部bean

说明:

​ 可以通过在当前bean标签中通过 ref属性引用外部bean的方式实现。

示例:通过使用外部bean方式,在员工中注入部门对象

① 配置文件 bean-diobj.xml

<bean id=\"dept1\" class=\"cn.tedu.spring.diobj.Dept\"> <property name=\"dName\" value=\"开发部\"></property></bean><bean id=\"emp1\" class=\"cn.tedu.spring.diobj.Emp\">  <property name=\"eName\" value=\"张三丰\"></property> <property name=\"salary\" value=\"50000.0\"></property>  <property name=\"dept\" ref=\"dept1\"></property></bean>

② 创建测试类测试 TestDept

public class TestDept { // 对象类型注入测试用例 @Test public void testObjDI(){ // 1.加载xml配置文件 ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-diobj.xml\"); // 2.获取bean对象 Emp emp1 = context.getBean(\"emp1\", Emp.class); // 3.测试(调用员工emp对象的方法) System.out.println(\"emp1 = \" + emp1); emp1.work(); }}
4.6.2 内部bean

在需要注入对象的bean标签中内嵌 对象类型属性的 bean标签即可。

​ 内嵌bean

① bean-diobj.xml进行属性注入配置

<bean id=\"emp2\" class=\"cn.tedu.spring.diobj.Emp\"> <property name=\"eName\" value=\"张无忌\"/> <property name=\"salary\" value=\"8000.0\"/>  <property name=\"dept\"> <bean id=\"dept2\" class=\"cn.tedu.spring.diobj.Dept\"> <property name=\"dName\" value=\"销售部\"/> </bean> </property></bean>

② 使用测试类测试

// 对象类型注入:内嵌bean@Testpublic void testObjDi2(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-diobj.xml\"); Emp emp2 = context.getBean(\"emp2\", Emp.class); System.out.println(\"emp2 = \" + emp2); emp2.work();}
4.6.3 级联属性赋值(了解)

可以在标签中给需要注入对象的属性重新赋值!

① 配置文件编写

<bean id=\"dept3\" class=\"cn.tedu.spring.diobj.Dept\"> <property name=\"dName\" value=\"市场部\"/></bean><bean id=\"emp3\" class=\"cn.tedu.spring.diobj.Emp\">  <property name=\"eName\" value=\"赵敏\"/> <property name=\"salary\" value=\"5000.0\"/>  <property name=\"dept\" ref=\"dept3\"/>  <property name=\"dept.dName\" value=\"客服部\"></property></bean>

② 测试类测试

// 对象类型注入:级联属性赋值@Testpublic void testObjDi3(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-diobj.xml\"); Emp emp3 = context.getBean(\"emp3\", Emp.class); System.out.println(\"emp3 = \" + emp3); emp3.work();}
4.7 数组类型属性注入

使用 标签和子标签实现。

说明:一个人除了姓名、年龄等属性外,还会有爱好,一个人的爱好可能有多个,可以把多个爱好存入数组中。

创建包:cn.tedu.spring.diarray

① 在diarray包中创建类:Person

public class Person { // 姓名、年龄、爱好 private String name; private String age; private String[] hobby; // 定义测试方法 public void run(){ System.out.println(\"Persen is running...\"); // 打印数组测试 System.out.println(Arrays.toString(hobby)); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } @Override public String toString() { return \"Person{\" + \"name=\'\" + name + \'\\\'\' + \", age=\'\" + age + \'\\\'\' + \", hobby=\" + Arrays.toString(hobby) + \'}\'; }}

新建配置文件:bean-diarray.xml 进行注入

<bean id=\"person\" class=\"cn.tedu.spring.diarray.Person\">  <property name=\"name\" value=\"孙悟空\"/> <property name=\"age\" value=\"36\"/> <!-- 数组属性注入,使用标签 --> <property name=\"hobby\"> <array> <value>抽烟</value> <value>喝酒</value> <value>烫头</value> </array> </property></bean>

③ 编写测试类TestPerson测试

public class TestPerson { // 数组注入测试用例 @Test public void testArray(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-diarray.xml\"); Person person = context.getBean(\"person\", Person.class); System.out.println(\"person = \" + person); person.run(); }}
4.8 集合类型属性注入
4.8.1 List集合属性注入

场景1:使用 标签下的 子标签和 子标签实现。

场景2:使用 标签下的 子标签和子标签实现。ref标识引用其他的bean

环境说明:创建老师类Student和学生类Student,一个老师可以有多个学生,在老师类中存入所教学生的对象,将其存入List集合中。

环境准备

① 创建包 dimap

② Teacher类

public class Teacher { // 老师姓名 private String tName; // 老师所教学生的对象,放到List集合中 private List<Student> studentList; public String gettName() { return tName; } public void settName(String tName) { this.tName = tName; } public List<Student> getStudentList() { return studentList; } public void setStudentList(List<Student> studentList) { this.studentList = studentList; } @Override public String toString() { return \"Teacher{\" + \"tName=\'\" + tName + \'\\\'\' + \", studentList=\" + studentList + \'}\'; }}

③ Student类

public class Student { // 学生姓名、年龄 private String sName; private String age; public String getsName() { return sName; } public void setsName(String sName) { this.sName = sName; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return \"Student{\" + \"sName=\'\" + sName + \'\\\'\' + \", course=\'\" + course + \'\\\'\' + \'}\'; }}

④ 创建配置文件:bean-dilistmap.xml 进行注入

<bean id=\"stu1\" class=\"cn.tedu.spring.dimap.Student\"> <property name=\"sName\" value=\"梁山伯\"/> <property name=\"age\" value=\"43\"/></bean><bean id=\"stu2\" class=\"cn.tedu.spring.dimap.Student\"> <property name=\"sName\" value=\"祝英台\"/> <property name=\"age\" value=\"33\"/></bean><bean id=\"teacher\" class=\"cn.tedu.spring.dimap.Teacher\">  <property name=\"tName\" value=\"沙师弟\"/>  <property name=\"studentList\"> <list> <ref bean=\"stu1\"/> <ref bean=\"stu2\"/> </list> </property></bean>

⑤ 测试类TestTeacher测试

public class TestTeacher { @Test public void testListMap(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-dilistmap.xml\"); Teacher teacher = context.getBean(\"teacher\", Teacher.class); System.out.println(\"teacher = \" + teacher); List<Student> list = teacher.getStudentList(); for (Student student: list) { System.out.println(student); } }}
4.8.2 Map集合属性注入

使用标签下的

子标签、子标签、子标签 子标签 子标签实现

<bean id=\"xxx\" class=\"xxx\"><property name=\"xxx\"> <map> <entry> <key><value>xxx</value></key> <value>xxx</value> </entry> <entry> <key><value>xxx</value></key> <ref bean=\"xxx\"></ref> </entry> </map></property></bean>

说明:使用上述的老师类和学生类,一个学生也可以有多个老师,在学生类Student中添加老师的属性,放到Map集合中。

① 调整Student类

// 1.学生的老师:可以有多个,放到Map集合中private Map<String,String> teacherMap;// 2.生成setter() getter()方法,重新生成toString()方法

② 创建配置文件:bean-dimap.xml

<bean id=\"stuMap\" class=\"cn.tedu.spring.dilistmap.Student\"> <property name=\"sName\" value=\"步惊云\"/> <property name=\"age\" value=\"36\"/> <property name=\"teacherMap\"> <map> <entry> <key>  <value>1111</value> </key> <value>雄霸</value> </entry> <entry> <key>  <value>2222</value> </key> <value>断浪</value> </entry> <entry> <key>  <value>3333</value> </key> <value>大空翼</value> </entry> </map> </property></bean>

③ 创建测试类进行测试 TestMap

@Testpublic void testMap(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-dimap.xml\"); Student student = context.getBean(\"stuMap\", Student.class); System.out.println(\"student = \" + student);}
4.8.3 引用集合类型bean注入
  • 说明

    通过使用 标签实现

  • 使用步骤

    • 在xml配置文件中引入util约束

      <beansxmlns:util=\"http://www.springframework.org/schema/util\"xsi:schemaLocation=\" http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd\">  </beans>
    • 使用util标签进入注入

      <util:map id=\"xxx\"></util:map><util:list id=\"xxx\"></util:list>

环境准备及操作步骤

添加课程类,一个学生可以上多门课程

① 在Student类中添加List集合属性

// 1.一个学生可以上多门课程,把课程名称放到List集合中 private List<String> courseList;// 2.生成get和set方法// 3.重新生成toString()方法

② 创建spring配置文件:bean-diref.xml,引入util约束

<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\"></beans>

③ 配置xml文件完成注入

<bean id=\"stuUtil\" class=\"cn.tedu.spring.dilistmap.Student\"> <property name=\"sName\" value=\"孔慈\"/> <property name=\"age\" value=\"36\"/> <property name=\"teacherMap\" ref=\"teacherMap\"></property> <property name=\"courseList\" ref=\"courseList\"></property></bean><util:map id=\"teacherMap\"> <entry> <key> <value>10000</value> </key> <value>小泽老师</value> </entry> <entry> <key> <value>10001</value> </key> <value>王老师</value> </entry></util:map><util:list id=\"courseList\"> <value>Spring</value> <value>SpringMVC</value> <value>MyBatis</value></util:list>

④ 创建测试方法进行测试

// 引用集合类型bean注入(util)@Testpublic void testRefBean(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-diref.xml\"); Student student = context.getBean(\"stuUtil\", Student.class); System.out.println(\"student = \" + student);}
4.9 p命名空间

这也是一种注入方式,可以在xml中定义命名空间或者叫名称空间,可以简化xml代码。

在bean-diref.html中操作

① 在xml配置文件中定义命名空间

xmlns:p=\"http://www.springframework.org/schema/p\"

② 在xml文件进行命名空间属性注入

<bean id=\"studentp\" class=\"cn.tedu.spring.iocxml.dimap.Student\" p:sid=\"100\" p:sname=\"铁锤妹妹\" p:courseList-ref=\"courseList\" p:teacherMap-ref=\"teacherMap\">

③ 测试

// p命名空间注入测试用例@Testpublic void testRefP(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-diref.xml\"); Student studentp = context.getBean(\"studentp\", Student.class); System.out.println(\"studentp = \" + studentp);}
4.10 引入外部属性文件
  • 说明

    ​ 当前所有的配置和数据都在xml文件中,一个文件中有很多bean,修改和维护起来很不方便,生产环境中会把特定的固定值放到外部文件中,然后引入外部文件进行注入,比如数据库连接信息。

  • 示例

    将外部文件中的数据引入xml配置文件进行注入

① pom.xml中引入数据库相关依赖,并刷新maven

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version></dependency><dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version></dependency>

② resources目录下创建外部属性文件,一般为properties格式,定义数据库信息,比如:jdbc.properties

jdbc.user=rootjdbc.password=rootjdbc.url=jdbc://mysql://localhost:3306/springjdbc.driver=com.mysql.cj.jdbc.Driver

③ 创建spring配置文件bean-jdbc.xml,引入context的命名空间

使用context可以为XML外部实体注入定义,使得解析器在解析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/context http://www.springframework.org/schema/context/spring-context.xsd  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\">  <context:property-placeholder location=\"classpath:jdbc.properties\"></context:property-placeholder>  <bean id=\"druidDataSource\" class=\"com.alibaba.druid.pool.DruidDataSource\"> <property name=\"url\" value=\"${jdbc.url}\"></property> <property name=\"username\" value=\"${jdbc.user}\"></property> <property name=\"password\" value=\"${jdbc.password}\"></property> <property name=\"driverClassName\" value=\"${jdbc.driver}\"></property> </bean></beans>

④ 创建包jdbc,包中创建测试类 TestJdbc

public class TestJdbc { // 外部文件属性引入测试用例 @Test public void demo02(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-jdbc.xml\"); DruidDataSource druidDataSource = context.getBean(\"druidDataSource\", DruidDataSource.class); System.out.println(druidDataSource.getUrl()); }}
4.11 bean的作用域
  • 说明

    ​ bean的作用域,是指在交给spring创建bean对象时,可以指定是单实例还是多实例,通过bean标签中的scope属性来指定,默认是单实例。

  • 单实例和多实例

    • 单实例

      单实例(Singleton)是指某个类只能创建唯一的一个实例对象,并且该类提供一个全局的访问点(静态方法)来让外界获取这个实例,常常用在那些只需要一个实例来处理所有任务的场景下,例如配置类或数据库连接池等。

    • 多实例

      多实例(Multiple Instance)则是指可以在同一个类的定义下,创建多个实例对象。每个对象都是相互独立的,有自己的状态和行为;常常用于需要同时处理多个任务的场景。

在Spring中可以通过配置bean标签的scope属性来之地那个bean的作用域范围,具体如下

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

案例演示

① 创建包scope,并在包下创建类Sku

public class Sku { }

② 创建spring的配置文件:bean-scope.xml

<bean id=\"sku\" class=\"cn.tedu.spring.scope.Sku\" scope=\"singleton\"></bean>

③ 创建测试类TestOrders

@Testpublic void testOrders(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-scope.xml\"); Orders orders = context.getBean(\"orders\", Orders.class); System.out.println(\"orders = \" + orders); Orders orders1 = context.getBean(\"orders\", Orders.class); System.out.println(\"orders1 = \" + orders1);}// 单实例,sku1和sku2地址相同// 多实例,sku1和sku2地址不同
4.12 bean的生命周期

是指一个bean对象从创建到销毁的整个过程。

4.12.1 bean的完整生命周期
  1. 实例化阶段(bean对象创建)

    在这个阶段中,容器会创建一个Bean的实例,并为其分配空间。这个过程可以通过构造方法完成。

  2. 属性赋值阶段

    在实例化完Bean之后,容器会把Bean中的属性值注入到Bean中,这个过程可以通过set方法完成。

  3. 初始化阶段(bean对象初始化)

    在属性注入完成后,容器会对Bean进行一些初始化操作;

    • 初始化之前:bean的后置处理器可以接收到bean,此处可以对bean做相关操作。
    • 初始化之后:bean的后置处理器可以接收到bean,此处可以对bean做相关操作。
  4. 使用阶段

    初始化完成后,Bean就可以被容器使用了

  5. 销毁阶段

    容器在关闭时会对所有的Bean进行销毁操作,释放资源。

4.12.2 生命周期验证

① 创建包life,创建类User

package cn.tedu.spring.life;import org.springframework.beans.BeansException;public class User { private String username; // 1.无参数构造 public User(){ System.out.println(\"1-bean对象创建,调用无参数构造。\"); } // 3.初始化阶段 public void initMethod(){ System.out.println(\"3-bean对象初始化,调用指定的初始化方法\"); } // 5.销毁阶段 public void destoryMethod(){ System.out.println(\"5-bean对象销毁,调用指定的销毁方法\"); } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; // 2.给bean对象属性赋值 System.out.println(\"2-通过set方法给bean对象赋值。\"); } @Override public String toString() { return \"User{\" + \"username=\'\" + username + \'\\\'\' + \'}\'; }}

② 创建spring配置文件 bean-life.xml

<bean id=\"user\" class=\"cn.tedu.spring.iocxml.life.User\" scope=\"singleton\" init-method=\"initMethod\" destroy-method=\"destroyMethod\"> <property name=\"username\" value=\"聂风\"></property></bean>

③ 创建测试类TestUser测试

@Testpublic void testUser(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(\"bean-life.xml\"); User user = context.getBean(\"user\", User.class); // 4.bean对象初始化完成,可以使用 System.out.println(\"4-bean对象初始化完成,开发者可以使用了。\"); // 销毁bean context.close();}

④ 后置处理器处理演示,新建类MyBeanPost

public class MyBeanPost implements BeanPostProcessor { // BeanPostProcessor接口 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(\"3之前:bean后置处理器,在初始化之前执行。\" + beanName + \":\" + bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(\"3之后:bean后置处理器,在初始化之后执行。\" + beanName + \":\" + bean); return bean; }}

⑤ 在spring的配置文件bean-life.xml中进行后置处理器配置

<bean id=\"myBeanPost\" class=\"cn.tedu.spring.life.MyBeanPost\"></bean>

⑥ 运行测试类测试

4.12.3 bean生命周期扩展
  • bean的初始化和销毁应用场景

    • 初始化
      • 创建连接池
      • 加载资源文件
      • 进行数据校验
    • 销毁
      • 关闭连接池
      • 保存数据
      • 释放占用的资源
  • 后置处理器

    实现自定义的Bean对象处理逻辑,比如在Bean实例化之前或者之后对Bean对象进行自定义的修改,可以方便地实现自定义逻辑和修改Bean对象的行为。

4.13 基于xml自动装配

自动装配说明:

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

环境准备

① 创建包auto,创建部门和员工的两个java类

② 部门类 Dept

public class Dept { private String dName; @Override public String toString() { return \"Dept{\" + \"dName=\'\" + dName + \'\\\'\' + \'}\'; } public String getdName() { return dName; } public void setdName(String dName) { this.dName = dName; }}

③ 员工类 Emp

public class Emp { private String eName; private Dept dept; @Override public String toString() { return \"Emp{\" + \"eName=\'\" + eName + \'\\\'\' + \", dept=\" + dept + \'}\'; } public String geteName() { return eName; } public void seteName(String eName) { this.eName = eName; } public Dept getDept() { return dept; } public void setDept(Dept dept) { this.dept = dept; }}

④ 创建spring配置文件bean-auto.xml

<bean id=\"dept\" class=\"cn.tedu.spring.iocxml.auto.Dept\"> <property name=\"dName\" value=\"技术部\"></property></bean><bean id=\"emp\" class=\"cn.tedu.spring.iocxml.auto.Emp\" autowire=\"byType\"> <property name=\"eName\" value=\"步惊云\"></property></bean>

⑤ 创建测试类测试TestAuto

@Testpublic void testAuto(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean-auto.xml\"); Emp emp = context.getBean(\"emp\", Emp.class); System.out.println(\"emp = \" + emp);}

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

自动装配方式:byType

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

​ 1. 如果在IoC中,没有任何一个兼容类型的bean能够为属性赋值,则改属性不装配,默认值为null;

​ 2. 如果在IoC中,有多个兼容类型的bean能够为属性赋值,则抛出异常 NoUniqueBeanDefinitionException

自动装配方式:byName

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

5 基于注解管理bean

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

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

Spring通过注解实现自动装配:

  1. 引入依赖
  2. 开启组件扫描
  3. 使用注解定义Bean
  4. 依赖注入
5.1 创建子工程

子工程:spring-ioc-annotation

在pom.xml中添加springframework的依赖,刷新maven

<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.24</version> </dependency></dependencies>
5.2 开启组件扫描

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

① 工程下创建包:cn.tedu.spring.bean

② resources目录下创建spring配置文件 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\">   <context:component-scan base-package=\"cn.tedu.spring\"></context:component-scan> </beans>
5.3 使用注解定义Bean

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

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

③ 创建User类,并添加注解

// value可以不写,默认为类名首字母小写//@Component(value = \"user\") // //@Repository//@Service@Controllerpublic class User {}

④ 创建测试类测试TestUser

public class TestUser { @Test public void testUser(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean.xml\"); User user = context.getBean(\"user\", User.class); System.out.println(\"user = \" + user); }}
5.4 @Autowired注入

单独使用@Autowired注解,默认根据类型装配(byType)

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

5.4.1 属性注入

① cn.tedu.spring下创建包autowired,并在autowired下创建两个包:controller包 和 service包

② 控制器层controller.UserController

public class UserController { private UserService userService; public void addController(){ System.out.println(\"controller is running...\"); userService.addService(); }}

③ 服务层service.UserService接口

public interface UserService { public void addService();}

④ 服务层service.UserServiceImpl接口的实现类

public class UserServiceImpl implements UserService { @Override public void addService() { System.out.println(\"service is running...\"); }}

⑤ 在UserController和UserSerivceImpl中添加@Controller注解和@Service注解

⑥ 在UserController中注入UserServiceImpl

@Controllerpublic class UserController { // 注入service // 第一种方式:属性注入 @Autowired // 根据类型找到对象,完成注入 private UserService userService;}

⑦ 测试类测试autowired.TestUserController

public class TestUserController { @Test public void testUserController(){ ApplicationContext context = new ClassPathXmlApplicationContext(\"bean.xml\"); UserController controller = context.getBean(UserController.class); controller.addController(); }}
5.4.2 set注入

① 修改UserController类

// 方式二:通过set方法注入private UserService userService;@Autowiredpublic void setUserService(UserService userService) { this.userService = userService;}

② 测试

5.4.3 构造方法注入

① 修改UserController类

// 第三种方式:构造方法注入private UserService userService;@Autowiredpublic UserController(UserService userService) { this.userService = userService;}

② 测试

5.4.4 形参上注入

① 修改UserController类

// 第四种方式:形参注入private UserService userService;public UserController(@Autowired UserService userService) { this.userService = userService;}

② 测试

5.4.5 只有一个构造函数,无注解

① 修改UserController类

// 第五种方式:只有一个有参数构造函数,无注解private UserService userService;public UserController(UserService userService) { this.userService = userService;}

② 测试

5.4.6 @Autowire注解和@Qualifier注解联合

① 再创建一个UserService接口的实现类service.UserServiceImpl2

@Servicepublic class UserServiceImpl2 implements UserService{ @Override public void addService() { System.out.println(\"service2 is running...\"); }}

② 测试发现报错

​ 因为UserService有两个实现类,而@Autowired注解根据byType定位,所以找到了两个实现类

③ 解决:修改UserController (使用两个注解)

// 1.第六种方式:根据类型和名称一起注入@Autowired@Qualifier(value = \"userServiceImpl2\") // 类名首字母小写private UserService userService;// 2.将构造函数注释
5.5 @Resource注入

@Resource注解也可以完成属性注入。它和@Autowired注解的区别如下

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

案例演示

① 工程下创建包 resource,和之前一样,创建controller和service两个包,并创建UserController类和UserService接口以及该接口的实现类UserServiceImpl

② 修改UserController

@Controller(\"myUserController\")public class UserController { // 根据名称进行注入 @Resource(name=\"myUserService\") private UserService userService; public void add(){ System.out.println(\"controller...\"); userService.add(); }}

③ 修改ServiceControllerImpl1

@Service(\"myUserService\")public class UserServiceImpl implements UserService {

⑤ 测试

  1. 指定@Resource中的name,则根据名称装配
  2. 未指定name时,则根据属性名装配
  3. 未指定name,属性名也不一致,则根据类型装配
5.6 Spring全注解开发

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

① 工程下创建包:config,创建类SpringConfig

// 配置类@Configuration// 开启组件扫描@ComponentScan(\"cn.tedu.spring\")public class SpringConfig {}

② 在resource下创建测试类进行测试

public class TestUserControllerAnno { public static void main(String[] args) { // 加载配置类 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserController controller = context.getBean(UserController.class); controller.add(); }}