SpringAOP介绍
什么是AOP
AOP:Aspect Oriented Programming,面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
OOP引入封装、继承、多态等概念来建立一种对象层次结构
AOP则利用一种称为“横切”的技术,剖开对象内部,并将公共行为封装到可重用模块,从而减少重复代码,降低耦合。
AOP的基本概念
- (1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
- (2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
- (3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
- (4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
- (5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子
用通俗的语言来描述AOP术语: 切面是指封装横切到系统功能的类,包含通知和切点 切点定义切面插入在哪些方法上,确定切面使用范围 通知定义了切点处所要执行的程序代码以及执行时机 连接点是在应用执行过程中满足切点范围的具体的点 织入是把切面插入到目标对象上
通知方法
前置通知,使用aop:before标签,在方法之前执行
后置通知,使用aop:after标签,在方法之后执行,无论方法内部是否抛出异常
后置返回通知,使用aop:after-returning标签,在方法之后执行并且方法内部不能抛出异常
后置异常通知,使用aop:after-throwing标签,在方法内部抛出异常时执行
环绕通知,使用aop:around标签,在方法之前和之后都执行
Spring AOP的两种实现方式
通过Spring API实现AOP
引入依赖
使用Spring自带的AOP
Spring API相关接口
前置通知:MethodBeforeAdvice
后置通知:AfterReturningAdvice
环绕通知:MethodInterceptor
异常抛出通知:ThrowAdvice
创建一个service类
创建applicationContext.xml
执行方法
执行结果
使用注解实现
执行结果
Spring AOP实现原理
Spring AOP是通过动态代理来实现,主要是JDK动态代理和CGLIB代理两种代理模式
JDK动态代理:Spring AOP的首选方法。 每当目标对象实现一个接口时,就会使用JDK动态代理。目标对象必须实现接口
CGLIB代理:如果目标对象没有实现接口,则可以使用CGLIB代理。
代理模式
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介
JDK动态代理
优点
JDK动态代理是JDK原生的,不需要任何依赖即可使用;
通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快;
缺点
如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理;
JDK动态代理无法为没有在接口中定义的方法实现代理,假设我们有一个实现了接口的类,我们为它的一个不属于接口中的方法配置了切面,Spring仍然会使用JDK的动态代理,但是由于配置了切面的方法不属于接口,为这个方法配置的切面将不会被织入。
JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低;
CGLIB代理
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好的补充。
事务
1.事务的概念
事务是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作,这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合。
2.事务的基本事务属性
传播行为(propagation behavior):当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。Spring定义了七种传播行为
隔离级别(isolation level):定义了一个事务可能受其他并发事务的影响程度。多个事务并发运行,经常会操作相同的数据来完成各自的任务,可能会出现脏读,不可重复读和幻读的问题。隔离级别有四种
是否只读(isReadOnly):如果一个方法内都是对数据库的select操作,那么可以设置方法事务为只读,数据库也会对该事务进行特定的优化。只读事务内不能有insert、update、delete的操作事务超时(timeout(默认是False)
):事务可能设计对后端数据库的锁定,所以长时间的事务运行会不必要的占用数据库资源,设置事务超时时间可以及时释放资源
3.事务的ACID原则
事务具有4个基本特性:原子性、一致性、隔离性、持久性。也就是我们常说的ACID原则。
原子性
一个事务已经是一个不可再分割的工作单位。事务中的全部操作要么都做;要么都不做
一致性
事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
隔离性
事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。
持久性
一个事务一旦提交,它对数据库中数据的改变会永久存储起来。其他操作不会对它产生影响
4.Spring事务API介绍
(1) PlatformTransactionManager【事务平台管理器】
-
是一个接口,定义了获取事务【getTransaction】、提交事务【commit】、回滚事务【rollback】的接口
(2)TransactionDefinition【事务定义信息】
-
是一个接口,定义了事务隔离级别、事务传播行为、事务超时时间、事务是否只读
5.事务管理级别
(1)为什么要事务隔离
脏读
在一个事务中读取到了另外一个事务修改的【未提交的数据】,而导致多次读取同一个数据返回的结果不一致 (必须要解决的)
不可重复读
在一个事务中读取到了另外一个事务修改的【已提交的数据】,而导致多次读取同一个数据返回的结果不一致
幻读
一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录
(2)Spring事务隔离级别
隔离级别由低到高【读未提交】=>【读已提交】=>【可重复读】=>【序列化操作】
6.事务传播行为
指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
7.事物的管理方式
编程式事务管理 :编程式事务管理是通过编写代码实现的事务管理。可以根据需求规定事务从哪里开始,到哪里结束,拥有很高的灵活性。但是这种方式,会使业务代码与事务规则高度耦合,难以维护,因此我们很少使用这种方式对事务进行管理。
声明式事务管理: 采用声明的方式来处理事务,可以通过 2 种方式实现,分别是 XML和注解方式。Spring 在进行声明式事务管理时,底层使用了 AOP 。事务管理不侵入开发的组件。
具体实现(模拟银行转账业务)
1.创建一个数据库
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `account`
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(11) COLLATE utf8mb4_general_ci NOT NULL,
`money` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('1', 'cmf', '2000.00');
INSERT INTO `account` VALUES ('2', 'lht', '1000.00');
2.导入相关依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
</dependencies>
3.创建一个实体类
4.创建Dao
5.创建一个Dao的实现类
6.创建一个Service
7.创建一个Service的实现类
8.编写jdbc.properties
9.基于注解方式
(1)Spring配置类SpringConfig
(2)配置类JdbcConfig
(3)业务层实现类修改代码
(4)测试
结果
扩展
只需要在Service实现类中做些变更,不用在JdbcConfig配置类中配置事务管理器,也可以实现事务管理
除了通过注解的方式,还可以通过XML的的方式实现事务的管理,如下配置