当前位置: 代码迷 >> 综合 >> Spring5 核心篇
  详细解决方案

Spring5 核心篇

热度:54   发布时间:2023-12-22 04:58:48.0

目录

  • 第一章 QuickStart
    • 1.1、文章介绍
    • 1.2、框架介绍
  • 第二章 IOC Container
    • 2.1、IOC容器的概述
    • 2.2、IOC容器的原理
    • 2.3、IOC容器的入门
      • 2.3.1、环境的准备
      • 2.3.2、容器的创建
    • 2.4、IOC容器的Bean管理(XML方式)
      • 2.4.1、Bean的配置
      • 2.4.2、Bean的获取
      • 2.4.3、Bean的别名
      • 2.4.4、Bean的依赖注入
        • 2.4.4.1、基于 setter 方法的依赖注入
          • 2.4.4.1.1、注入基本类型
          • 2.4.4.1.2、注入空值类型
          • 2.4.4.1.3、注入特殊符号
          • 2.4.4.1.4、注入对象类型
          • 2.4.4.1.5、注入集合类型
            • 2.4.4.1.5.1、集合中数据为基本类型
            • 2.4.4.1.5.2、集合中数据为对象类型
          • 2.4.4.1.6、注入级联属性
          • 2.4.4.1.7、注入属性信息
            • 2.4.4.1.7.1、注入内部属性对象
            • 2.4.4.1.7.2、注入外部属性文件
            • 2.4.4.1.7.3、注入系统属性信息
        • 2.4.4.2、基于有参构造方法的依赖注入
          • 2.4.4.2.1、注入基本类型
          • 2.4.4.2.2、注入空值类型
          • 2.4.4.2.3、注入特殊符号
          • 2.4.4.2.4、注入对象类型
          • 2.4.4.2.5、注入集合类型
            • 2.4.4.2.5.1、集合中数据为基本类型
            • 2.4.4.2.5.2、集合中数据为对象类型
        • 2.4.4.3、基于p名称空间的依赖注入
          • 2.4.4.3.1、注入基本类型
          • 2.4.4.3.2、注入对象类型
        • 2.4.4.4、基于c名称空间的依赖注入
          • 2.4.4.4.1、注入基本类型
          • 2.4.4.4.2、注入对象类型
      • 2.4.5、Bean的自动注入
        • 2.4.5.1、根据Bean的名称自动注入
        • 2.4.5.2、根据Bean的类型自动注入
      • 2.4.6、Bean的实例化
        • 2.4.6.1、使用无参构造函数实例化
        • 2.4.6.2、使用静态工厂方法实例化
        • 2.4.6.3、使用实例工厂方法实例化
      • 2.4.7、Bean的前置实例化
      • 2.4.8、Bean的延迟实例化
      • 2.4.9、Bean的作用域
        • 2.4.9.1、单例模式
        • 2.4.9.2、多例模式
      • 2.4.10、Bean的生命周期
        • 2.4.10.1、默认的生命周期
        • 2.4.10.2、特殊的生命周期
    • 2.5、IOC容器的Bean管理(注解方式)
      • 2.5.1、半注解开发
        • 2.5.1.1、Bean的配置
        • 2.5.1.2、Bean的依赖注入
          • 2.5.1.2.1、根据类型自动注入
          • 2.5.1.2.2、根据名称自动注入
          • 2.5.1.2.3、注入基本类型属性
          • 2.5.1.2.4、注入外部属性文件
        • 2.5.1.3、Bean的作用域
          • 2.5.1.3.1、单例模式
          • 2.5.1.3.2、多例模式
      • 2.5.2、全注解开发
        • 2.5.2.1、扫描式注册
        • 2.5.2.2、配置式注册
        • 2.5.2.3、环境的切换
        • 2.5.2.4、属性配置导入
        • 2.5.2.5、普通对象导入
        • 2.5.2.6、原生配置导入
  • 第三章 AOP
    • 3.1、AOP的概述
    • 3.2、AOP的原理
      • 3.2.1、代理概述
      • 3.2.2、JDK静态代理
      • 3.2.3、JDK动态代理
      • 3.2.4、CGLIB静态代理
      • 3.2.5、CGLIB动态代理
    • 3.3、AOP的入门
      • 3.3.1、环境的准备
      • 3.3.2、术语的介绍
    • 3.4、AOP的配置(XML方式)
    • 3.5、AOP的配置(注解方式)
      • 3.5.1、半注解开发
      • 3.5.2、全注解开发
  • 第四章 Test
    • 4.1、整合Junit4单元测试
    • 4.2、整合Junit5单元测试
    • 4.3、整合log4j2日志框架
  • 第五章 JdbcTemplate
    • 5.1、JdbcTemplate的概述
    • 5.2、JdbcTemplate的环境准备
    • 5.3、JdbcTemplate的基本使用
      • 5.3.1、创建数据表
      • 5.3.2、删除数据表
      • 5.3.3、插入一条数据
      • 5.3.4、修改一条数据
      • 5.3.5、删除一条数据
      • 5.3.6、查询所有数据
      • 5.3.7、查询一条数据
      • 5.3.8、查询记录个数
  • 第六章 JmsTemplate
    • 6.1、JmsTemplate的概述
    • 6.2、JmsTemplate的环境准备
    • 6.3、JmsTemplate的基本使用
      • 6.3.1、点对点模式
        • 6.3.1.1、消息生产者
        • 6.3.1.2、消息消费者
      • 6.3.2、发布订阅模式
        • 6.3.2.1、消息生产者
        • 6.3.2.2、消息消费者
  • 第七章 Transactions
    • 7.1、Transactions的概述
    • 7.2、Transactions的环境准备
    • 7.3、Transactions的配置(XML方式)
      • 7.3.1、事务模板方式
      • 7.3.2、AOP切面方式
    • 7.4、Transactions的配置(注解方式)
      • 7.4.1、半注解开发
      • 7.4.2、全注解开发


配套资料,免费下载
链接:https://pan.baidu.com/s/14AOFoYKdu-QDAFf_3GvwqQ
提取码:7hqi
复制这段内容后打开百度网盘手机App,操作更方便哦

第一章 QuickStart

1.1、文章介绍

预备知识:

  • Java8 编程基础(必须的)
  • MySQL 数据库(必须的)
  • JDBC 编程基础(必须的)
  • JavaWeb 编程基础(必须的)
  • Maven 基本操作(必须的)

开发环境:

  • Idea:2020.1.2
  • Java:1.8.0_261
  • Maven:3.3.9
  • MySQL:5.5.61
  • ActiveMQ:5.16.0
  • Spring:5.3.2

学习路线:

  • Spring IOC核心容器
  • Spring AOP面向切面编程
  • Spring Junit5单元测试
  • JdbcTemplate操作数据库
  • JmsTemplate操作消息服务
  • Transactions事务管理

1.2、框架介绍

什么是Spring?

Spring中文翻译为“春天”,他可以用来指代“SpringFramework”这个项目本身。随着时间的流逝,其他Spring项目已经在“SpringFramework”的基础上进行构建。所以,当我们说“Spring”的时候,实际上代表的是整个Spring项目系列。

什么是Framework?

Framework 中文翻译为“框架”,通常指的是为了实现某个业界标准或者完成某个特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。

为什么要用Spring框架?

Spring框架是一款是轻量级的、开源的 JavaEE 一站式框架,它是为了解决企业应用开发的复杂性而创建的。

从Spring Framework、Spring Boot、Spring Cloud三者的关系出发:你对Spring Cloud多了解源自于你对Spring Boot有多了解,你对Spring Boot多了解源自于你对Spring Framework有多了解。这就是为何我文章花大量笔墨在Spring Framework上而非Spring Boot上的根本原因,底层通透了,上层运用自如。

Spring框架总共大约有 20 个模块组成,以下是Spring5的模块结构图:

image-20201217110927836

官方网站:https://spring.io/

下载地址:https://repo.spring.io/release/org/springframework/spring/

第二章 IOC Container

2.1、IOC容器的概述

控制反转

我们知道在面向对象设计的软件系统中,它的底层都是由N个对象构成的,各个对象之间通过相互合作,最终实现系统地业务逻辑。

耦合的对象

如果我们打开机械式手表的后盖,就会看到与上面类似的情形,各个齿轮分别带动时针、分针和秒针顺时针旋转,从而在表盘上产生正确的时间。上图描述的就是这样的一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互啮合在一起,协同工作,共同完成某项任务。我们可以看到,在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。齿轮组中齿轮之间的啮合关系与软件系统中对象之间的耦合关系非常相似,对象间的耦合关系是无法避免的,也是必要的,这是协同工作的基础。

现在,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。

耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。

为了解决对象之间的耦合度过高的问题,1996年,软件专家Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC这个概念,用来实现对象之间的“解耦”。IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”。

IOC理论提出的观点大体是这样的:借助于“IOC容器”实现具有依赖关系的对象之间的解耦,如下图:

解耦后对象

由于引进了中间位置的“IOC容器”,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“IOC容器”了,全部对象的控制权全部上缴给“IOC容器”,所以,“IOC容器”成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系。

我们再来做个试验,把上图中间的“IOC容器”拿掉,然后再来看看这套系统:

理想化系统

我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现“IOC容器”,对于系统开发而言,这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!

我们再来看看,控制反转(IOC)到底为什么要起这么个名字?

软件系统在没有引入“IOC容器”之前,如图“耦合的对象”所示,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B,无论是创建还是使用对象B,控制权都在自己手上。

软件系统在引入“IOC容器”之后,这种情形就完全改变了,如图“解耦后对象”所示,由于“IOC容器”的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,“IOC容器”会主动创建一个对象B注入到对象A需要的地方。

通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。

依赖注入

2004年,Martin Fowler探讨了同一个问题,既然IOC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由“IOC容器”主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。

他的这个答案,实际上给出了实现IOC的方法:依赖注入,所谓依赖注入,就是由“IOC容器”在运行期间,动态地将某种依赖关系注入到对象之中。所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。

Spring IOC容器

Spring框架实现了“IOC容器”,org.springframework.context.ApplicationContext接口代表Spring IOC容器,并负责实例化、配置和组装Bean。Spring IOC容器通过读取配置信息获取有关要实例化、配置和组装哪些对象的指令,通过这些配置指令,它使您能够表达组成应用程序的对象以及这些对象之间的依赖关系。

Spring Bean对象

在Spring框架中,构成应用程序主干并由Spring IOC容器管理的对象称为Bean,它一般是由Spring IOC容器实例化、配置和组装的对象。

2.2、IOC容器的原理

image-20201220121343791

2.3、IOC容器的入门

2.3.1、环境的准备

项目名称: ioc-demo-01

导入依赖:

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

创建配置: 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"></beans>

2.3.2、容器的创建

第一种形式:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

第二种形式:

ApplicationContext applicationContext = new FileSystemXmlApplicationContext("C:/applicationContext.xml");

2.4、IOC容器的Bean管理(XML方式)

2.4.1、Bean的配置

第一种形式:

<bean class="com.caochenlei.spring5.bean.User"></bean>

第二种形式:

<bean id="user" class="com.caochenlei.spring5.bean.User"></bean>

第三种形式:

<bean name="user" class="com.caochenlei.spring5.bean.User"></bean>

2.4.2、Bean的获取

第一种形式:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext.getBean(User.class);
System.out.println(user);

第二种形式:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user);

第三种形式:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println(user);

2.4.3、Bean的别名

第一种形式:

<bean class="com.caochenlei.spring5.bean.User"></bean>
<alias name="com.caochenlei.spring5.bean.User" alias="systemUser"></alias>

第二种形式:

<bean id="user" class="com.caochenlei.spring5.bean.User"></bean>
<alias name="user" alias="systemUser"></alias>

第三种形式:

<bean name="user" class="com.caochenlei.spring5.bean.User"></bean>
<alias name="user" alias="systemUser"></alias>

2.4.4、Bean的依赖注入

2.4.4.1、基于 setter 方法的依赖注入

2.4.4.1.1、注入基本类型
public class User {
    private String uname;private Character ugender;private Integer uage;private Double umoney;private Boolean uflag;public void setUname(String uname) {
    this.uname = uname;}public void setUgender(Character ugender) {
    this.ugender = ugender;}public void setUage(Integer uage) {
    this.uage = uage;}public void setUmoney(Double umoney) {
    this.umoney = umoney;}public void setUflag(Boolean uflag) {
    this.uflag = uflag;}@Overridepublic String toString() {
    return "User{" +"uname='" + uname + '\'' +", ugender=" + ugender +", uage=" + uage +", umoney=" + umoney +", uflag=" + uflag +'}';}
}

第一种形式:

<bean id="user" class="com.caochenlei.spring5.bean.User"><property name="uname" value="张三"></property><property name="uage" value="28"></property><property name="ugender" value=""></property><property name="umoney" value="31.4"></property><property name="uflag" value="true"></property>
</bean>

第二种形式:

<bean id="user" class="com.caochenlei.spring5.bean.User"><property name="uname"><value>张三</value></property><property name="uage"><value>28</value></property><property name="ugender"><value></value></property><property name="umoney"><value>31.4</value></property><property name="uflag"><value>true</value></property>
</bean>
2.4.4.1.2、注入空值类型
public class NullBean {
    private String name;public void setName(String name) {
    this.name = name;}@Overridepublic String toString() {
    return "NullBean{" +"name='" + name + '\'' +'}';}
}

第一种形式:

<bean id="nullBean" class="com.caochenlei.spring5.bean.NullBean"><property name="name" value="#{null}"></property>
</bean>

第二种形式:

<bean id="nullBean" class="com.caochenlei.spring5.bean.NullBean"><property name="name"><value>#{null}</value></property>
</bean>

第三种形式:

<bean id="nullBean" class="com.caochenlei.spring5.bean.NullBean"><property name="name"><null/></property>
</bean>
2.4.4.1.3、注入特殊符号
public class SymbolBean {
    private String address;public void setAddress(String address) {
    this.address = address;}@Overridepublic String toString() {
    return "SymbolBean{" +"address='" + address + '\'' +'}';}
}

第一种形式:

<bean id="symbolBean" class="com.caochenlei.spring5.bean.SymbolBean"><property name="address" value="&lt;&lt;北京&gt;&gt;"></property>
</bean>

第二种形式:

<bean id="symbolBean" class="com.caochenlei.spring5.bean.SymbolBean"><property name="address"><value>&lt;&lt;北京&gt;&gt;</value></property>
</bean>

第三种形式:

<bean id="symbolBean" class="com.caochenlei.spring5.bean.SymbolBean"><property name="address"><value><![CDATA[<<南京>>]]></value></property>
</bean>
2.4.4.1.4、注入对象类型
public class Emp {
    private String ename;private String egender;private Dep edep;public void setEname(String ename) {
    this.ename = ename;}public void setEgender(String egender) {
    this.egender = egender;}public void setEdep(Dep edep) {
    this.edep = edep;}@Overridepublic String toString() {
    return "Emp{" +"ename='" + ename + '\'' +", egender='" + egender + '\'' +", edep=" + edep +'}';}
}
public class Dep {
    private String dname;public void setDname(String dname) {
    this.dname = dname;}@Overridepublic String toString() {
    return "Dep{" +"dname='" + dname + '\'' +'}';}
}

第一种形式:

<bean id="dep" class="com.caochenlei.spring5.bean.Dep"><property name="dname" value="开发部"></property>
</bean><bean id="emp1" class="com.caochenlei.spring5.bean.Emp"><property name="ename" value="张三"></property><property name="egender" value=""></property><property name="edep" ref="dep"></property>
</bean><bean id="emp2" class="com.caochenlei.spring5.bean.Emp"><property name="ename" value="李四"></property><property name="egender" value=""></property><property name="edep" ref="dep"></property>
</bean>

第二种形式:

<bean id="dep" class="com.caochenlei.spring5.bean.Dep"><property name="dname" value="开发部"></property>
</bean><bean id="emp1" class="com.caochenlei.spring5.bean.Emp"><property name="ename" value="张三"></property><property name="egender" value=""></property><property name="edep"><ref bean="dep"/></property>
</bean><bean id="emp2" class="com.caochenlei.spring5.bean.Emp"><property name="ename" value="李四"></property><property name="egender" value=""></property><property name="edep"><ref bean="dep"/></property>
</bean>

第三种形式:

<bean id="emp1" class="com.caochenlei.spring5.bean.Emp"><property name="ename" value="张三"></property><property name="egender" value=""></property><property name="edep"><bean class="com.caochenlei.spring5.bean.Dep"><property name="dname" value="开发部"></property></bean></property>
</bean><bean id="emp2" class="com.caochenlei.spring5.bean.Emp"><property name="ename" value="李四"></property><property name="egender" value=""></property><property name="edep"><bean class="com.caochenlei.spring5.bean.Dep"><property name="dname" value="开发部"></property></bean></property>
</bean>
2.4.4.1.5、注入集合类型
2.4.4.1.5.1、集合中数据为基本类型
public class CollectionBean {
    // 数组类型private String[] arrays;// 集合类型private List<String> lists;private Set<String> sets;private Map<String, String> maps;public void setArrays(String[] arrays) {
    this.arrays = arrays;}public void setLists(List<String> lists) {
    this.lists = lists;}public void setSets(Set<String> sets) {
    this.sets = sets;}public void setMaps(Map<String, String> maps) {
    this.maps = maps;}@Overridepublic String toString() {
    return "CollectionBean{" +"\narrays=" + Arrays.toString(arrays) +", \nlists=" + lists +", \nsets=" + sets +", \nmaps=" + maps +"\n}";}
}
<bean id="collectionBean" class="com.caochenlei.spring5.bean.CollectionBean"><!--数组类型--><property name="arrays"><array><value>张三</value><value>李四</value><value>王五</value></array></property><!--集合类型--><property name="lists"><list><value>赵六</value><value>小七</value><value>柳八</value></list></property><property name="sets"><set><value>大明</value><value>小明</value><value>丹尼</value></set></property><property name="maps"><map><entry key="西游记" value="吴承恩"/><entry key="水浒传" value="施耐庵"/><entry key="红楼梦" value="曹雪芹"/><entry key="三国演义" value="罗贯中"/></map></property>
</bean>
2.4.4.1.5.2、集合中数据为对象类型
public class CollectionObjectBean {
    //数组类型private Product[] products;//集合类型private List<Product> lists;private Set<Product> sets;private Map<String, Product> maps;public void setProducts(Product[] products) {
    this.products = products;}public void setLists(List<Product> lists) {
    this.lists = lists;}public void setSets(Set<Product> sets) {
    this.sets = sets;}public void setMaps(Map<String, Product> maps) {
    this.maps = maps;}@Overridepublic String toString() {
    return "CollectionObjectBean{" +"\nproducts=" + Arrays.toString(products) +", \nlists=" + lists +", \nsets=" + sets +", \nmaps=" + maps +"\n}";}
}
<bean id="product1" class="com.caochenlei.spring5.bean.Product"><property name="pname" value="手机"></property><property name="pcount" value="1000"></property>
</bean><bean id="product2" class="com.caochenlei.spring5.bean.Product"><property name="pname" value="电脑"></property><property name="pcount" value="2000"></property>
</bean><bean id="product3" class="com.caochenlei.spring5.bean.Product"><property name="pname" value="冰箱"></property><property name="pcount" value="3000"></property>
</bean><bean id="collectionObjectBean" class="com.caochenlei.spring5.bean.CollectionObjectBean"><!--数组类型--><property name="products"><array><ref bean="product1"/><ref bean="product2"/><ref bean="product3"/></array></property><!--集合类型--><property name="lists"><list><ref bean="product1"/><ref bean="product2"/><ref bean="product3"/></list></property><property name="sets"><set><ref bean="product1"/><ref bean="product2"/><ref bean="product3"/></set></property><property name="maps"><map><entry key="10010"><ref bean="product1"/></entry><entry key="10011"><ref bean="product2"/></entry><entry key="10012"><ref bean="product3"/></entry></map></property>
</bean>
2.4.4.1.6、注入级联属性
public class Person {
    private String pname;private Birthday pbirthday;public void setPname(String pname) {
    this.pname = pname;}public Birthday getPbirthday() {
    return pbirthday;}public void setPbirthday(Birthday pbirthday) {
    this.pbirthday = pbirthday;}@Overridepublic String toString() {
    return "Person{" +"pname='" + pname + '\'' +", pbirthday=" + pbirthday +'}';}
}
public class Birthday {
    private Integer year;private Integer month;private Integer day;public void setYear(Integer year) {
    this.year = year;}public void setMonth(Integer month) {
    this.month = month;}public void setDay(Integer day) {
    this.day = day;}@Overridepublic String toString() {
    return "Birthday{" +"year=" + year +", month=" + month +", day=" + day +'}';}
}
<bean id="person" class="com.caochenlei.spring5.bean.Person"><property name="pname" value="张三"></property><property name="pbirthday"><bean class="com.caochenlei.spring5.bean.Birthday"></bean></property><property name="pbirthday.year" value="1997"></property><property name="pbirthday.month" value="12"></property><property name="pbirthday.day" value="05"></property>
</bean>
2.4.4.1.7、注入属性信息
2.4.4.1.7.1、注入内部属性对象
public class PropBean {
    private Properties prop;public void setProp(Properties prop) {
    this.prop = prop;}@Overridepublic String toString() {
    return "PropBean{" +"prop=" + prop +'}';}
}
<bean id="propBean" class="com.caochenlei.spring5.bean.PropBean"><property name="prop"><props><prop key="username">admin</prop><prop key="password">admin</prop></props></property>
</bean>
2.4.4.1.7.2、注入外部属性文件
public class PropBean {
    private Properties prop;public void setProp(Properties prop) {
    this.prop = prop;}@Overridepublic String toString() {
    return "PropBean{" +"prop=" + prop +'}';}
}
username=root
password=root
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

第一种形式:

<!--引入外部属性文件-->
<context:property-placeholder location="classpath:admin.properties" local-override="true"/><bean id="propBean" class="com.caochenlei.spring5.bean.PropBean"><property name="prop"><props><prop key="username">${username}</prop><prop key="password">${password}</prop></props></property>
</bean>

第二种形式:

<!--引入外部属性文件-->
<context:property-placeholder location="classpath:admin.properties" system-properties-mode="NEVER"/><bean id="propBean" class="com.caochenlei.spring5.bean.PropBean"><property name="prop"><props><prop key="username">${username}</prop><prop key="password">${password}</prop></props></property>
</bean>

第三种形式:

admin.username=root
admin.password=root
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:admin.properties"/><bean id="propBean" class="com.caochenlei.spring5.bean.PropBean"><property name="prop"><props><prop key="username">${admin.username}</prop><prop key="password">${admin.password}</prop></props></property>
</bean>
2.4.4.1.7.3、注入系统属性信息
public class PropBean {
    private Properties prop;public void setProp(Properties prop) {
    this.prop = prop;}@Overridepublic String toString() {
    return "PropBean{" +"prop=" + prop +'}';}
}
<!--开启系统属性加载-->
<context:property-placeholder/><bean id="propBean" class="com.caochenlei.spring5.bean.PropBean"><property name="prop"><props><prop key="username">${os.name}</prop><prop key="password">${os.version}</prop></props></property>
</bean>
Map<Object, Object> map = new TreeMap<Object, Object>();
Set<String> keySet1 = System.getenv().keySet();
for (String key : keySet1) {
    map.put(key, System.getenv().get(key));
}
Set<Object> keySet2 = System.getProperties().keySet();
for (Object key : keySet2) {
    map.put(key, System.getProperties().get(key));
}
Set<Object> keySet = map.keySet();
for (Object key : keySet) {
    System.out.println(key + "=" + map.get(key));
}

2.4.4.2、基于有参构造方法的依赖注入

2.4.4.2.1、注入基本类型
public class User {
    private String uname;private Character ugender;private Integer uage;private Double umoney;private Boolean uflag;public User(String uname, Character ugender, Integer uage, Double umoney, Boolean uflag) {
    this.uname = uname;this.ugender = ugender;this.uage = uage;this.umoney = umoney;this.uflag = uflag;}@Overridepublic String toString() {
    return "User{" +"uname='" + uname + '\'' +", ugender=" + ugender +", uage=" + uage +", umoney=" + umoney +", uflag=" + uflag +'}';}
}

第一种形式:

<bean id="user" class="com.caochenlei.spring5.bean.User"><constructor-arg name="uname" value="张三"></constructor-arg><constructor-arg name="ugender" value=""></constructor-arg><constructor-arg name="uage" value="28"></constructor-arg><constructor-arg name="umoney" value="31.4"></constructor-arg><constructor-arg name="uflag" value="true"></constructor-arg>
</bean>

第二种形式:

<bean id="user" class="com.caochenlei.spring5.bean.User"><constructor-arg name="uname"><value>张三</value></constructor-arg><constructor-arg name="ugender"><value></value></constructor-arg><constructor-arg name="uage"><value>28</value></constructor-arg><constructor-arg name="umoney"><value>31.4</value></constructor-arg><constructor-arg name="uflag"><value>true</value></constructor-arg>
</bean>

第二种形式:

<bean id="user" class="com.caochenlei.spring5.bean.User"><constructor-arg index="0" value="张三"></constructor-arg><constructor-arg index="1" value=""></constructor-arg><constructor-arg index="2" value="28"></constructor-arg><constructor-arg index="3" value="31.4"></constructor-arg><constructor-arg index="4" value="true"></constructor-arg>
</bean>

第三种形式:

<bean id="user1" class="com.caochenlei.spring5.bean.User"><constructor-arg type="java.lang.String" value="张三"></constructor-arg><constructor-arg type="java.lang.Character" value=""></constructor-arg><constructor-arg type="java.lang.Integer" value="28"></constructor-arg><constructor-arg type="java.lang.Double" value="31.4"></constructor-arg><constructor-arg type="java.lang.Boolean" value="true"></constructor-arg>
</bean>
2.4.4.2.2、注入空值类型
public class NullBean {
    private String name;public NullBean(String name) {
    this.name = name;}@Overridepublic String toString() {
    return "NullBean{" +"name='" + name + '\'' +'}';}
}

第一种形式:

    <bean id="nullBean" class="com.caochenlei.spring5.bean.NullBean"><constructor-arg name="name" value="#{null}"></constructor-arg></bean>

第二种形式:

<bean id="nullBean" class="com.caochenlei.spring5.bean.NullBean"><constructor-arg name="name"><value>#{null}</value></constructor-arg>
</bean>

第三种形式:

<bean id="nullBean" class="com.caochenlei.spring5.bean.NullBean"><constructor-arg name="name"><null/></constructor-arg>
</bean>
2.4.4.2.3、注入特殊符号
public class SymbolBean {
    private String address;public SymbolBean(String address) {
    this.address = address;}@Overridepublic String toString() {
    return "SymbolBean{" +"address='" + address + '\'' +'}';}
}

第一种形式:

<bean id="symbolBean" class="com.caochenlei.spring5.bean.SymbolBean"><constructor-arg name="address" value="&lt;&lt;北京&gt;&gt;"></constructor-arg>
</bean>

第二种形式:

<bean id="symbolBean" class="com.caochenlei.spring5.bean.SymbolBean"><constructor-arg name="address"><value>&lt;&lt;北京&gt;&gt;</value></constructor-arg>
</bean>

第三种形式:

<bean id="symbolBean" class="com.caochenlei.spring5.bean.SymbolBean"><constructor-arg name="address"><value><![CDATA[北京]]></value></constructor-arg>
</bean>
2.4.4.2.4、注入对象类型
public class Emp {
    private String ename;private String egender;private Dep edep;public Emp(String ename, String egender, Dep edep) {
    this.ename = ename;this.egender = egender;this.edep = edep;}@Overridepublic String toString() {
    return "Emp{" +"ename='" + ename + '\'' +", egender='" + egender + '\'' +", edep=" + edep +'}';}
}
public class Dep {
    private String dname;public void setDname(String dname) {
    this.dname = dname;}@Overridepublic String toString() {
    return "Dep{" +"dname='" + dname + '\'' +'}';}
}

第一种形式:

<bean id="dep" class="com.caochenlei.spring5.bean.Dep"><property name="dname" value="设计部"></property>
</bean><bean id="emp1" class="com.caochenlei.spring5.bean.Emp"><constructor-arg name="ename" value="张三"></constructor-arg><constructor-arg type="java.lang.String" value=""></constructor-arg><constructor-arg index="2" ref="dep"></constructor-arg>
</bean><bean id="emp2" class="com.caochenlei.spring5.bean.Emp"><constructor-arg name="ename" value="李四"></constructor-arg><constructor-arg type="java.lang.String" value=""></constructor-arg><constructor-arg index="2" ref="dep"></constructor-arg>
</bean>

第二种形式:

<bean id="dep" class="com.caochenlei.spring5.bean.Dep"><property name="dname" value="设计部"></property>
</bean><bean id="emp1" class="com.caochenlei.spring5.bean.Emp"><constructor-arg name="ename" value="张三"></constructor-arg><constructor-arg type="java.lang.String" value=""></constructor-arg><constructor-arg index="2"><ref bean="dep"/></constructor-arg>
</bean><bean id="emp2" class="com.caochenlei.spring5.bean.Emp"><constructor-arg name="ename" value="李四"></constructor-arg><constructor-arg type="java.lang.String" value=""></constructor-arg><constructor-arg index="2"><ref bean="dep"/></constructor-arg>
</bean>

第三种形式:

<bean id="emp1" class="com.caochenlei.spring5.bean.Emp"><constructor-arg name="ename" value="张三"></constructor-arg><constructor-arg type="java.lang.String" value=""></constructor-arg><constructor-arg index="2"><bean class="com.caochenlei.spring5.bean.Dep"><property name="dname" value="设计部"></property></bean></constructor-arg>
</bean><bean id="emp2" class="com.caochenlei.spring5.bean.Emp"><constructor-arg name="ename" value="李四"></constructor-arg><constructor-arg type="java.lang.String" value=""></constructor-arg><constructor-arg index="2"><bean class="com.caochenlei.spring5.bean.Dep"><property name="dname" value="设计部"></property></bean></constructor-arg>
</bean>
2.4.4.2.5、注入集合类型
2.4.4.2.5.1、集合中数据为基本类型
public class CollectionBean {
    // 数组类型private String[] arrays;// 集合类型private List<String> lists;private Set<String> sets;private Map<String, String> maps;public CollectionBean(String[] arrays, List<String> lists, Set<String> sets, Map<String, String> maps) {
    this.arrays = arrays;this.lists = lists;this.sets = sets;this.maps = maps;}@Overridepublic String toString() {
    return "CollectionBean{" +"\narrays=" + Arrays.toString(arrays) +", \nlists=" + lists +", \nsets=" + sets +", \nmaps=" + maps +"\n}";}
}
<bean id="collectionBean" class="com.caochenlei.spring5.bean.CollectionBean"><constructor-arg name="arrays"><array><value>张三</value><value>李四</value><value>王五</value></array></constructor-arg><constructor-arg type="java.util.List"><list><value>赵六</value><value>小七</value><value>柳八</value></list></constructor-arg><constructor-arg index="2"><set><value>大明</value><value>小明</value><value>丹尼</value></set></constructor-arg><constructor-arg name="maps"><map><entry key="西游记" value="吴承恩"/><entry key="水浒传" value="施耐庵"/><entry key="红楼梦" value="曹雪芹"/><entry key="三国演义" value="罗贯中"/></map></constructor-arg>
</bean>
2.4.4.2.5.2、集合中数据为对象类型
public class Product {
    private String pname;private Integer pcount;public void setPname(String pname) {
    this.pname = pname;}public void setPcount(Integer pcount) {
    this.pcount = pcount;}@Overridepublic String toString() {
    return "Product{" +"pname='" + pname + '\'' +", pcount=" + pcount +'}';}
}
public class CollectionObjectBean {
    //数组类型private Product[] products;//集合类型private List<Product> lists;private Set<Product> sets;private Map<String, Product> maps;public CollectionObjectBean(Product[] products, List<Product> lists, Set<Product> sets, Map<String, Product> maps) {
    this.products = products;this.lists = lists;this.sets = sets;this.maps = maps;}@Overridepublic String toString() {
    return "CollectionObjectBean{" +"\nproducts=" + Arrays.toString(products) +", \nlists=" + lists +", \nsets=" + sets +", \nmaps=" + maps +"\n}";}
}
<bean id="product1" class="com.caochenlei.spring5.bean.Product"><property name="pname" value="手机"></property><property name="pcount" value="1000"></property>
</bean><bean id="product2" class="com.caochenlei.spring5.bean.Product"><property name="pname" value="电脑"></property><property name="pcount" value="2000"></property>
</bean><bean id="product3" class="com.caochenlei.spring5.bean.Product"><property name="pname" value="冰箱"></property><property name="pcount" value="3000"></property>
</bean><bean id="collectionObjectBean" class="com.caochenlei.spring5.bean.CollectionObjectBean"><constructor-arg name="products"><array><ref bean="product1"/><ref bean="product2"/><ref bean="product3"/></array></constructor-arg><constructor-arg type="java.util.List"><list><ref bean="product1"/><ref bean="product2"/><ref bean="product3"/></list></constructor-arg><constructor-arg index="2"><set><ref bean="product1"/><ref bean="product2"/><ref bean="product3"/></set></constructor-arg><constructor-arg name="maps"><map><entry key="10010"><ref bean="product1"/></entry><entry key="10011"><ref bean="product2"/></entry><entry key="10012"><ref bean="product3"/></entry></map></constructor-arg>
</bean>

2.4.4.3、基于p名称空间的依赖注入

xmlns:p="http://www.springframework.org/schema/p"
2.4.4.3.1、注入基本类型
public class User {
    private String uname;private Character ugender;private Integer uage;private Double umoney;private Boolean uflag;public void setUname(String uname) {
    this.uname = uname;}public void setUgender(Character ugender) {
    this.ugender = ugender;}public void setUage(Integer uage) {
    this.uage = uage;}public void setUmoney(Double umoney) {
    this.umoney = umoney;}public void setUflag(Boolean uflag) {
    this.uflag = uflag;}@Overridepublic String toString() {
    return "User{" +"uname='" + uname + '\'' +", ugender=" + ugender +", uage=" + uage +", umoney=" + umoney +", uflag=" + uflag +'}';}
}
<bean id="user" class="com.caochenlei.spring5.bean.User"p:uname="张三"p:ugender=""p:uage="28"p:umoney="31.4"p:uflag="true" />
2.4.4.3.2、注入对象类型
public class Emp {
    private String ename;private String egender;private Dep edep;public void setEname(String ename) {
    this.ename = ename;}public void setEgender(String egender) {
    this.egender = egender;}public void setEdep(Dep edep) {
    this.edep = edep;}@Overridepublic String toString() {
    return "Emp{" +"ename='" + ename + '\'' +", egender='" + egender + '\'' +", edep=" + edep +'}';}
}
public class Dep {
    private String dname;public void setDname(String dname) {
    this.dname = dname;}@Overridepublic String toString() {
    return "Dep{" +"dname='" + dname + '\'' +'}';}
}
<bean id="dep" class="com.caochenlei.spring5.bean.Dep" p:dname="运维部"/><bean id="emp1" class="com.caochenlei.spring5.bean.Emp"p:ename="张三"p:egender=""p:edep-ref="dep" /><bean id="emp2" class="com.caochenlei.spring5.bean.Emp"p:ename="李四"p:egender=""p:edep-ref="dep" />

2.4.4.4、基于c名称空间的依赖注入

xmlns:c="http://www.springframework.org/schema/c"
2.4.4.4.1、注入基本类型
public class User {
    private String uname;private Character ugender;private Integer uage;private Double umoney;private Boolean uflag;public User(String uname, Character ugender, Integer uage, Double umoney, Boolean uflag) {
    this.uname = uname;this.ugender = ugender;this.uage = uage;this.umoney = umoney;this.uflag = uflag;}@Overridepublic String toString() {
    return "User{" +"uname='" + uname + '\'' +", ugender=" + ugender +", uage=" + uage +", umoney=" + umoney +", uflag=" + uflag +'}';}
}

第一种形式:

<bean id="user" class="com.caochenlei.spring5.bean.User"c:uname="张三"c:ugender=""c:uage="28"c:umoney="31.4"c:uflag="true" />

第二种形式:

<bean id="user" class="com.caochenlei.spring5.bean.User"c:_0="张三"c:_1=""c:_2="28"c:_3="31.4"c:_4="true" />
2.4.4.4.2、注入对象类型
public class Emp {
    private String ename;private String egender;private Dep edep;public Emp(String ename, String egender, Dep edep) {
    this.ename = ename;this.egender = egender;this.edep = edep;}@Overridepublic String toString() {
    return "Emp{" +"ename='" + ename + '\'' +", egender='" + egender + '\'' +", edep=" + edep +'}';}
}
public class Dep {
    private String dname;public Dep(String dname) {
    this.dname = dname;}@Overridepublic String toString() {
    return "Dep{" +"dname='" + dname + '\'' +'}';}
}

第一种形式:

<bean id="dep" class="com.caochenlei.spring5.bean.Dep"c:dname="保安部" /><bean id="emp1" class="com.caochenlei.spring5.bean.Emp"c:ename="张三"c:egender=""c:edep-ref="dep" /><bean id="emp2" class="com.caochenlei.spring5.bean.Emp"c:ename="李四"c:egender=""c:edep-ref="dep" />

第二种形式:

<bean id="dep" class="com.caochenlei.spring5.bean.Dep"c:_0="保安部" /><bean id="emp1" class="com.caochenlei.spring5.bean.Emp"c:_0="张三"c:_1=""c:_2-ref="dep" /><bean id="emp2" class="com.caochenlei.spring5.bean.Emp"c:_0="李四"c:_1=""c:_2-ref="dep" />

2.4.5、Bean的自动注入

自动注入模式:autowire

模式 说明
no (默认)不使用自动注入
byName 根据bean的名称自动注入
byType 根据bean的类型自动注入

您还可以通过使用元素default-autowire上的属性在容器级别上控制自动注入类型,如以下示例所示:

<beans default-lazy-init="true"><!-- all beans will be autowire by name or type -->
</beans>

2.4.5.1、根据Bean的名称自动注入

public class Emp {
    private String ename;private String egender;private Dep edep;public void setEname(String ename) {
    this.ename = ename;}public void setEgender(String egender) {
    this.egender = egender;}public void setEdep(Dep edep) {
    this.edep = edep;}@Overridepublic String toString() {
    return "Emp{" +"ename='" + ename + '\'' +", egender='" + egender + '\'' +", edep=" + edep +'}';}
}
public class Dep {
    private String dname;public void setDname(String dname) {
    this.dname = dname;}@Overridepublic String toString() {
    return "Dep{" +"dname='" + dname + '\'' +'}';}
}
<bean id="edep" class="com.caochenlei.spring5.bean.Dep"><property name="dname" value="公关部"></property>
</bean><bean id="emp" class="com.caochenlei.spring5.bean.Emp" autowire="byName"><!--<property name="edep" ref="edep"></property>-->
</bean>

2.4.5.2、根据Bean的类型自动注入

public class Emp {
    private String ename;private String egender;private Dep edep;public void setEname(String ename) {
    this.ename = ename;}public void setEgender(String egender) {
    this.egender = egender;}public void setEdep(Dep edep) {
    this.edep = edep;}@Overridepublic String toString() {
    return "Emp{" +"ename='" + ename + '\'' +", egender='" + egender + '\'' +", edep=" + edep +'}';}
}
public class Dep {
    private String dname;public void setDname(String dname) {
    this.dname = dname;}@Overridepublic String toString() {
    return "Dep{" +"dname='" + dname + '\'' +'}';}
}
<bean id="edep" class="com.caochenlei.spring5.bean.Dep"><property name="dname" value="公关部"></property>
</bean><bean id="emp" class="com.caochenlei.spring5.bean.Emp" autowire="byType"><!--<property name="edep" ref="edep"></property>-->
</bean>

2.4.6、Bean的实例化

2.4.6.1、使用无参构造函数实例化

public class User {
    private String uname;private String ugender;public User() {
    System.out.println("使用无参构造函数实例化 ...");}
}
<bean id="user" class="com.caochenlei.spring5.bean.User"></bean>

2.4.6.2、使用静态工厂方法实例化

public class User {
    private String uname;private String ugender;private static User user = new User();public static User createInstance() {
    System.out.println("使用静态工厂方法实例化 ...");return user;}
}
<bean id="user" class="com.caochenlei.spring5.bean.User" factory-method="createInstance"></bean>

2.4.6.3、使用实例工厂方法实例化

public class User {
    private String uname;private String ugender;
}
public class UserFactory {
    private static User user = new User();public User createInstance() {
    System.out.println("使用实例工厂方法实例化 ...");return user;}
}
<bean id="userFactory" class="com.caochenlei.spring5.bean.UserFactory"></bean>
<bean id="user" factory-bean="userFactory" factory-method="createInstance"></bean>

2.4.7、Bean的前置实例化

如果一个bean是另一个bean的依赖项,则通常意味着将一个bean设置为另一个bean的属性。但是,有时bean之间的依赖性不太直接。例如:打开门、打开灯,在打开灯之前一定要先打开门。使用depends-on属性可以在初始化使用此元素的bean之前显式强制初始化一个或多个bean。

以下示例使用该depends-on属性表示对单个bean的依赖关系:

public class OpenLamp {
    public OpenLamp() {
    System.out.println("打开灯 ...");}
}
public class OpenDoor {
    public OpenDoor() {
    System.out.println("打开门 ...");}
}
<bean id="openLamp" class="com.caochenlei.spring5.bean.OpenLamp" depends-on="openDoor"></bean>
<bean id="openDoor" class="com.caochenlei.spring5.bean.OpenDoor"></bean>

要表达对多个bean的依赖性,请提供一个bean名称列表作为depends-on属性值(逗号,空格和分号是有效的分隔符):

public class OpenLamp {
    public OpenLamp() {
    System.out.println("打开灯 ...");}
}
<bean id="openLamp" class="com.caochenlei.spring5.bean.OpenLamp" depends-on="openLock;openDoor"></bean>
<bean id="openDoor" class="com.caochenlei.spring5.bean.OpenDoor"></bean>
<bean id="openLock" class="com.caochenlei.spring5.bean.OpenLock"></bean>

在销毁bean对象的时候,会首先销毁自身bean对象,然后,会反向依次销毁依赖的bean对象。这样,depends-on还可以控制关机顺序。

  • 执行的时候:先开锁、再开门、打开灯
  • 销毁的时候:先关灯、再关门、锁上门

2.4.8、Bean的延迟实例化

public class TheLazyBean {
    public TheLazyBean() {
    System.out.println("TheLazyBean init ...");}
}
<bean id="theLazyBean" class="com.caochenlei.spring5.bean.TheLazyBean" lazy-init="true"></bean>

您还可以通过使用元素default-lazy-init上的属性在容器级别上控制是否延迟初始化,如以下示例所示:

<beans default-lazy-init="true"><!-- no beans will be pre-instantiated... -->
</beans>

2.4.9、Bean的作用域

Bean的作用域:

范围 说明
singleton 单例模式(默认)
prototype 多例模式
request 将单个bean定义的作用域限定为HTTP的生命周期Request
session 将单个bean定义的作用域限定为HTTP的生命周期Session
application 将单个bean定义的作用域限定为HTTP的生命周期ServletContext
websocket 将单个bean定义的作用域限定为HTTP的生命周期WebSocket

2.4.9.1、单例模式

public class User {
    private String uname;private Character ugender;private Integer uage;private Double umoney;private Boolean uflag;
}
<bean id="user" class="com.caochenlei.spring5.bean.User" scope="singleton"></bean>
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user1 = applicationContext.getBean("user", User.class);
User user2 = applicationContext.getBean("user", User.class);
System.out.println(user1);
System.out.println(user2);

2.4.9.2、多例模式

public class User {
    private String uname;private Character ugender;private Integer uage;private Double umoney;private Boolean uflag;
}
<bean id="user" class="com.caochenlei.spring5.bean.User" scope="prototype"></bean>
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user1 = applicationContext.getBean("user", User.class);
User user2 = applicationContext.getBean("user", User.class);
System.out.println(user1);
System.out.println(user2);

2.4.10、Bean的生命周期

2.4.10.1、默认的生命周期

public class User {
    private String uname;public User() {
    System.out.println("User 执行无参构造 ...");}public void setUname(String uname) {
    this.uname = uname;System.out.println("User 执行设置方法 ...");}public void initMethod() {
    System.out.println("User 执行初始方法 ...");}public void destroyMethod() {
    System.out.println("User 执行销毁方法 ...");}
}
<bean id="user" class="com.caochenlei.spring5.bean.User" init-method="initMethod" destroy-method="destroyMethod"><property name="uname" value="张三"></property>
</bean>

  • User 执行无参构造 …
  • User 执行设置方法 …
  • User 执行初始方法 …
  • com.caochenlei.spring5.bean.User@71423665
  • User 执行销毁方法 …

2.4.10.2、特殊的生命周期

public class User{
    private String uname;public User() {
    System.out.println("User 执行无参构造 ...");}public void setUname(String uname) {
    this.uname = uname;System.out.println("User 执行设置方法 ...");}public void initMethod() {
    System.out.println("User 执行初始方法 ...");}public void destroyMethod() {
    System.out.println("User 执行销毁方法 ...");}
}
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
    System.out.println("在初始化之前执行的方法 ...");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {
    System.out.println("在初始化之后执行的方法 ...");return bean;}
}
<bean id="user" class="com.caochenlei.spring5.bean.User" init-method="initMethod" destroy-method="destroyMethod"><property name="uname" value="张三"></property>
</bean>
<!--配置后置处理器-->
<bean id="myBeanPostProcessor" class="com.caochenlei.spring5.bean.MyBeanPostProcessor"></bean>

  • User 执行无参构造 …
  • User 执行设置方法 …
  • 在初始化之前执行的方法 …
  • User 执行初始方法 …
  • 在初始化之后执行的方法 …
  • com.caochenlei.spring5.bean.User@3632be31
  • User 执行销毁方法 …

2.5、IOC容器的Bean管理(注解方式)

2.5.1、半注解开发

项目名称: ioc-demo-02

导入依赖:

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

命名空间:

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

2.5.1.1、Bean的配置

声明一个组件:

//默认不配置名称,代表的是当前类名的首字母小写
@Component("user")
//@Repository
//@Service
//@Controller
public class User {
    private String uname;private String ugender;public String getUname() {
    return uname;}public void setUname(String uname) {
    this.uname = uname;}public String getUgender() {
    return ugender;}public void setUgender(String ugender) {
    this.ugender = ugender;}@Overridepublic String toString() {
    return "User{" +"uname='" + uname + '\'' +", ugender='" + ugender + '\'' +'}';}
}

扫描所有组件:

<!--开启组件扫描-->
<context:component-scan base-package="com.caochenlei"/>

组件扫描配置:

<!--开启组件扫描-->
<context:component-scan base-package="com.caochenlei" use-default-filters="false"><!--只扫描《注解类型》,注解类型为:@Component--><context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/><!--不扫描《注解类型》,注解类型为:@Repository、@Service、@Controller--><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

2.5.1.2、Bean的依赖注入

2.5.1.2.1、根据类型自动注入
@Component
public class User {
    private Integer uid;private String uname;private String ugender;public User() {
    }public User(Integer uid, String uname, String ugender) {
    this.uid = uid;this.uname = uname;this.ugender = ugender;}public Integer getUid() {
    return uid;}public void setUid(Integer uid) {
    this.uid = uid;}public String getUname() {
    return uname;}public void setUname(String uname) {
    this.uname = uname;}public String getUgender() {
    return ugender;}public void setUgender(String ugender) {
    this.ugender = ugender;}@Overridepublic String toString() {
    return "User{" +"uid=" + uid +", uname='" + uname + '\'' +", ugender='" + ugender + '\'' +'}';}
}
@Repository
public class UserDao {
    public User findOne(Integer id) {
    return new User(10086, "张三", "男");}
}

第一种形式: 将依赖注入到私有属性中

@Service
public class UserService {
    @Autowired//@Resourceprivate UserDao userDao;public User findOne(Integer id) {
    return userDao.findOne(id);}
}

第二种形式: 将依赖注入到setter方法中

@Service
public class UserService {
    private UserDao userDao;@Autowired//@Resourcepublic void setUserDao(UserDao userDao) {
    this.userDao = userDao;}public User findOne(Integer id) {
    return userDao.findOne(id);}
}

第三种形式: 将依赖注入到有参构造方法中

@Service
public class UserService {
    private UserDao userDao;@Autowired//@Resourcepublic UserService(UserDao userDao) {
    this.userDao = userDao;}public User findOne(Integer id) {
    return userDao.findOne(id);}
}
2.5.1.2.2、根据名称自动注入
@Component
public class User {
    private Integer uid;private String uname;private String ugender;public User() {
    }public User(Integer uid, String uname, String ugender) {
    this.uid = uid;this.uname = uname;this.ugender = ugender;}public Integer getUid() {
    return uid;}public void setUid(Integer uid) {
    this.uid = uid;}public String getUname() {
    return uname;}public void setUname(String uname) {
    this.uname = uname;}public String getUgender() {
    return ugender;}public void setUgender(String ugender) {
    this.ugender = ugender;}@Overridepublic String toString() {
    return "User{" +"uid=" + uid +", uname='" + uname + '\'' +", ugender='" + ugender + '\'' +'}';}
}
@Repository("abc")
public class UserDao {
    public User findOne(Integer id) {
    return new User(10086, "张三", "男");}
}

第一种形式: 将依赖注入到私有属性中

@Service
public class UserService {
    @Autowired@Qualifier("abc")//@Resource(name = "abc")private UserDao userDao;public User findOne(Integer id) {
    return userDao.findOne(id);}
}

第二种形式: 将依赖注入到setter方法中

@Service
public class UserService {
    private UserDao userDao;@Autowired@Qualifier("abc")//@Resource(name = "abc")public void setUserDao(UserDao userDao) {
    this.userDao = userDao;}public User findOne(Integer id) {
    return userDao.findOne(id);}
}

第三种形式: 将依赖注入到有参构造方法中

@Service
public class UserService {
    private UserDao userDao;@Autowired //@Resource(name = "abc")不支持public UserService(@Qualifier("abc") UserDao userDao) {
    this.userDao = userDao;}public User findOne(Integer id) {
    return userDao.findOne(id);}
}
2.5.1.2.3、注入基本类型属性
@Component
public class User {
    private Integer uid;private String uname;private String ugender;public User() {
    }public User(Integer uid, String uname, String ugender) {
    this.uid = uid;this.uname = uname;this.ugender = ugender;}public Integer getUid() {
    return uid;}public void setUid(Integer uid) {
    this.uid = uid;}public String getUname() {
    return uname;}public void setUname(String uname) {
    this.uname = uname;}public String getUgender() {
    return ugender;}public void setUgender(String ugender) {
    this.ugender = ugender;}@Overridepublic String toString() {
    return "User{" +"uid=" + uid +", uname='" + uname + '\'' +", ugender='" + ugender + '\'' +'}';}
}
@Repository
public class UserDao {
    public User findOne(Integer id) {
    return new User(10086, "张三", "男");}
}

第一种形式: 将依赖注入到私有属性中

@Service
public class UserService {
    @Autowiredprivate UserDao userDao;@Value("UserService")private String serviceName;public User findOne(Integer id) {
    System.out.println(serviceName);return userDao.findOne(id);}
}

第二种形式: 将依赖注入到setter方法中

@Service
public class UserService {
    @Autowiredprivate UserDao userDao;private String serviceName;@Value("UserService")public void setServiceName(String serviceName) {
    this.serviceName = serviceName;}public User findOne(Integer id) {
    System.out.println(serviceName);return userDao.findOne(id);}
}

第三种形式: 将依赖注入到有参构造方法中

@Service
public class UserService {
    @Autowiredprivate UserDao userDao;private String serviceName;public UserService(@Value("UserService") String serviceName) {
    this.serviceName = serviceName;}public User findOne(Integer id) {
    System.out.println(serviceName);return userDao.findOne(id);}
}
2.5.1.2.4、注入外部属性文件
<!--开启组件扫描-->
<context:component-scan base-package="com.caochenlei"/><!--引入外部文件-->
<context:property-placeholder location="classpath:admin.properties"/>
@Service
public class UserService {
    @Autowiredprivate UserDao userDao;public User findOne(Integer id) {
    return userDao.findOne(id);}
}

第一种形式: 将依赖注入到私有属性中

@Repository
public class UserDao {
    @Value("${admin.username}")private String username;@Value("${admin.password}")private String password;public User findOne(Integer id) {
    System.out.println("admin username:" + username);System.out.println("admin password:" + password);return new User(10086, "张三", "男");}
}

第二种形式: 将依赖注入到setter方法中

@Repository
public class UserDao {
    private String username;private String password;@Value("${admin.username}")public void setUsername(String username) {
    this.username = username;}@Value("${admin.password}")public void setPassword(String password) {
    this.password = password;}public User findOne(Integer id) {
    System.out.println("admin username:" + username);System.out.println("admin password:" + password);return new User(10086, "张三", "男");}
}

第三种形式: 将依赖注入到有参构造方法中

@Repository
public class UserDao {
    private String username;private String password;public UserDao(@Value("${admin.username}") String username, @Value("${admin.password}") String password) {
    this.username = username;this.password = password;}public User findOne(Integer id) {
    System.out.println("admin username:" + username);System.out.println("admin password:" + password);return new User(10086, "张三", "男");}
}

2.5.1.3、Bean的作用域

2.5.1.3.1、单例模式
@Component
@Scope("singleton")
public class Person {
    }
2.5.1.3.2、多例模式
@Component
@Scope("prototype")
public class Person {
    }

2.5.2、全注解开发

项目名称: ioc-demo-03

导入依赖:

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

2.5.2.1、扫描式注册

声明一个组件:

@Component
public class User {
    }

扫描所有组件:

@Configuration
@ComponentScan("com.caochenlei")
public class AppConfig {
    }

组件扫描配置:

@Configuration
@ComponentScan(basePackages = "com.caochenlei",/*只扫描《注解类型》,注解类型为:@Component*/includeFilters = {
    @ComponentScan.Filter(classes = Component.class)},/*不扫描《注解类型》,注解类型为:@Repository、@Service、@Controller*/excludeFilters = {
    @ComponentScan.Filter(classes = Repository.class),@ComponentScan.Filter(classes = Service.class),@ComponentScan.Filter(classes = Controller.class)}
)
public class AppConfig {
    }

第三种创建IOC容器的方法:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
User user = applicationContext.getBean("user", User.class);
System.out.println(user);

2.5.2.2、配置式注册

@Bean注释被用于配置一个方法实例,并初始化到Spring IOC容器中进行管理。@Bean常与@Configuration一起配合使用。

对于那些熟悉Spring的<beans/>XML配置的人来说,@Bean注释的作用与<bean/>元素相同。

最简单的@Configuration类如下:

@Configuration
public class AppConfig {
    @Beanpublic User user() {
    return new User();}
}

上一AppConfig类等效于以下Spring <beans/>XML:

<beans><bean id="user" class="com.caochenlei.spring5.bean.User"/>
</beans>

我们需要注意的是 proxyBeanMethods 属性:

  • @Configuration(proxyBeanMethods = true) :代表Full模式,外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
  • @Configuration(proxyBeanMethods = false) :代表Lite模式,外部无论对配置类中的这个组件注册方法调用多少次获取的都是创建一个新的实例对象

2.5.2.3、环境的切换

public class DataSource {
    private String driverClass;private String url;private String username;private String password;public void setDriverClass(String driverClass) {
    this.driverClass = driverClass;}public void setUrl(String url) {
    this.url = url;}public void setUsername(String username) {
    this.username = username;}public void setPassword(String password) {
    this.password = password;}@Overridepublic String toString() {
    return "DataSource{" +"driverClass='" + driverClass + '\'' +", url='" + url + '\'' +", username='" + username + '\'' +", password='" + password + '\'' +'}';}
}
@Configuration
@ComponentScan("com.caochenlei")
public class AppConfig {
    @Bean("dataSource")@Profile({
    "development", "default"})public DataSource dataSourceDevelopment() {
    DataSource dataSource = new DataSource();dataSource.setDriverClass("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/userDB");dataSource.setUsername("root");dataSource.setPassword("123456");return dataSource;}@Bean("dataSource")@Profile("production")public DataSource dataSourceProduction() {
    DataSource dataSource = new DataSource();dataSource.setDriverClass("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://caochenlei.com:3306/userDB");dataSource.setUsername("root");dataSource.setPassword("456789");return dataSource;}
}

第一种切换的形式:

-Dspring.profiles.active="production"

第二种切换的形式:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//设置激活环境
applicationContext.getEnvironment().setActiveProfiles("production");
//注册配置对象
applicationContext.register(AppConfig.class);
//启动刷新容器
applicationContext.refresh();DataSource dataSource = applicationContext.getBean("dataSource", DataSource.class);
System.out.println(dataSource);

注意:@Profile 可以写在类上,代表只有指定环境中的所有配置才能生效。没有标注 @Profile 的bean,默认在任何环境都是加载的。

2.5.2.4、属性配置导入

#开发环境数据库配置
jdbc.dev.driverClass=com.mysql.jdbc.Driver
jdbc.dev.url=jdbc:mysql://localhost:3306/userDB
jdbc.dev.username=root
jdbc.dev.password=123456#生产环境数据库配置
jdbc.pro.driverClass=com.mysql.jdbc.Driver
jdbc.pro.url=jdbc:mysql://caochenlei.com:3306/userDB
jdbc.pro.username=root
jdbc.pro.password=456789
@Configuration
@ComponentScan("com.caochenlei")
@PropertySource("classpath:jdbc.properties")
public class AppConfig {
    // 第一种形式:通过env获取@AutowiredEnvironment env;@Bean("dataSource")@Profile({
    "development", "default"})public DataSource dataSourceDevelopment() {
    DataSource dataSource = new DataSource();dataSource.setDriverClass(env.getProperty("jdbc.dev.driverClass"));dataSource.setUrl(env.getProperty("jdbc.dev.url"));dataSource.setUsername(env.getProperty("jdbc.dev.username"));dataSource.setPassword(env.getProperty("jdbc.dev.password"));return dataSource;}// 第二种形式:通过@Value注入@Value("${jdbc.pro.driverClass}")private String driverClass;@Value("${jdbc.pro.url}")private String url;@Value("${jdbc.pro.username}")private String username;@Value("${jdbc.pro.password}")private String password;@Bean("dataSource")@Profile("production")public DataSource dataSourceProduction() {
    DataSource dataSource = new DataSource();dataSource.setDriverClass(driverClass);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}
}
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//设置激活环境
applicationContext.getEnvironment().setActiveProfiles("production");
//注册配置对象
applicationContext.register(AppConfig.class);
//启动刷新容器
applicationContext.refresh();DataSource dataSource = applicationContext.getBean("dataSource", DataSource.class);
System.out.println(dataSource);

2.5.2.5、普通对象导入

public class Person {
    private String pname;private String pgender;public void setPname(String pname) {
    this.pname = pname;}public void setPgender(String pgender) {
    this.pgender = pgender;}@Overridepublic String toString() {
    return "Person{" +"pname='" + pname + '\'' +", pgender='" + pgender + '\'' +'}';}
}
@Configuration
@ComponentScan("com.caochenlei")
@PropertySource("classpath:jdbc.properties")
// 给容器中自动创建出这个类型的组件、默认组件的名字就是全类名
@Import(Person.class)
public class AppConfig {
    ...
}
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = applicationContext.getBean("com.caochenlei.spring5.bean.Person", Person.class);
System.out.println(person);

2.5.2.6、原生配置导入

public class Dog {
    private String dname;private String dcolor;public void setDname(String dname) {
    this.dname = dname;}public void setDcolor(String dcolor) {
    this.dcolor = dcolor;}@Overridepublic String toString() {
    return "Dog{" +"dname='" + dname + '\'' +", dcolor='" + dcolor + '\'' +'}';}
}

src\main\resources\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"><bean class="com.caochenlei.spring5.bean.Dog"></bean>
</beans>
@Configuration
@ComponentScan("com.caochenlei")
@PropertySource("classpath:jdbc.properties")
@Import(Person.class)
@ImportResource("classpath:applicationContext.xml")
public class AppConfig {
    ...
}
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Dog dog = applicationContext.getBean("com.caochenlei.spring5.bean.Dog", Dog.class);
System.out.println(dog);

第三章 AOP

3.1、AOP的概述

AOP是Aspect Oriented Programming的缩写,意思是:面向切面编程,它是通过预编译方式和运行期间动态代理方式实现程序功能统一维护的一种技术。简单的来说,就是通过不修改源码的情况下,为系统添加新的功能。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,加快了开发的效率。

image-20201221125710180

3.2、AOP的原理

3.2.1、代理概述

代理(Proxy)是一种设计模式,它的主要作用就是在原有对象的基础上,不修改源码,增加额外的功能操作,即拓展了原有对象的功能。

  • 静态代理:在程序运行前,由程序员手写源码,然后再对其编译创建字节码的技术。
  • 动态代理:在程序运行中,运用反射机制动态创建字节码的技术。

JDK动态代理是通过 java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象,对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。

JDK的动态代理用起来非常简单,但它是有局限性的,使用动态代理的对象必须实现一个或多个接口。为了实现普通类的代理可以使用CGLIB代理。 CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。

3.2.2、JDK静态代理

项目名称: proxy-demo-01

public interface UserDao {
    public void save();public void delete();public void update();public void find();
}
public class UserDaoImpl implements UserDao {
    @Overridepublic void save() {
    System.out.println("UserDaoImpl save ...");}@Overridepublic void delete() {
    System.out.println("UserDaoImpl delete ...");}@Overridepublic void update() {
    System.out.println("UserDaoImpl update ...");}@Overridepublic void find() {
    System.out.println("UserDaoImpl find ...");}
}
public class UserDaoProxy implements UserDao {
    private UserDao userDao;public UserDaoProxy(UserDao userDao) {
    this.userDao = userDao;}@Overridepublic void save() {
    userDao.save();}@Overridepublic void delete() {
    System.out.println("UserDaoProxy 权限校验 ...");userDao.delete();System.out.println("UserDaoProxy 日志记录 ...");}@Overridepublic void update() {
    userDao.update();}@Overridepublic void find() {
    userDao.find();}
}
UserDao userDao = new UserDaoImpl();
UserDao userDaoProxy = new UserDaoProxy(userDao);
userDaoProxy.save();
userDaoProxy.delete();
userDaoProxy.update();
userDaoProxy.find();

3.2.3、JDK动态代理

项目名称: proxy-demo-02

public interface UserDao {
    public void save();public void delete();public void update();public void find();
}
public class UserDaoImpl implements UserDao {
    @Overridepublic void save() {
    System.out.println("UserDaoImpl save ...");}@Overridepublic void delete() {
    System.out.println("UserDaoImpl delete ...");}@Overridepublic void update() {
    System.out.println("UserDaoImpl update ...");}@Overridepublic void find() {
    System.out.println("UserDaoImpl find ...");}
}
public class UserDaoProxy {
    private UserDao userDao;public UserDaoProxy(UserDao userDao) {
    this.userDao = userDao;}public UserDao createUserDao() {
    Class<? extends UserDao> clazz = userDao.getClass();UserDao ud = (UserDao) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
    @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object result;if ("delete".equals(method.getName())) {
    System.out.println("UserDaoProxy 权限校验 ...");result = method.invoke(userDao, args);System.out.println("UserDaoProxy 日志记录 ...");} else {
    result = method.invoke(userDao, args);}return result;}});return ud;}
}
UserDao userDao = new UserDaoImpl();
UserDao userDaoProxy = new UserDaoProxy(userDao).createUserDao();
userDaoProxy.save();
userDaoProxy.delete();
userDaoProxy.update();
userDaoProxy.find();

3.2.4、CGLIB静态代理

项目名称: proxy-demo-03

public class UserDaoImpl {
    public void save() {
    System.out.println("UserDaoImpl save ...");}public void delete() {
    System.out.println("UserDaoImpl delete ...");}public void update() {
    System.out.println("UserDaoImpl update ...");}public void find() {
    System.out.println("UserDaoImpl find ...");}
}
public class UserDaoImplProxy extends UserDaoImpl {
    @Overridepublic void save() {
    super.save();}@Overridepublic void delete() {
    System.out.println("UserDaoProxy 权限校验 ...");super.delete();System.out.println("UserDaoProxy 日志记录 ...");}@Overridepublic void update() {
    super.update();}@Overridepublic void find() {
    super.find();}
}
UserDaoImplProxy userDaoImplProxy = new UserDaoImplProxy();
userDaoImplProxy.save();
userDaoImplProxy.delete();
userDaoImplProxy.update();
userDaoImplProxy.find();

3.2.5、CGLIB动态代理

项目名称: proxy-demo-04

导入依赖:

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version>
</dependency>
public class UserDaoImpl {
    public void save() {
    System.out.println("UserDaoImpl save ...");}public void delete() {
    System.out.println("UserDaoImpl delete ...");}public void update() {
    System.out.println("UserDaoImpl update ...");}public void find() {
    System.out.println("UserDaoImpl find ...");}
}
public class UserDaoImplProxy {
    public UserDaoImpl createUserDaoImpl() {
    //创建增强对象Enhancer enhancer = new Enhancer();//设置父类对象enhancer.setSuperclass(UserDaoImpl.class);//设置回调方法enhancer.setCallback(new MethodInterceptor() {
    @Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object result;if ("delete".equals(method.getName())) {
    System.out.println("UserDaoImplProxy 权限校验 ...");result = methodProxy.invokeSuper(proxy, args);System.out.println("UserDaoImplProxy 日志记录 ...");} else {
    result = methodProxy.invokeSuper(proxy, args);}return result;}});//返回代理对象return (UserDaoImpl) enhancer.create();}
}
UserDaoImpl userDaoImplProxy = new UserDaoImplProxy().createUserDaoImpl();
userDaoImplProxy.save();
userDaoImplProxy.delete();
userDaoImplProxy.update();
userDaoImplProxy.find();

3.3、AOP的入门

3.3.1、环境的准备

项目名称: aop-demo-01

导入依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.2</version>
</dependency>

命名空间:

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

3.3.2、术语的介绍

连接点

英文名:Join Point,代表类中有哪些方法可以被增强,这些可以被增强的方法称为连接点。

切入点

英文名:Pointcut,类中虽然有很多方法可以被增强,但是只有实际被增强的方法称为切入点。

表达式:execution([权限修饰符] 返回类型 包名.类名.方法名(参数))

通知

英文名:Advice,通知描述了新增加的代码段在切入点处何时执行。

其分类:

  • 前置通知,Before Advice
  • 后置通知,AfterReturning Advice
  • 环绕通知,Around Advice
  • 异常通知,AfterThrowing Advice
  • 最终通知,After Advice

目标对象

英文:Target Object,被增强的对象称为目标对象,由于AOP框架是使用动态代理实现的,因此该对象始终是代理对象。

代理对象

英文:Proxy Object,由AOP框架在程序运行期间动态创建的一个对象。

织入

英文名:Weaving,把通知应用到目标对象的切入点的过程就是织入。

切面

英文名:Aspect,切面是一系列通知和切入点的结合。

3.4、AOP的配置(XML方式)

public class UserDaoImpl {
    public void save() {
    System.out.println("UserDaoImpl save ...");}public Integer delete() {
    System.out.println("UserDaoImpl delete ...");return 10086;}public void update() {
    System.out.println("UserDaoImpl update ...");int i = 1 / 0;}public void find() {
    for (int i = 0; i < 100000; i++) {
     }System.out.println("UserDaoImpl find ...");}
}
public class SysCheck {
    public void check(){
    System.out.println("SysCheck 权限校验 ...");}
}
public class SysLog {
    public void log(Object result) {
    System.out.println("SysLog 日志记录,删除主键:" + result);}
}
public class SysMonitor {
    public void monitor(ProceedingJoinPoint joinPoint) throws Throwable {
    //开始执行时间long startTime = System.currentTimeMillis();//正式执行方法joinPoint.proceed();//结束执行时间long endTime = System.currentTimeMillis();//输出时间差值System.out.println("SysMonitor 耗费时间:" + (endTime - startTime) + "ms");}
}
public class SysEmail {
    public void email(Throwable throwable) {
    System.out.println("SysEmail 发送邮件,异常信息:" + throwable.getMessage());}
}
public class SysCounter {
    private static Integer count = 0;public void counter() {
    System.out.println("SysCounter 调用次数:" + (++count));}
}
<bean id="userDaoImpl" class="com.caochenlei.spring5.dao.UserDaoImpl"></bean>
<bean id="sysCheck" class="com.caochenlei.spring5.enhance.SysCheck"></bean>
<bean id="sysLog" class="com.caochenlei.spring5.enhance.SysLog"></bean>
<bean id="sysMonitor" class="com.caochenlei.spring5.enhance.SysMonitor"></bean>
<bean id="sysEmail" class="com.caochenlei.spring5.enhance.SysEmail"></bean>
<bean id="sysCounter" class="com.caochenlei.spring5.enhance.SysCounter"></bean><!--aop配置-->
<aop:config><!--配置切入点:删除方法--><aop:pointcut id="deleteMethodEnhance" expression="execution(* com.caochenlei.spring5.dao.UserDaoImpl.delete(..))"/><!--配置切入点:查找方法--><aop:pointcut id="findMethodEnhance" expression="execution(* com.caochenlei.spring5.dao.UserDaoImpl.find(..))"/><!--配置切入点:更新方法--><aop:pointcut id="updateMethodEnhance" expression="execution(* com.caochenlei.spring5.dao.UserDaoImpl.update(..))"/><!--配置切入点:所有方法--><aop:pointcut id="allMethodEnhance" expression="execution(* com.caochenlei.spring5.dao.UserDaoImpl.*(..))"/><!--配置切面:调用计数--><aop:aspect ref="sysCounter"><aop:after method="counter" pointcut-ref="allMethodEnhance"/></aop:aspect><!--配置切面:发送邮件--><aop:aspect ref="sysEmail"><aop:after-throwing method="email" pointcut-ref="updateMethodEnhance" throwing="throwable"/></aop:aspect><!--配置切面:性能监控--><aop:aspect ref="sysMonitor"><aop:around method="monitor" pointcut-ref="findMethodEnhance"/></aop:aspect><!--配置切面:日志记录--><aop:aspect ref="sysLog"><aop:after-returning method="log" pointcut-ref="deleteMethodEnhance" returning="result"/></aop:aspect><!--配置切面:权限校验--><aop:aspect ref="sysCheck"><aop:before method="check" pointcut-ref="deleteMethodEnhance"/></aop:aspect>
</aop:config>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDaoImpl userDaoImpl = applicationContext.getBean("userDaoImpl", UserDaoImpl.class);
userDaoImpl.save();
userDaoImpl.delete();
userDaoImpl.find();
userDaoImpl.update();

3.5、AOP的配置(注解方式)

3.5.1、半注解开发

项目名称: aop-demo-02

导入依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.2</version>
</dependency>

命名空间:

xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
@Repository
public class UserDaoImpl {
    public void save() {
    System.out.println("UserDaoImpl save ...");}public Integer delete() {
    System.out.println("UserDaoImpl delete ...");return 10086;}public void update() {
    System.out.println("UserDaoImpl update ...");int i = 1 / 0;}public void find() {
    for (int i = 0; i < 100000; i++) {
     }System.out.println("UserDaoImpl find ...");}
}
@Component
@Aspect
public class SysCheck {
    @Pointcut("execution(* com.caochenlei.spring5.dao.UserDaoImpl.delete(..))")public void deleteMethodEnhance() {
    }@Before("deleteMethodEnhance()")public void check(){
    System.out.println("SysCheck 权限校验 ...");}
}
@Component
@Aspect
public class SysLog {
    @Pointcut("execution(* com.caochenlei.spring5.dao.UserDaoImpl.delete(..))")public void deleteMethodEnhance() {
    }@AfterReturning(value = "deleteMethodEnhance()",returning = "result")public void log(Object result) {
    System.out.println("SysLog 日志记录,删除主键:" + result);}
}
@Component
@Aspect
public class SysMonitor {
    @Pointcut("execution(* com.caochenlei.spring5.dao.UserDaoImpl.find(..))")public void findMethodEnhance() {
    }@Around("findMethodEnhance()")public void monitor(ProceedingJoinPoint joinPoint) throws Throwable {
    //开始执行时间long startTime = System.currentTimeMillis();//正式执行方法joinPoint.proceed();//结束执行时间long endTime = System.currentTimeMillis();//输出时间差值System.out.println("SysMonitor 耗费时间:" + (endTime - startTime) + "ms");}
}
@Component
@Aspect
public class SysEmail {
    @Pointcut("execution(* com.caochenlei.spring5.dao.UserDaoImpl.update(..))")public void updateMethodEnhance() {
    }@AfterThrowing(value = "updateMethodEnhance()", throwing = "throwable")public void email(Throwable throwable) {
    System.out.println("SysEmail 发送邮件,异常信息:" + throwable.getMessage());}
}
@Component
@Aspect
public class SysCounter {
    private static Integer count = 0;@Pointcut("execution(* com.caochenlei.spring5.dao.UserDaoImpl.*(..))")public void allMethodEnhance() {
    }@After("allMethodEnhance()")public void counter() {
    System.out.println("SysCounter 调用次数:" + (++count));}
}
<!--开启组件扫描-->
<context:component-scan base-package="com.caochenlei"/><!--开启AOP代理-->
<aop:aspectj-autoproxy/>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDaoImpl userDaoImpl = applicationContext.getBean("userDaoImpl", UserDaoImpl.class);
userDaoImpl.save();
userDaoImpl.delete();
userDaoImpl.find();
userDaoImpl.update();

3.5.2、全注解开发

项目名称: aop-demo-03

导入依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.2</version>
</dependency>
@Repository
public class UserDaoImpl {
    public void save() {
    System.out.println("UserDaoImpl save ...");}public Integer delete() {
    System.out.println("UserDaoImpl delete ...");return 10086;}public void update() {
    System.out.println("UserDaoImpl update ...");int i = 1 / 0;}public void find() {
    for (int i = 0; i < 100000; i++) {
     }System.out.println("UserDaoImpl find ...");}
}
@Component
@Aspect
public class SysCheck {
    @Before("execution(* com.caochenlei.spring5.dao.UserDaoImpl.delete(..))")public void check(){
    System.out.println("SysCheck 权限校验 ...");}
}
@Component
@Aspect
public class SysLog {
    @AfterReturning(value = "execution(* com.caochenlei.spring5.dao.UserDaoImpl.delete(..))", returning = "result")public void log(Object result) {
    System.out.println("SysLog 日志记录,删除主键:" + result);}
}
@Component
@Aspect
public class SysMonitor {
    @Around("execution(* com.caochenlei.spring5.dao.UserDaoImpl.find(..))")public void monitor(ProceedingJoinPoint joinPoint) throws Throwable {
    //开始执行时间long startTime = System.currentTimeMillis();//正式执行方法joinPoint.proceed();//结束执行时间long endTime = System.currentTimeMillis();//输出时间差值System.out.println("SysMonitor 耗费时间:" + (endTime - startTime) + "ms");}
}
@Component
@Aspect
public class SysEmail {
    @AfterThrowing(value = "execution(* com.caochenlei.spring5.dao.UserDaoImpl.update(..))", throwing = "throwable")public void email(Throwable throwable) {
    System.out.println("SysEmail 发送邮件,异常信息:" + throwable.getMessage());}
}
@Component
@Aspect
public class SysCounter {
    private static Integer count = 0;@After("execution(* com.caochenlei.spring5.dao.UserDaoImpl.*(..))")public void counter() {
    System.out.println("SysCounter 调用次数:" + (++count));}
}
@Configuration
@ComponentScan("com.caochenlei")
@EnableAspectJAutoProxy
public class AppConfig {
    }
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserDaoImpl userDaoImpl = applicationContext.getBean("userDaoImpl", UserDaoImpl.class);
userDaoImpl.save();
userDaoImpl.delete();
userDaoImpl.find();
userDaoImpl.update();

多个切面增强同一切点指定优先级:

在切面@Aspect上使用注解@Order(整数)来标识当前切面下所有的通知的优先级,数字越小,优先级越高,优先级越高就越先执行。

第四章 Test

4.1、整合Junit4单元测试

项目名称: junit4-demo

导入依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.1</version><scope>test</scope>
</dependency>

Spring5配置方式:

public class User {
    }
<bean id="user" class="com.caochenlei.spring5.bean.User"></bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Junit4Test {
    @Autowiredprivate User user;@Testpublic void testUser() {
    System.out.println(user);}
}

Spring5注解方式:

@Component
public class User {
    }
@Configuration
@ComponentScan("com.caochenlei")
public class AppConfig {
    }
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
    AppConfig.class})
public class Junit4Test2 {
    @Autowiredprivate User user;@Testpublic void testUser() {
    System.out.println(user);}
}

4.2、整合Junit5单元测试

项目名称: junit5-demo

导入依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.7.0</version><scope>test</scope>
</dependency>

Spring5配置方式:

public class User {
    }
<bean id="user" class="com.caochenlei.spring5.bean.User"></bean>
@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
public class Junit5Test {
    @Autowiredprivate User user;@Testpublic void testUser() {
    System.out.println(user);}
}

Spring5注解方式:

@Component
public class User {
    }
@Configuration
@ComponentScan("com.caochenlei")
public class AppConfig {
    }
@SpringJUnitConfig(AppConfig.class)
public class Junit5Test2 {
    @Autowiredprivate User user;@Testpublic void testUser() {
    System.out.println(user);}
}

4.3、整合log4j2日志框架

项目名称: log-demo

导入依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.14.0</version>
</dependency>

添加配置: log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<Configuration status="info"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>

测试代码:

public class Log4j2Test {
    private static final Log log = LogFactory.getLog(Log4j2Test.class);public static void main(String[] args) {
    log.error("错误信息");log.warn("警告信息");log.info("提示信息");}
}

第五章 JdbcTemplate

5.1、JdbcTemplate的概述

JDBC

Java数据库连接(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。

没出现JDBC之前,我们要适配不同数据库,就要分别为每种数据库编写一套代码。

image-20201222100913985

在出现JDBC之后,我们不用直接对数据库进行操作了,我们通过面向接口编程的方式,编写一次代码,底层数据库就可以任意切换了。

image-20201222101237606

JDBCTemplate

JDBC已经能够满足大部分用户最基本的需求,但是在使用JDBC时,必须自己来管理数据库资源如:获取PreparedStatement,设置SQL语句参数,关闭连接等步骤。JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate是Spring的一部分,JdbcTemplate处理了资源的建立和释放,他帮助我们避免一些常见的错误,比如忘了总要关闭连接。他运行核心的JDBC工作流,如Statement的建立和执行,而我们只需要提供SQL语句和提取结果。

image-20201222105856554

在JdbcTemplate中执行SQL语句的方法大致分为3类:

  1. execute:可以执行所有SQL语句,但是该方法没有返回值,一般用于执行DDL语句。
  2. update:用于执行INSERTUPDATEDELETE等DML语句。
  3. queryXxx:用于DQL数据查询语句。

5.2、JdbcTemplate的环境准备

安装软件: MySQL 5.5.61

项目名称: jdbctemplate-demo

导入依赖:

<!--导入Spring核心依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<!--整合Junit5单元测试-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.7.0</version><scope>test</scope>
</dependency>
<!--导入MySQL数据库驱动-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version>
</dependency>
<!--导入Druid数据库连接池-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.4</version>
</dependency>
<!--导入JdbcTemplate模板-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.2</version>
</dependency>
<!--整合log4j2日志框架-->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.14.0</version>
</dependency>

创建配置: applicationContext.xml

<!--配置Druid数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test"/><property name="username" value="root"/><property name="password" value="123456"/>
</bean><!--配置JdbcTemplate模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/>
</bean>

创建配置: log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<Configuration status="info"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>

测试方法: JdbcTemplateTest

@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
public class JdbcTemplateTest {
    @Autowiredprivate JdbcTemplate jdbcTemplate;@Testpublic void testGetCatalog() throws SQLException {
    Connection connection = jdbcTemplate.getDataSource().getConnection();System.out.println(connection.getCatalog());}
}

5.3、JdbcTemplate的基本使用

5.3.1、创建数据表

@Test
public void testCreateTable() throws SQLException {
    String sql = "create table t_user(uid int not null primary key auto_increment, uname varchar(20), uage int, ugender varchar(20))";jdbcTemplate.execute(sql);System.out.println("数据表创建成功");
}

5.3.2、删除数据表

@Test
public void testDeleteTable() throws SQLException {
    String sql = "drop table t_user";jdbcTemplate.execute(sql);System.out.println("数据表删除成功");
}

5.3.3、插入一条数据

@Test
public void testInsertOne() throws SQLException {
    String sql = "insert into t_user values(null,'张三',28,'男')";int rows = jdbcTemplate.update(sql);System.out.println("影响行数:" + rows);
}

5.3.4、修改一条数据

@Test
public void testUpdateOne() throws SQLException {
    String sql = "update t_user set ugender = '女' where uid = 1";int rows = jdbcTemplate.update(sql);System.out.println("影响行数:" + rows);
}

5.3.5、删除一条数据

@Test
public void testDeleteOne() throws SQLException {
    String sql = "delete from t_user where uid = 1";int rows = jdbcTemplate.update(sql);System.out.println("影响行数:" + rows);
}

5.3.6、查询所有数据

@Test
public void testFindAll() throws SQLException {
    String sql = "select * from t_user";List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));for (User user : users) {
    System.out.println(user);}
}

5.3.7、查询一条数据

@Test
public void testFindOne() throws SQLException {
    String sql = "select * from t_user where uid = 1";User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class));System.out.println(user);
}

5.3.8、查询记录个数

@Test
public void testFindCount() throws SQLException {
    String sql = "select count(*) from t_user";Integer count = jdbcTemplate.queryForObject(sql, Integer.class);System.out.println("记录总数:" + count);
}

第六章 JmsTemplate

6.1、JmsTemplate的概述

消息中间件

消息中间件就是利用高效可靠的消息传递机制进行与平台无关的数据交流并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。对于消息中间件,常见的角色大致也就有Producer(生产者)、Consumer(消费者)。

常见的消息中间件产品:

  • ActiveMQ:它是Apache出品,最流行、能力强劲的开源消息总线。它是一个完全支持JMS 1.1和J2EE 1.4规范的JMS Provider实现。
  • RabbitMQ:AMQP协议的领导实现,支持多种场景。淘宝的MySQL集群内部有使用它进行通讯,OpenStack开源云平台的通信组件,最先在金融行业得到运用。
  • Kafka:Apache下的一个子项目 。高吞吐,在一台普通的服务器上也可以达到10W/s的吞吐速率,完全的分布式系统,适合处理海量数据。

JMS

JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数消息中间件提供商都对JMS提供支持。

消息

消息是 JMS 中的一种类型对象,我们可以将消息分为几种类型,它们分别携带:简单文本(TextMessage)、可序列化的对象(ObjectMessage)、属性集合(MapMessage)、字节流(BytesMessage)、原始值流(StreamMessage)。

对于消息的传递有两种类型:

  • 一种是点对点的,即一个生产者和一个消费者一一对应。

image-20201222141927605

  • 一种是发布/订阅模式,即一个生产者产生消息并进行发送后,可以由多个消费者进行接收。

image-20201222142133340

JmsTemplate

JmsTemplate是spring框架中提供的一个对象,是对原始繁琐的JMS API对象的简单封装。

6.2、JmsTemplate的环境准备

安装软件: Activemq 5.16.0

启动命令:activemq.bat start
请求地址:http://localhost:8161/
登录账号:admin
登录密码:admin

6.3、JmsTemplate的基本使用

6.3.1、点对点模式

6.3.1.1、消息生产者

项目名称: jmstemplate-producer-demo

导入依赖:

<!--引入Spring核心依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<!--整合Junit5单元测试-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.7.0</version><scope>test</scope>
</dependency>
<!--导入JmsTemplate模板-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jms</artifactId><version>5.3.2</version>
</dependency>
<!--导入ActiveMQ客户端-->
<dependency><groupId>org.apache.activemq</groupId><artifactId>activemq-client</artifactId><version>5.16.0</version>
</dependency>
<!--整合log4j2日志框架-->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.14.0</version>
</dependency>

创建配置: applicationContext-ptp.xml

<!--配置ActiveMQ连接工厂-->
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"><property name="brokerURL" value="tcp://localhost:61616"/>
</bean><!--配置JmsTemplate模板-->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"><property name="connectionFactory" ref="connectionFactory"/>
</bean><!--配置点对点的消息队列-->
<bean id="queue" class="org.apache.activemq.command.ActiveMQQueue"><constructor-arg name="name" value="send_sms_queue"/>
</bean>

创建配置: log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<Configuration status="info"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>

测试代码: PTPProducerTest

@SpringJUnitConfig(locations = "classpath:applicationContext-ptp.xml")
public class PTPProducerTest {
    @Autowiredprivate JmsTemplate jmsTemplate;@Autowiredprivate Queue queue;@Testpublic void testPTPSend() {
    jmsTemplate.send(queue, new MessageCreator() {
    @Overridepublic Message createMessage(Session session) throws JMSException {
    return session.createTextMessage("123456");}});}
}

6.3.1.2、消息消费者

项目名称: jmstemplate-consumer-demo

导入依赖:

<!--引入Spring核心依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<!--整合Junit5单元测试-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.7.0</version><scope>test</scope>
</dependency>
<!--导入JmsTemplate模板-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jms</artifactId><version>5.3.2</version>
</dependency>
<!--导入ActiveMQ客户端-->
<dependency><groupId>org.apache.activemq</groupId><artifactId>activemq-client</artifactId><version>5.16.0</version>
</dependency>
<!--整合log4j2日志框架-->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.14.0</version>
</dependency>

创建配置: applicationContext-ptp.xml

<!--配置ActiveMQ连接工厂-->
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"><property name="brokerURL" value="tcp://localhost:61616"/>
</bean><!--配置JmsTemplate模板-->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"><property name="connectionFactory" ref="connectionFactory"/>
</bean><!--配置点对点的消息队列-->
<bean id="queue" class="org.apache.activemq.command.ActiveMQQueue"><constructor-arg name="name" value="send_sms_queue"/>
</bean><!--配置默认消息监听容器-->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer"><property name="connectionFactory" ref="connectionFactory"/><property name="destination" ref="queue"/><property name="messageListener"><bean class="com.caochenlei.spring5.listener.PTPConsumerListener"/></property>
</bean>

创建配置: log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<Configuration status="info"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>

消息监听: PTPConsumerListener

public class PTPConsumerListener implements MessageListener {
    @Overridepublic void onMessage(Message message) {
    TextMessage textMessage = (TextMessage) message;try {
    System.out.println(textMessage.getText());} catch (JMSException e) {
    e.printStackTrace();}}
}

测试代码: PTPConsumerTest

@SpringJUnitConfig(locations = "classpath:applicationContext-ptp.xml")
public class PTPConsumerTest {
    @Testpublic void testPTPReceive() {
    Scanner sc = new Scanner(System.in);sc.next();}
}

6.3.2、发布订阅模式

6.3.2.1、消息生产者

创建配置: applicationContext-ps.xml

<!--配置ActiveMQ连接工厂-->
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"><property name="brokerURL" value="tcp://localhost:61616"/>
</bean><!--配置JmsTemplate模板-->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"><property name="connectionFactory" ref="connectionFactory"/>
</bean><!--配置发布订阅消息队列-->
<bean id="topic" class="org.apache.activemq.command.ActiveMQTopic"><constructor-arg name="name" value="generate_page_topic"/>
</bean>

测试代码: PSProducerTest

@SpringJUnitConfig(locations = "classpath:applicationContext-ps.xml")
public class PSProducerTest {
    @Autowiredprivate JmsTemplate jmsTemplate;@Autowiredprivate Topic topic;@Testpublic void testPSSend() {
    jmsTemplate.send(topic, new MessageCreator() {
    @Overridepublic Message createMessage(Session session) throws JMSException {
    return session.createTextMessage("生成页面...");}});}
}

6.3.2.2、消息消费者

创建配置: applicationContext-ps.xml

<!--配置ActiveMQ连接工厂-->
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"><property name="brokerURL" value="tcp://localhost:61616"/>
</bean><!--配置JmsTemplate模板-->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"><property name="connectionFactory" ref="connectionFactory"/>
</bean><!--配置发布订阅消息队列-->
<bean id="topic" class="org.apache.activemq.command.ActiveMQTopic"><constructor-arg name="name" value="generate_page_topic"/>
</bean><!--配置默认消息监听容器-->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer"><property name="connectionFactory" ref="connectionFactory"/><property name="destination" ref="topic"/><property name="messageListener"><bean class="com.caochenlei.spring5.listener.PSConsumerListener"/></property>
</bean>

消息监听: PSConsumerListener

public class PSConsumerListener implements MessageListener {
    @Overridepublic void onMessage(Message message) {
    TextMessage textMessage = (TextMessage) message;try {
    System.out.println(textMessage.getText());} catch (JMSException e) {
    e.printStackTrace();}}
}

测试代码: PSConsumerTest

@SpringJUnitConfig(locations = "classpath:applicationContext-ps.xml")
public class PSConsumerTest {
    @Testpublic void testPSReceive() {
    Scanner sc = new Scanner(System.in);sc.next();}
}

第七章 Transactions

7.1、Transactions的概述

事务

Transactions代表事务,简写为tx,它是用户定义的一个操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。

典型场景:银行转账

image-20201222195912959

如果张三的钱减少了,而此后,系统发生故障,那么后边的代码都不会执行,因此,李四并没有多100元,而张三减少的100元就这么消失了。

如果我们在 @Service 注解所标注的服务层加上事务管理,那么方法中的所有操作都将会变成一个操作序列,这些操作要么全做要么全不做,如果全部都做就意味着转账可以正常的执行,即使全部都没有做,那么账户的钱也不会减少,事务的增加,大大提高了系统对数据库操作的安全性。

事务特性

  • 原子性(Atomicity):强调事务的不可分割
  • 一致性(Consistency):事务的执行的前后数据的完整性保持一致
  • 隔离性(Isolation):一个事务执行的过程中,不应该受到其他事务的干扰
  • 持久性(Durability):一个事务一旦结束,数据就会持久到数据库

事务隔离级别

如果不考虑事务的隔离性,可能会引发读安全性问题:

  • 脏读:一个事务读到了另一个事务未提交的数据
  • 不可重复读:一个事务读到了另一个事务已经提交的 update 的数据,导致多次查询结果不一致
  • 幻读 / 虚读:一个事务读到了另一个事务已经提交的 insert 的数据,导致多次查询结果不一致

脏读:

image-20201223101011417

不可重复读:

image-20201223100940044

幻读 / 虚读:

image-20201223102824838

要想解决读安全性问题,我们就需要给事务设置隔离级别,Spring定义了4种隔离级别:

隔离级别 中文说明 说明
READ UNCOMMITTED 读未提交 不能解决以上所有读问题,效率最高,安全性最低,一般不用
READ COMMITTED 读已提交 避免脏读,不可重复读和幻读有可能发生,Oracle默认的隔离级别
REPEATABLE READ 可重复读 避免脏读、不可重复读,幻读有可能发生,MySQL默认的隔离级别
SERIALIZABLE 串行化 可以解决以上所有读问题,效率最差,安全性最高,一般不用

事务传播行为

事务传播行为(Propagation Behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何执行。

事务传播行为既然是传播,那么至少有两个东西,才可以发生传播,单体不存在传播这个行为。

例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

通常事务都是加在 Service 层,事务的传播行为是为了解决特别复杂的业务,业务层方法互相调用的问题,Spring定义了7种传播行为:

image-20201222214721106

传播行为 说明
REQUIRED 如果方法A有事务在运行,当前的方法B就在这个事务内运行,否则它就创建一个新的事务,在自己的事务内运行。
SUPPORTS 如果方法A有事务在运行,当前的方法B就在这个事务内运行,否则它可以不运行在事务中。
MANDATORY 如果方法A有事务在运行,当前的方法B就在这个事务内运行,否则它就抛出异常。
REQUIRES_NEW 方法B建个新的事务运行,如果方法A有事务存在,则挂起方法A当前的事务。
NOT_SUPPORTED 方法B以非事务方式运行,如果方法A有事务存在,则挂起方法A当前的事务。
NEVER 方法B以非事务方式运行,如果方法A有事务存在,则抛出异常。
NESTED 如果当前事务存在,则嵌套事务执行。

注意:使用REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚,两个事务互不影响,两个事务不是一个真正的嵌套事务。使用NESTED时,外层事务的回滚可以引起内层事务的回滚,而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。

只读事务概念

如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性。

如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。

在将事务设置成只读后,相当于将数据库设置成只读数据库,此时若要进行写的操作,会出现错误。由于只读事务不存在数据的修改,因此数据库将会为只读事务提供一些优化手段,例如Oracle对于只读事务,不启动回滚段,不记录回滚log。

7.2、Transactions的环境准备

准备数据:

DROP DATABASE IF EXISTS `test`;CREATE DATABASE `test`;USE `test`;DROP TABLE IF EXISTS `t_account`;CREATE TABLE `t_account` (`aid` INT(11) NOT NULL AUTO_INCREMENT COMMENT '用户主键',`aname` VARCHAR(20) DEFAULT NULL COMMENT '用户名称',`amoney` INT(11) DEFAULT NULL COMMENT '用户余额',PRIMARY KEY (`aid`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;INSERT  INTO `t_account`(`aid`,`aname`,`amoney`) VALUES (1,'张三',1000);
INSERT  INTO `t_account`(`aid`,`aname`,`amoney`) VALUES (2,'李四',1000);

7.3、Transactions的配置(XML方式)

7.3.1、事务模板方式

项目名称: tx-demo-01

导入依赖:

<!--导入Spring核心依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<!--整合Junit5单元测试-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.7.0</version><scope>test</scope>
</dependency>
<!--导入MySQL数据库驱动-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version>
</dependency>
<!--导入Druid数据库连接池-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.4</version>
</dependency>
<!--导入JdbcTemplate模板-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.2</version>
</dependency>
<!--整合log4j2日志框架-->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.14.0</version>
</dependency>

创建配置: applicationContext.xml

<!--配置Druid数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test"/><property name="username" value="root"/><property name="password" value="123456"/>
</bean><!--配置JdbcTemplate模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/>
</bean><!--配置Dao-->
<bean id="accountDao" class="com.caochenlei.spring5.dao.impl.AccountDaoImpl"><property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean><!--配置Service-->
<bean id="accountService" class="com.caochenlei.spring5.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/><property name="transactionTemplate" ref="transactionTemplate"/>
</bean><!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean><!--配置事务模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref="transactionManager"/><property name="isolationLevelName" value="ISOLATION_REPEATABLE_READ"/><property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
</bean>

创建配置: log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<Configuration status="info"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>
public interface AccountDao {
    //少钱public void reduceMoney(Integer id, Integer money);//多钱public void addMoney(Integer id, Integer money);
}
public class AccountDaoImpl implements AccountDao {
    private JdbcTemplate jdbcTemplate;public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;}@Overridepublic void reduceMoney(Integer id, Integer money) {
    String sql = "update t_account set amoney = amoney - ? where aid = ?";jdbcTemplate.update(sql, money, id);System.out.println(id + ":少钱");}@Overridepublic void addMoney(Integer id, Integer money) {
    String sql = "update t_account set amoney = amoney + ? where aid = ?";jdbcTemplate.update(sql, money, id);System.out.println(id + ":多钱");}
}
public interface AccountService {
    //转账public void transfer(Integer fromId, Integer toId, Integer money);
}
public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;}private TransactionTemplate transactionTemplate;public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
    this.transactionTemplate = transactionTemplate;}@Overridepublic void transfer(final Integer fromId, final Integer toId, final Integer money) {
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    @Overrideprotected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
    //少钱accountDao.reduceMoney(fromId, money);//模拟异常int i = 1 / 0;//加钱accountDao.addMoney(toId, money);}});}
}

测试方法: JdbcTemplateTest

@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
public class TxTest {
    @Autowiredprivate AccountService accountService;@Testpublic void testTransfer() {
    accountService.transfer(1, 2, 100);}
}

7.3.2、AOP切面方式

项目名称: tx-demo-02

导入依赖:

<!--导入Spring核心依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<!--整合Junit5单元测试-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.7.0</version><scope>test</scope>
</dependency>
<!--导入MySQL数据库驱动-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version>
</dependency>
<!--导入Druid数据库连接池-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.4</version>
</dependency>
<!--导入JdbcTemplate模板-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.2</version>
</dependency>
<!--整合log4j2日志框架-->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.14.0</version>
</dependency>
<!--导入AOP的依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.2</version>
</dependency>

命名空间:

xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

创建配置: applicationContext.xml

<!--配置Druid数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test"/><property name="username" value="root"/><property name="password" value="123456"/>
</bean><!--配置JdbcTemplate模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/>
</bean><!--配置Dao-->
<bean id="accountDao" class="com.caochenlei.spring5.dao.impl.AccountDaoImpl"><property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean><!--配置Service-->
<bean id="accountService" class="com.caochenlei.spring5.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/>
</bean><!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean><!--配置事务通知-->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="transfer" isolation="READ_COMMITTED" propagation="REQUIRED"/></tx:attributes>
</tx:advice><!--配置事务切面-->
<aop:config><aop:pointcut id="transactionPointcut" expression="execution(* com.caochenlei.spring5.service.impl.AccountServiceImpl.transfer(..))"/><aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointcut"/>
</aop:config>

创建配置: log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<Configuration status="info"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>
public interface AccountDao {
    //少钱public void reduceMoney(Integer id, Integer money);//多钱public void addMoney(Integer id, Integer money);
}
public class AccountDaoImpl implements AccountDao {
    private JdbcTemplate jdbcTemplate;public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;}@Overridepublic void reduceMoney(Integer id, Integer money) {
    String sql = "update t_account set amoney = amoney - ? where aid = ?";jdbcTemplate.update(sql, money, id);System.out.println(id + ":少钱");}@Overridepublic void addMoney(Integer id, Integer money) {
    String sql = "update t_account set amoney = amoney + ? where aid = ?";jdbcTemplate.update(sql, money, id);System.out.println(id + ":多钱");}
}
public interface AccountService {
    //转账public void transfer(Integer fromId, Integer toId, Integer money);
}
public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;}@Overridepublic void transfer(Integer fromId, Integer toId, Integer money) {
    //少钱accountDao.reduceMoney(fromId, money);//模拟异常int i = 1 / 0;//加钱accountDao.addMoney(toId, money);}
}

测试方法: JdbcTemplateTest

@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
public class TxTest {
    @Autowiredprivate AccountService accountService;@Testpublic void testTransfer() {
    accountService.transfer(1, 2, 100);}
}

7.4、Transactions的配置(注解方式)

7.4.1、半注解开发

项目名称: tx-demo-03

导入依赖:

<!--导入Spring核心依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<!--整合Junit5单元测试-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.7.0</version><scope>test</scope>
</dependency>
<!--导入MySQL数据库驱动-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version>
</dependency>
<!--导入Druid数据库连接池-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.4</version>
</dependency>
<!--导入JdbcTemplate模板-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.2</version>
</dependency>
<!--整合log4j2日志框架-->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.14.0</version>
</dependency>

命名空间:

xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd

创建配置: applicationContext.xml

<!--配置Druid数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test"/><property name="username" value="root"/><property name="password" value="123456"/>
</bean><!--配置JdbcTemplate模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/>
</bean><!--配置Dao-->
<bean id="accountDao" class="com.caochenlei.spring5.dao.impl.AccountDaoImpl"><property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean><!--配置Service-->
<bean id="accountService" class="com.caochenlei.spring5.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/>
</bean><!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean><!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>

创建配置: log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<Configuration status="info"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>
public interface AccountDao {
    //少钱public void reduceMoney(Integer id, Integer money);//多钱public void addMoney(Integer id, Integer money);
}
public class AccountDaoImpl implements AccountDao {
    private JdbcTemplate jdbcTemplate;public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;}@Overridepublic void reduceMoney(Integer id, Integer money) {
    String sql = "update t_account set amoney = amoney - ? where aid = ?";jdbcTemplate.update(sql, money, id);System.out.println(id + ":少钱");}@Overridepublic void addMoney(Integer id, Integer money) {
    String sql = "update t_account set amoney = amoney + ? where aid = ?";jdbcTemplate.update(sql, money, id);System.out.println(id + ":多钱");}
}
public interface AccountService {
    //转账public void transfer(Integer fromId, Integer toId, Integer money);
}
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED
)
public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;}@Overridepublic void transfer(Integer fromId, Integer toId, Integer money) {
    //少钱accountDao.reduceMoney(fromId, money);//模拟异常int i = 1 / 0;//加钱accountDao.addMoney(toId, money);}
}

测试方法: JdbcTemplateTest

@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
public class TxTest {
    @Autowiredprivate AccountService accountService;@Testpublic void testTransfer() {
    accountService.transfer(1, 2, 100);}
}

7.4.2、全注解开发

项目名称: tx-demo-04

导入依赖:

<!--导入Spring核心依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.2</version>
</dependency>
<!--整合Junit5单元测试-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.7.0</version><scope>test</scope>
</dependency>
<!--导入MySQL数据库驱动-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version>
</dependency>
<!--导入Druid数据库连接池-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.4</version>
</dependency>
<!--导入JdbcTemplate模板-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.2</version>
</dependency>
<!--整合log4j2日志框架-->
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.14.0</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.14.0</version>
</dependency>

创建配置: AppConfig

@Configuration
@ComponentScan("com.caochenlei")
@EnableTransactionManagement
public class AppConfig {
    /*配置Druid数据库连接池*/@Beanpublic DataSource dataSource() {
    DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/test");dataSource.setUsername("root");dataSource.setPassword("123456");return dataSource;}/*配置JdbcTemplate模板*/@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource) {
    return new JdbcTemplate(dataSource);}/*配置事务管理器*/@Beanpublic TransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);}
}

创建配置: log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<Configuration status="info"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>
public interface AccountDao {
    //少钱public void reduceMoney(Integer id, Integer money);//多钱public void addMoney(Integer id, Integer money);
}
@Repository
public class AccountDaoImpl implements AccountDao {
    private JdbcTemplate jdbcTemplate;@Autowiredpublic void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;}@Overridepublic void reduceMoney(Integer id, Integer money) {
    String sql = "update t_account set amoney = amoney - ? where aid = ?";jdbcTemplate.update(sql, money, id);System.out.println(id + ":少钱");}@Overridepublic void addMoney(Integer id, Integer money) {
    String sql = "update t_account set amoney = amoney + ? where aid = ?";jdbcTemplate.update(sql, money, id);System.out.println(id + ":多钱");}
}
public interface AccountService {
    //转账public void transfer(Integer fromId, Integer toId, Integer money);
}
@Service
@Transactional
public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;@Autowiredpublic void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;}@Overridepublic void transfer(Integer fromId, Integer toId, Integer money) {
    //少钱accountDao.reduceMoney(fromId, money);//模拟异常int i = 1 / 0;//加钱accountDao.addMoney(toId, money);}
}

测试方法: JdbcTemplateTest

@SpringJUnitConfig(AppConfig.class)
public class TxTest {
    @Autowiredprivate AccountService accountService;@Testpublic void testTransfer() {
    accountService.transfer(1, 2, 100);}
}