当前位置: 首页 > news >正文

Spring学习——SpringAOP

0. IOC思想(DI)1. 关键注解@Repositorypublic class DeptDaoImpl1 implements DeptDao{}@Repository@Primarypublic class DeptDaoImpl2 implements DeptDao{}@Servicepublic class DeptServiceImpl implements DeptService{@Autowired@Qulifier("deptDaoImpl2")private DeptDao deptDao;}2. 创建对象自定义对象:@Repository  @Service  @Controller  @Component  第三方对象:@Bean  @Configuration3. 对象作用域@Scope单例:只有一个, 默认容器启动,对象创建(@Lazy延迟到第一次获取)多例:每次获取,创建一个新的4. 单元测试@RunWith(运行器)@ContextConfiguration(classes=)

日志功能

需求:在业务层类中的方法中打印日志,记录方法执行前后以及方法发生异常的时间点

基本方式实现

导入初始工程

在这里插入图片描述

添加日志功能

修改DeptServiceImpl,添加日志功能

在这里插入图片描述

测试

在这里插入图片描述

分析代码问题

目前代码存在两个问题

  1. 代码耦合性高:业务代码和日志代码耦合在了一起
  2. 代码复用性低:日志代码在每个方法都要书写一遍

jdk动态代理

在这里插入图片描述

复制工程

在这里插入图片描述

准备目标类

目标类指的是要被代理的类

在这里插入图片描述

准备增强类

增强类指的是要给被代理类添加的功能

在这里插入图片描述

创建代理对象

在测试类中,使用JDK技术创建代理对象,然后调用方法

在这里插入图片描述

cglib动态代理

CGLIB(Code Generation )动态代理利用ASM字节码操作框架在运行时生成代理类,它通过继承的方式代理目标对象,因此可以代理没有实现接口的类。

在这里插入图片描述

复制工程

在这里插入图片描述

删除接口

删除DeptService接口,并删除实现类中对接口的实现

在这里插入图片描述

创建代理对象

package com.itheima.test;import com.itheima.config.SpringConfig;
import com.itheima.log.Logger;
import com.itheima.service.impl.DeptServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.lang.reflect.Method;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class DeptServiceTest {@Autowiredprivate DeptServiceImpl deptServiceImpl;@Autowiredprivate Logger logger;@Testpublic void test1() {//1. 准备目标对象(deptService就是)//2. 编写增强逻辑//注意: 这个InvocationHandler是org.springframework.cglib.proxy.InvocationHandler提供的InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object res = null;logger.m1();try {res = method.invoke(deptServiceImpl,args);}catch (Exception e){logger.m3();}logger.m2();return res;}};//3. 创建代理对象DeptServiceImpl proxyInstance = (DeptServiceImpl) Enhancer.create(DeptServiceImpl.class, invocationHandler);//4. 调用代理对象的方法proxyInstance.findAll();}
}

小结

首先明确在创建代理实现类时,jdk的速度要高于cglib,所以选择的时候:

当被代理类有接口的时候,使用jdk动态代理;当被代理类没有接口的时候,使用cglib动态代理

不同点:

在这里插入图片描述

总结

在这里插入图片描述

当核心业务(保存)和增强业务(日志)同时出现时,我们可以在开发时对他们分别开发,运行时再组装在一起(使用动态代理的方式)。

这样做的好处是:

  1. 逻辑清晰:开发核心业务的时候,不必关注增强业务的代码
  2. 代码复用性高:增强代码不用重复书写

这就是一种 AOP ( 面向切面编程 ) 的思想,它的目的就是在不修改源代码的基础上,对原有功能进行增强。

我的总结: 开发阶段分别开发,运行阶段组装运行

AOP

AOP介绍

AOP( 面向切面编程 )是一种思想,它的目的就是在不修改源代码的基础上,对原有功能进行增强。

在这里插入图片描述

SpringAOP是对AOP思想的一种实现,Spring底层同时支持jdk和cglib动态代理。

Spring会根据被代理的类是否有接口自动选择代理方式:

  • 如果有接口,就采用jdk动态代理
  • 如果没接口,就采用cglib的方式

在AOP中有一些核心概念,需要大家了解

* 目标对象(Target)被代理的对象* 连接点(JoinPoint)目标对象中得所有方法* 切入点(PointCut)目标对象中得要进行功能增强那部分方法* 增强 (Advice 通知)一个具体增强功能(增强对象  增强方法)* 切面 (Aspect)切面是一种描述,描述的是: 哪个增强方法   加入到了  哪个切点  的   什么位置增强方法和切点方法的执行顺序

在这里插入图片描述

入门案例

使用SpringAop完成在业务层类中的方法上打印日志

创建模块,导入依赖

在这里插入图片描述

    <dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.6</version></dependency><!--切点表达式解析坐标--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version></dependency><!--测试--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.6</version></dependency></dependencies>

创建实体类

创建业务层接口和实现类

在这里插入图片描述

创建日志类

在这里插入图片描述

配置切面

我们需要把切面相关的内容配置在这种增强类中

在这里插入图片描述

创建配置类

在这里插入图片描述

单元测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)

在这里插入图片描述

通知类型

四大通知

四大通知描述的就是增强方法在切点方法的什么位置上执行

- 前置通知(before):增强方法在切点运行之前执行
- 返回后通知(after-returning):增强方法在某切点正常完成后执行的通知,不包括抛出异常的情况
- 异常后通知(after-throwing):增强方法在某切点抛出异常退出时执行的通知
- 后置通知(after):增强方法在某切点退出的时候执行的通知(不论是正常返回还是异常退出)
try{前置通知(before)//切点执行位置返回后通知(after-returning)
}catch(Execption e){异常后通知(after-throwing)
}finally{后置通知(after)
}

① 添加通知方法

在这里插入图片描述

② 测试

在这里插入图片描述

环绕通知

它是一种特殊的通知,他允许以编码的形式实现四大通知

① 添加通知方法

在这里插入图片描述

② 测试

在这里插入图片描述

切点表达式

切点表达式用于挑选切点

execution

execution() :指定一组规则来匹配某些类中的方法,匹配中的就是切点

* 代表一个或多个位置
.. 代表0个或多个位置

在这里插入图片描述

@annotation

@annotation:指定一个注解,凡是标有此注解的方法都是切点

① 自定义注解

在这里插入图片描述

② 在需要作为切点的方法上添加注解
在这里插入图片描述

③ 设置切点表达式

在这里插入图片描述

④ 测试

在这里插入图片描述

记录日志详情

在这里插入图片描述

事务管理

事务回顾

事务是一组操作的集合,它是一个不可分割的工作单位,这些操作 要么同时成功,要么同时失败。

开启事务(一组操作开始前,开启事务):start transaction / begin ;提交事务(这组操作全部成功后,提交事务):commit ;回滚事务(中间任何一个操作出现异常,回滚事务):rollback ;

转账案例

添加账户表

-- 创建账户表,并且添加两条测试数据
create table account (id int primary key auto_increment,name varchar(32),balance float
);
insert into account (name, balance) VALUES ('A', 1000), ('B', 1000);

导入工程

在这里插入图片描述

添加持久层代码

在Mapper接口中添加加钱和减钱的方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

添加业务层代码

在Service接口和实现类中添加转账方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

测试

在测试类中添加转账方法

在这里插入图片描述

事务管理

注解:@Transactional

位置:业务(service)层的方法上、类上、接口上

作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务

配置事务管理器

在配置类中配置事务管理器,并开启声明式事务

在这里插入图片描述

添加事务注解

在转账方法上添加@Transactional注解,注意方法中要模拟异常

在这里插入图片描述

测试

在测试方法中对转账方法进行测试

在这里插入图片描述

事务属性

rollbackFor

默认情况下,只有出现 RuntimeException 才回滚异常,rollbackFor属性用于控制让非运行时异常也回滚。

在这里插入图片描述

propagation

propagation称为事务传播行为,表示当一个事务方法被另一个事务方法调用时,应该如何进行事务控制

在这里插入图片描述

Spring支持通过配置的形式来实现7种事务传播行为,我们需要掌握其中的前两种

属性值含义
REQUIRED【默认值】需要事务,有则加入,无则创建新事务
REQUIRES_NEW需要新事务,无论有无,总是创建新事务
SUPPORTS支持事务,有则加入,无则在无事务状态中运行
NOT_SUPPORTED不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务
MANDATORY必须有事务,否则抛异常
NEVER必须没事务,否则抛异常
NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则开启一个新的事务。
内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。
而内层事务操作失败并不会引起外层事务的回滚。

建新事务 |
| REQUIRES_NEW | 需要新事务,无论有无,总是创建新事务 |
| SUPPORTS | 支持事务,有则加入,无则在无事务状态中运行 |
| NOT_SUPPORTED | 不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务 |
| MANDATORY | 必须有事务,否则抛异常 |
| NEVER | 必须没事务,否则抛异常 |
| NESTED | 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则开启一个新的事务。
内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。
而内层事务操作失败并不会引起外层事务的回滚。 |


http://www.mrgr.cn/news/49466.html

相关文章:

  • 【LeetCode热题100】分治-快排
  • 刷c语言练习题8(牛客网)
  • BWA -A -B -O -E参数控制比对得分
  • java项目之精准扶贫管理系统源码(springboot+mysql+vue)
  • AI开发-三方库-Hugging Face-Pipelines
  • 【C++】--内存管理
  • 视频剪辑必备!优质视频素材获取途径
  • 数据结构与算法——Java实现 35.求数据流中的第K大元素
  • 攻防世界2
  • 安全可靠测评结果公告(2024年第1号)
  • 常见开源组件的详解
  • ```python
  • 文本生成视频技术:艺术与科学的交汇点
  • RHCSA的学习(5)
  • 3D医学影像开发入门<二>:VS2019+Qt5.15.2+VTK9.3.1编译及环境配置
  • Gin入门指南:从零开始快速掌握Go Web框架Gin
  • 【10月13日晴】A股下周趋势分析
  • MongoDB的基本内容和应用场景介绍
  • pip安装指定版本的tensorflow
  • 卡尔曼滤波器原理介绍