Spring从入门到精通 | 动力节点老杜

目录

一、Spring启示录

1、OCP开闭原则

2、依赖倒置原则(DIP原则)

3、控制反转(重点)

4、Spring框架(重点)

二、Spring概述

Spring的8大模块

​编辑

Spring特点

1、轻量

2、控制反转IOC

3、面向切面AOP

4、容器

5、框架

三、Spring入门程序

四、Spring对IOC的实现

1、IOC控制反转

2、依赖注入

(1)set注入

(2)构造注入

3、set注入专题

(1)注入外部Bean

(2)注入内部bean

(3)注入简单类型

(4)注入数组

(5)注入list集合、set集合

(6)注入Map集合、properties

4、P命名空间注入

5、c命名空间注入

6、基于XML的自动装配

(1)根据名字进行自动装配 

 (2)根据类型进行自动装配

7、引入外部的属性配置文件

五、bean的作用域

1、bean单例和多例(重点)

2、scope的其他选项

六、工厂模式

1、工厂模式的三种形态

2、简单工厂模式

3、工厂方法模式

七、Bean的实例化方式(重点)

1、通过构造方法实例化

2、简单工厂模式实例化

3、通过factory-bean(工厂方法模式)实例化

 4、通过FactoryBean接口实例化

5、BeanFactory和FactorybEean的区别(面试题)

6、注入自定义Date

八、Bean的生命周期(重点)

1、Bean的生命周期之5步(重点)

2、Bean的生命周期之7步(重点)

3、Bean的生命周期之10步(重点)

4、Bean的作用域不同管理方式不同

5、自己new的对象如何让Spring管理

九、Bean的循环依赖问题(重点)

1、什么是循环依赖

2、单例下set注入产生的循环依赖

3、多例下set注入产生的循环依赖

4、构造注入的循环依赖

5、Spring解决循环依赖的机理(重点)(面试题)

十、回顾反射机制

十一、Spring IOC注解开发(重点)

1、回顾注解

2、声明Bean注解

3、Spring注解的使用

4、选择性实例化Bean

5、负责注入的注解(重点)

(1)@Value注解

(2)@Autowired与@Qualifier

(3)@Resource注解

6、全注解开发

十二、代理模式(重点)

1、静态代理模式

2、动态代理模式

(1)JDK动态代理(重点)

(2)CGLB动态代理(了解)

十三、面向切面编程AOP(重点)

1、AOP介绍

2、AOP的七大术语

3、切点表达式

4、SpringAOP的使用

(1)准备工作

(2)注解实现步骤

(3)切面的排序

(4)通用切点

(4)全注解开发

(5)Xml方式的实现(了解)

5、AOP实际案例

(1)事务

(2)安全日志

十四、Spring事务的支持(重点)

1、事务概述

什么是事务

事务四个处理过程

事务四个特性

Spring实现事务两种方式

Spring事务管理API

声明式事务之注解实现

2、事务的属性

(1)传播行为 propagation

(2)事务隔离级别 isolation

(3)事务超时

(4)只读事务

(5)设置哪些异常回滚事务


一、Spring启示录

1、OCP开闭原则

OCP是软件七大开发原则当中最基本的一个原则:开闭原则
对什么开? 对扩展开放。
对什么闭? 对修改关闭。
OCP原则是最核心的,最基本的,其他的六个原则都是为这个原则服务的。

OCP开闭原则的核心是什么?
只要你在扩展系统功能的时候,没有修改以前写好的代码,那么你就是符合0CP原则的。反之,如果在扩展系统功能的时候,你修改了之前的代码,那么这个设计是失败的,违背OCP原则。
当进行系统功能扩展的时候,如果动了之前稳定的程序,修改了之前的程序,之前所有程序都需要进行重新测试。这是不想看到的,因为非常麻烦。

2、依赖倒置原则(DIP原则)

面向接口编程,面向抽象编程,不要面向具体编程。
依赖倒置原则的目的?
降低程序的耦合度,提高扩展力。
什么叫做符合依赖倒置?
上 不依赖 下,就是符合。
什么叫做违背依赖倒置?
上 依赖 下,就是违背。
只要"下”一改动,"上"就受到牵连

当前程序的设计,显然既违背OCP,又违背DIP,怎么办?
可以采用“控制反转"这种编程思想来解决这个问题。

3、控制反转(重点)

控制反转IOC(Inversion of Control)

反转的是什么?

  • 我不在程序中采用硬编码来new对象了。(new对象不管了,new对象的权利交出去)
  • 不在采用硬编码的方式来维护对象的关系了。(对象之间关系的维护期也交出去)

4、Spring框架(重点)

Spring框架实现了控制反转IOC这种思想

  • spring框架可以帮你new对象
  • spring框架可以帮你维护对象和对象之间的关系

控制反转的实现方式有多种,其中比较重要的叫做:依赖注入(Dependency Injection,简称DI)。控制反转是思想。依赖注入是这种思想的具体实现。依赖注入DI,又包括常见的两种方式:

  1. set注入(执行set方法给属性赋值)
  2. 构造方法注入(执行构造方法给属性赋值)

依赖注入 中“依赖"是什么意思?“注入”是什么意思?

  • 依赖:A对象和B对象的关系。
  • 注入:是一种手段,通过这种手段,可以让A对象和B对象产生关系。
  • 依赖注入:对象A和对象B的关系,靠注入的手段来维护

二、Spring概述

Spring的8大模块

Spring特点

1、轻量

从大小与开销两方面而言Spring都是轻量的。完整的spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。

Spring是非侵入式的: Spring应用中的对象不依赖于Spring的特定类。不用依赖其他类

2、控制反转IOC

Spring通过一种称作控制反转loC的技术进了松拥合。当应用了loc,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为ioc与IND相反一不是对象从容器中查找依赖,而是容器在对象视始化时不等对象请求就主动将依赖传递给它。

3、面向切面AOP

Spring提供了面向切面编的丰富支持,允许通过分离应用的业务逻与系统级务(例如计 auditing) 和务tansacton)管理)进行内聚性的开发。应用对象只实现它们应该做的--完成业务逻辑一仅此而已。它们并不负责(其至是意识)其它的系统级关注点,例如日志或事务支持

4、容器

Spring包含管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每bean如何被创建一一基于一个可配置原型(prototype),你的a.bean可以创建一个单独的实例或者每次需要时都生成一个新的实例--以及它们是如何相与关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。

5、框架

Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring 也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

三、Spring入门程序

spring的bean是不能重复的

spring默认会通过反射机制,调用类的无参构造方法来实例化对象

spring的核心配置文件可以配置多个,只要ClassPathXmlApplicationContext扫描到就行,可以传入多个参数。

 spring在解析配置文件的时候就已经创建对象了

四、Spring对IOC的实现

1、IOC控制反转

2、依赖注入

(1)set注入

set注入的核心实现原理:通过反射机制调用set方法给属性赋值,让两个对象之间产生关系

(2)构造注入

构造注入是在对象实例化过程中注入的

3、set注入专题

(1)注入外部Bean

外部bean就是bean定义在外面,然后引用进去

(2)注入内部bean

在bean标签内部嵌套bean标签就是内部bean,这种方式很少用

(3)注入简单类型

简单类型:八种基本类型及包装类、enum、字符串、Date、uri、url、Locale、Class

 实际开发中不会给Date当简单赋值,一般用ref,因为用value必须用他这个格式,很难记

实际开发中简单类型的经典案例:

(4)注入数组

 

(5)注入list集合、set集合

(6)注入Map集合、properties

4、P命名空间注入

p命名空间注入底层还是set注入,只不过p命名空间注入可以让spring配置变得更简单(我们发现把set方法注释掉就会报错)简化set注入的方式

5、c命名空间注入

本质上就是简化构造方法注入的,是基于构造方法的,所以构造方法必须有

6、基于XML的自动装配

Spring可以自动化注入,被称为自动装配。可以根据名字进行自动装配,也可以根据类型自动装配

(1)根据名字进行自动装配 

 (2)根据类型进行自动装配

7、引入外部的属性配置文件

五、bean的作用域

1、bean单例和多例(重点)

Spring默认情况下bean是单例的,在Spring上下文初始化的时候实例化(new ClassPathXmlApplicationContext)每次调用getBean都返回那个单例对象。

这里可以设置是否用单例,默认为单例singleton。如果手动设置为prototype就会让bean变成多例的,spring上下文初始化的时候不会初始化这些prototype的bean,而是每次调用getBean方法的时候实例化bean对象 

2、scope的其他选项

当引入web框架之后,scope就会多两个选项。 

其实还有很多选项的 

六、工厂模式

1、工厂模式的三种形态

  • 简单工厂模式 :不属于23种设计模式之一。(简单工厂模式又叫做: 静态工厂方法模式。简单工厂模式是工厂方法模式的一种特殊实现)
  • 工厂方法模式 : 是23种设计模式之一。
  • 抽象工厂模式 : 是23种设计模式之一。

2、简单工厂模式

简单工厂模式的角色包括三个:

  • 抽象产品
  • 具体产品
  • 工厂类 

抽象产品:

 

具体产品:

工厂类:

消费者消费:

优点:客户端不需要关心对象的创建过程,需要哪个对象直接向工厂索要即可,初步实现职责分离。客户端只负责“消费”,生产端只负责“生产”。

缺点:假设现在需要扩展一个新的产品,工厂类的代码是要修改的,违背了OCP原则。工厂类的责任重大,不能出现任何问题,因为这个工厂类负责所有产品的生产,成为全能类,工厂类一旦出现问题,整个系统必然全部瘫痪。(全部鸡蛋放到一个篮子里)

3、工厂方法模式

一个工厂对应一种产品,这样就解决工厂全能类的问题了,也符合OCP原则

角色:

  • 抽象产品角色
  • 具体产品角色
  • 抽象工厂角色
  • 具体工厂角色

抽象工厂类

 具体工厂类(一个产品对应一个工厂):

优点:当你要扩展的时候,符合OCP原则,只要添加两个类具体类就行,都是添加类,不用修改之前的代码。(还有简单工厂模式的优点都有)

缺点:每次增加一个类,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加。在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖

七、Bean的实例化方式(重点)

Spring为Bean提供了多种实例化方式,通常包括4种方式 (Spring中为Bean对象的创建准备了多种方案,目的是: 更加灵活)

  1. 通过构造方法实例化
  2. 通过简单工厂模式实例化
  3. 通过factory-bean实例化
  4. 通过FactoryBean接口实例化

1、通过构造方法实例化

2、简单工厂模式实例化

(1)编写一个实体类

(2)编写工厂类

(3)在spring配置文件中配置

 (4)test类中测试

3、通过factory-bean(工厂方法模式)实例化

这次调用的不是简单工厂的静态方法,而是实例方法,需要实例化对象之后才能调用(需要先把工厂实例化出来才能调用方法)

 4、通过FactoryBean接口实例化

以上的第三种方式中,factory-bean是我们自定义的,factory-method也是我们自己定义的。
在Spring中,当你编写的类直接实现FactoryBean接口之后factory-bean不需要指定了,factory-method也不需要指定了。
factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject0方法。

(所以第四种就是第三种演变而来的,可以简化第三种的配置)

实现他的接口,重写他的方法就可以了,最下面那个是单例的方法,true就为单例

5、BeanFactory和FactorybEean的区别(面试题)

BeanFactory是spring ioc容器的顶级对象,被翻译为bean工厂,bean工厂负责创建bean对象

ClassPathXmlApplicationContext就是它的子子子类

FactoryBean它是一个Bean,是一个辅助Spring实例化其他Bean对象的一个Bean

在Spring中,bean可以分类两类bean:普通bean、工厂bean(辅助spring实例化其他bean)

6、注入自定义Date

用一个工厂bean来辅助date的赋值

八、Bean的生命周期(重点)

1、Bean的生命周期之5步(重点)

Bean的生命周期可以粗略的分为以下5步

  1. 实例化Bean (调用无参数构造方法)
  2. 给Bean届性赋值 (调用set方法)
  3. 初始化Bean (会调用Bean的init方法。注意: 这个init()方法需要自已写,自己配)
  4. 使用Bean
  5. 销毁Bean (会调用Bean前destroy方法。注意: 这个destroy()方法需要自己写,自己配)

2、Bean的生命周期之7步(重点)

在5步,第三步是初始化Bean,如果你想在bean初始化前和之后添加代码,可以加入"Bean后处理器",编写一个实现BeanPostProcessor类,并重写before和after方法

  1. 实例化Bean
  2. Bean属性赋值
  3. 执行"Bean后处理器"的before方法。
  4. 初始化Bean
  5. 执行"Bean后处理器"的after方法
  6. 使用Bean
  7. 销毁Bean

3、Bean的生命周期之10步(重点)

 Bean生命周期十步: 比七步添加的那三步在哪里?

  1. 在"Bean后处理器"before方法之前点位
  2. 在"Bean后处理器"before方法之后点位
  3. 使用Bean之后,或者说销毁Bean之前

本质上就是检查是否实现了某个特定的接口,是就调用接口的方法 

4、Bean的作用域不同管理方式不同

Spring容器只对单例的bean进行完整的生命周期管理

如果是prototype作用域的Bean,Spring只负责将bean的初始化完毕,等客户端程序一旦获取到bean之后,spring不再管理该对象的生命周期。只管理一部分的生命周期(只负责前8步,使用Bean之后就不管了)

5、自己new的对象如何让Spring管理

九、Bean的循环依赖问题(重点)

1、什么是循环依赖

A对象中有B属性,B对象中有A属性,这就是循环依赖,我依赖你,你依赖我

比如:丈夫类和妻子类,hasband有wife的引用,wife类中有hasband的引用

2、单例下set注入产生的循环依赖

这种单例下的set注入是不会产生循环依赖问题的,当我们丈夫类在实例化的时候会要用到wife类,我们没有,然后顺便实例化wife类然后赋值,然后到我们实例化wife类的时候,因为是单例的已经有了不再实例化,用到的丈夫类也是一样。

单例和set模式下,spring是如何解决循环依赖的?

主要是,在这种模式下Spring对Bean的管理主要分为清晰的两个阶段:

  1. 在spring容器加载的时候,实例化Bean,只要其中任意一个Bean实例化之后,马上进行"曝光"(不等属性赋值就先曝光)
  2. Bean曝光之后,再进行赋值(调用set方法)

注意:只有在scope为单例的模式下,才会采用提前曝光的措施

3、多例下set注入产生的循环依赖

这种情况就会出现异常,当两个bean的scope都是prototype才会出现异常,一个单例,一个prototype是不会出现的。

4、构造注入的循环依赖

这种情况下产生的循环依赖也是无法解决的。

5、Spring解决循环依赖的机理(重点)(面试题)

Spring为什么可以解决set + singleton模式下循环依赖? 别的模式都解决不了
根本的原因在于:这种方式可以做到将“实例化Bean”和“给Bean属性赋值”这两个动作分开去完成。实例化Bean的时候:调用无参数构造方法来完成。此时可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。给Bean属性赋值的时候: 调用setter方法来完成。
两个步骤是完全可以分离开去完成的,并且这两步不要求在同一个时间点上完成
也就是说,Bean都是单例的,我们可以先把所有的单例bean实化出来,放到一集合当中(我们可称之为缓存)所有的单Bean全部实例化完之后,以后我们再调用set给属性赋值。

底层:先创建bean对象,对象的属性是空,提前曝光工厂(往3级缓存里面放工厂对象),获取单例对象,先从一级缓存中取,如果没有就找二级缓存,如果还是没有就去三级缓存找工厂,通过工厂getObject拿到单例bean之后放回二级缓存,然后清空三级缓存。

十、回顾反射机制

十一、Spring IOC注解开发(重点)

注解存在是为了简化xml的配置,spring6倡导全注解开发

1、回顾注解

这个注解只能出现在类上,不能出现在属性值上面

通过反射机制读取注解

当只给一个包,包下有的类有注解,有的没有,要把有注解的都加入map里,怎么做?

2、声明Bean注解

负责声明Bean的注解,常见的包括四个:

  • @Component
  • @Controller
  • @Service
  • @Repository

上面的是老大,下面3个其实都是第一个的别名,分这么多是为了增加程序的可读性。

3、Spring注解的使用

  1. 加入aop的依赖
  2. 在配置文件中添加context命名空间
  3. 在配置文件中指定扫描的包
  4. 在Bean类上使用注解

小细节:如果像下面这样不指定bean的名字,那么默认会类名第一个字母小写 

 如果有多个包怎么办?

直接扫多个包 或 扫他们的父类包(但这样效率低)

4、选择性实例化Bean

假如某个包下有很多Bean,有的Bean是Component,有的是Controller注解,有的是Service等,现在只允许所有的Controller参与Bean管理,其他都不实例化,要怎么弄?

第一种(全部失效,包含的生效): 

 第二种(全部生效,排除掉不生效的):

5、负责注入的注解(重点)

用了之前的四个注解可以声明Bean,声明后就会被实例化。接下来,我们就要给这些Bean赋值

给bean赋值用到的注解:

  • @Value
  • @Autowired
  • @Qualifier
  • @Resource

(1)@Value注解

@Value用来注入简单类型

之前用xml来注入的必须依赖属性的set方法来注入,现在@Value不提供set方法也可以注入。

@value注解还可以提供在set方法上

 甚至还可以直接放在构造方法上

(2)@Autowired与@Qualifier

@Autowired注解可以用来注入非简单类型,被翻译为:自动连线的,或自动装配

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

@Autowired与@Qualifier一起用才能根据名字来装配

 如果有多个类同一个类型(而且他们都被spring管理了),那么这样单独使用autowired按类型注入就会报错,要按照名字装配(@Autowired与@Qualifier一起用才能根据名字来装配)

下图,虽然Autowired在特定情况可以省略,但最好别省 

(3)@Resource注解

@Resource也可以完成非简单类型注入,和@Autowired区别?

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

6、全注解开发

我们每次都要写xml配置文件,太麻烦了,能不能不用xml,做到全注解开发

能不能编写一个类代替spring的配置文件,可以: 

十二、代理模式(重点)

代理模式在代码实现上有两种形式:静态代理动态代理

1、静态代理模式

现在有个需求,要把项目中原本的所有类加上统计执行时间的功能

解决方案一:直接在原来每个方法中添加代码统计时间的代码。这会违背OCP原则改动原有代码,所有类都要重新测试一遍,而且每个类都要添加这个代码,没有做到代码复用

解决方案二:编写业务类的子类,让子类继承业务类,然后重写方法加统计时间的代码。虽然解决了OCP,但是采用继承关系,偶尔度非常高,而且代码还是没有得到复用。

解决方案三:代理模式。

公共接口:

目标对象:

代理对象:

继承了目标类的同一个接口,实现方法,然后在方法增加功能,再调用原来的方法。

这种方式解决上面两种的缺点,但是这个是静态代理,缺点是每次代理都要重新写一个代理类,太多了就会导致类爆炸。

2、动态代理模式

在程序运行阶段,在内存中动态生成代理类,被称为动态代理

(1)JDK动态代理(重点)

首先还是要写上公共的接口和目标类,但是代理类不用写了,用JDK自动生成:

动态代理需要设置动态代理对象

newProxyInstance 翻译为: 新建代理对象

也就是说,通过调用这个方法可以创建代理对象本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事

  1. 第一件事:在内存中动态的生成了一个代理类的字节码class
  2. 第二件事: new对象了。通过内存中生成的代理类这个代码,实例化了代理对象

 第三个参数用来写增强代码的:

但是这样写完我们发现有了增强代码,但原本目标对象方法的功能却没有了,这时候我们就要学习invoke方法的三个参数了。
invoke 方法是JDK负责调用的,所以JDK 调用这个方法的时候会自动给我们传过来这三个参数我们可以在invoke 方法的大括号中直接使用。

  1. 第一个参数: object proxy 代理对象的引用。这个参数使用较少。
  2. 第二个参数: Method method 目标对象上的目标方法。 (要执行的目标方法就是它。)
  3. 第三个参数: 0bject[] args 月标方法上的实参。

 这时候我们就可以利用invoke方法的参数来调用目标对象的方法,target可以通过构造方法传

最后测试代码

注意:一定要记得把目标对象的目标方法执行结果返回

 如果觉得写JDK的newProxyInstance太麻烦了,可以提取工具类封装起来:

(2)CGLB动态代理(了解)

JDK的动态代理只能代理接口,而CGLB的动态代理可以代理接口也可以代理类,CGLB底层采用继承的方式实现,所以被代理的目标类不能使用final修饰。

十三、面向切面编程AOP(重点)

1、AOP介绍

AOP:面向切面编程(AOP是一种编程技术)

一般一个系统都会有系统服务,例如:日志、事务、安全等。这些系统服务称交叉业务

这些交叉业务几乎通用,不管什么项目什么模块,都是需要的。(面试的时候要说交叉业务

  • 交叉业务代码在多个业务流程中反复出现,显然这个交叉业务代码没得到复用,如果修改的话,多处都需要修改
  • 程序员无法专注核心业务代码的编写,在编写核心业务还需要处理这些交叉业务

将与核心业务无关的代码独立抽取出来,形成独立的组件,然后横向交叉的方式应用到业务流程当中的过程称为AOP

Spring的AOP使用的动态代理是:JDK动态代理+CGLIB动态代理技术。Spring在这两种动态代理中灵活切换,如果是代理接口就默认用JDK动态代理,如果是代理某个类,这个类没有接口实现,就用CGLIB。当然,也可以配置让Spring只用CGLIB(面试可能会问)

AOP优点:

  1. 代码复用性增强
  2. 代码易维护
  3. 使开发者更关注业务逻辑

2、AOP的七大术语

连接点(Joinpoint)可以织入切面的位置。方法执行前后,异常抛出之后等位置。

切点(pointcut)真正织入切面的方法(一个切点对应对个连接点)

通知(Advice)通知又叫增强,就是增强的代码。

  • 放到方法前叫前置通知
  • 放在方法后叫后置通知
  • 方法前后都放叫环绕通知
  • 如果程序有异常,放到catch里面叫异常通知
  • 放到finally叫最终通知

切面(Aspect)切点+通知

织入Weaving:把通知应用到对象的过程

代理对象Proxy:一个目标对象被织入后产生的新对象

目标对象Target:被织入通知的对象

3、切点表达式

切点表达式用来定义通知往哪些方法上切入

execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])

访问控制权限修饰符:可选填,没写就是4个权限都包括,写public表示只包括公开的方法

返回值类型:必须填,* 表示返回值类型任意

全限定类名:可选项,两个点 .. 代表当前包以及包下所有类,省略时表示所有的类

方法名:必填项,*表示所有方法,set*表示所有set方法

形式参数列表:必填

  • ()表示没参数的方法 
  • (..)表示参数类型和个数随意的方法 
  • (*)表示只有一个参数的方法 
  • (*,String)第一个参数任意类型,第二个String=

异常:可选,省略表示任意异常类型

4、SpringAOP的使用

Spring的AOP实现包括3种方式:

  1. Spring框架结合AspectJ框架实现的AOP,基于注解方式(推荐用)(重点)
  2. Spring框架结合AspectJ框架实现的AOP,基于XML方式
  3. Spring框架自己实现的AOP,基于XML配置方式(大家都不用这种方式,了解)

什么是AspectJ?

是Eclipse组织的支持AOP的框架,是独立于Spring之外的框架,Spring框架用了AspectJ

(1)准备工作

先引入Spring的context依赖和Spring-aspects的依赖

然后核心配置文件中添加context和aop的明命名空间

(2)注解实现步骤

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

先试试怎么用,用前置来演示

 proxy-target-class=“true”表示强制使用CGLIB动态代理,不写默认为false,表示混合用(接口使用JDK动态代理,反之使用CGLIB动态代理)        

测试:

我们再来试试各种通知怎么用和循序

 

得出:环绕的范围是最大的,先前环绕再前置,先后置再后环绕

如果加上最终也是后环绕最大

 发送异常的时候,后置和后环绕都不会执行

(3)切面的排序

@Order(数字) 这个注解来排序

比如在安全校验的模块上面写order(1),通知的切面写order(2),那么会先执行安全的切面代码

(4)通用切点

 

 我们可以像这样把通用的切点提取出来,这样以后只用写一遍修改也好修改。

甚至跨类的时候都可以使用:

(4)全注解开发

我们不想写xml核心配置文件,我们就想全注解开发(现在开发都是这种方式)

我们就要专门写个核心配置类config来配置文件

我们的测试类也要方式改变:

(5)Xml方式的实现(了解)

5、AOP实际案例

(1)事务

项目中事务在所难免,需要执行多条DML语句,为了保证数据安全,要么同时成功要么同时失败,这就需要添加事务控制的代码。如下

控制事务的代码是和业务逻辑没有关系的交叉业务,要让这些交叉业务代码得到复用,而且要易于改动,那么我们就可以采用AOP,可以把事务代码作为环绕通知,切入到目标类的方法中。

(2)安全日志

 项目已经上线,运行正常,新需求:凡是系统中增删改操作都要把这个人记录下来

 

十四、Spring事务的支持(重点)

1、事务概述

什么是事务

在一个业务中,通常需要多条增删改语句共同联合完成,这么多条增删改必须同时成功或失败,这样才能保证数据安全。要么同时成功,要么同时失败,就叫事务Transaction 

事务四个处理过程

  1. 开启事务start
  2. 执行核心业务代码
  3. 如果执行过程中没有出现异常,提交事务commit
  4. 如果执行业务代码过程出现异常,回滚事务rollback

事务四个特性

  • 原子性:事务是最小的工作单元,不可再分
  • 一致性:事务要求要么同时成功,要么同时失败,事务前和事务后总量不变
  • 隔离性:事务和事务之间因为有隔离性,才能保证互不干扰
  • 持久性:持久性是事务结束的标志

Spring实现事务两种方式

编程式事务:通过编写代码的方式来实现事务(一般不用这种)

声明式事务:基于注解方式、基于xml配置方式

Spring事务管理API

Spring对事务的管理底层实现方式是基于AOP实现的,采用AOP的方式进行封装,所以Spring专门针对事务开发了一套API

PlatformTransactionManager接口:spring事务管理器的核心接口,在Spring6中它有两个实现:

  • DataSourceTransactionManager:支持jdbcTemplate、mybatis等事务管理
  • JtaTransactionManmger:支持分布式事务管理

写个实现接口的实现类就能被spring帮助管理事务 

声明式事务之注解实现

要在核心配置文件配置事务管理器,开启事务注解驱动器,开启事务注解

写在类上:这个类上的所有的方法都支持事务

2、事务的属性

(1)传播行为 propagation

如果一个方法有事务了,然后调用了另一个方法,另一个方法可能也有事务,那么他们这两个事务要怎么处理呢?这个时候就需要设置事务的传播行为来规定这两个事务怎么处理

规定了一共有7中传播行为:

  • REQUIRED:支持当前事务,如果不存在就新建一个(默认)[没有就新建,有就加入](重要)
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行[有就加入,没有就不管了]
  • MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常[有就加入,没有就抛异常]
  • REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起[不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起](重要)
  • NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务[不支持事务,存在就挂起]
  • NEVER:以非事务方式运行,如果有事务存在,抛出异常[不支持事务,存在就抛异常]
  • NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立千外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED-样。[有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚,没有事务就和REQUIRED一样]

事务的传播行为在Spring框架中被定义为枚举类型

(2)事务隔离级别 isolation

事务隔离级别类似于教室A和教室B之间的那道墙,隔离级别越高表示墙体越厚,隔离效果越好。

隔离事务就是为了防止多事务操作一张表的时候出现的干扰

事务的隔离级别也是枚举类型,越往下隔离效果越好

oracle默认是读提交,mysql默认是可重复读

数据库中读取存在三大问题

  • 脏读:读取到没有提交到数据库的数据,还没有commit还在缓存就能直接读到
  • 不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样
  • 幻读:读到的数据是假的(只要多个事务并发,肯定是存在幻读的,除非一次一个事务排队)

事务隔离级别包括四个级别:

  1. 读取提交 READ_UNCOMMITTED:这种隔离级别,存在脏读问题,表示能够读取到其他事务未提交的数据
  2. 读提交 READ_COMMITTED(oracle默认级别):解决了脏读问题,其他事务提交后才能读到,但存在不可重复读问题
  3. 可重复读 REPEATABLE_READ(mysql默认级别):解决了不可重复读问题,但是存在幻读问题
  4. 序列化 SERIALIZABLE:解决了幻读,但是事务必须排队执行,不支持并发
隔离级别脏读不可重复读幻读
读未提交
读提交
可重复读
序列化

(3)事务超时

@Transactional(timeout=10)

以上代码表示设置事务超时设置为10秒

表示超过10秒如果该事务所有的DML还没执行完毕,最终结果会选择回滚

默认值为-1,表示没有时间限制。

这里有个坑:事务的超时时间是指执行到最后一条DML语句的时间,而不是整个方法的执行时间

所以如果你最后一条DML语句后有很多业务代码,这些代码的执行时间都不计入超时

(4)只读事务

@TranSactional(readOnly = true)

将当前事务设置为只读事务的时候,该事务执行过程中只允许使用select语句执行,增删改不能执行,该属性有什么作用:

启动spring的优化策略,提高select语句的执行效率

如果确实没有增删改操作,建议设置为只读事务

(5)设置哪些异常回滚事务

默认是只要执行过程中发送异常,就回滚事务

我们也可以指定什么异常或该异常的子类异常回滚事务

@Transactional(rollbackFor=RuntimeException.class)

上面代码表示RuntimeException异常或该异常的子类异常才回滚

也可以设置什么异常或他的子类不回滚: 

@Transactional(noRollbackFor=RuntimeException.class)

上面代码表示RuntimeException异常或该异常的子类异常的时候不会回滚

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

卒获有所闻

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

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

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

打赏作者

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

抵扣说明:

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

余额充值