Spring - AOP

  • 1.环境准备(省略导包)
    • 0.Pom
    • 1.创建数据库/表
    • 2.entity
    • 3.mapper
    • 4.execption
    • 5.service
    • 6.xml (cn.kgc.mapper)
    • 7.applicationContext.xml
    • 8.mybatis-config.xml
    • 9.TestBuy
  • 2.企业级常用版A-注解实现事务
    • 2.1 applicationContext.xml
    • 2.2 BuyComputerServiceImpl (第一种)
    • 2.3 BuyComputerServiceImpl (第二种)
  • 3.特殊项目复杂版B-去除事务注解版
    • 3.0 引用:
    • 3.1 BuyComputerServiceImpl
    • 3.2 applicationContext.xml

1.环境准备(省略导包)

0.Pom

<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.5.RELEASE</version></dependency>

==》第一个依赖是针对下面的 B事务的

  <!--1.spring的依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.5.RELEASE</version></dependency><!--2.spring事务的依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.5.RELEASE</version></dependency><!--3.mybatis的依赖--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.1</version></dependency><!--4.mybatis集成spring的依赖--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.1</version></dependency><!--5.mysql的依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.36</version>
</dependency>

这边虚拟机的数据库是5版本的。

    <!--6.连接池的依赖--><!--连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.12</version></dependency><!--7.junit的依赖--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version><scope>provided</scope></dependency></dependencies>

1.创建数据库/表

基于Linux系统的虚拟机

CREATE TABLE `t_computer` (`id` int(32) NOT NULL AUTO_INCREMENT,`cname` varchar(32) DEFAULT NULL,`cmount` int(32) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `t_store` (`id` int(32) NOT NULL AUTO_INCREMENT,`cid` int(32) DEFAULT NULL,`c_num` int(32) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;insert into t_computer(id,cname,cmount) values(111,'华为',100);

2.entity

(1)Computer

@Getter
@Setter
public class Computer {private Integer id;private String name;private Integer cmount;
}

(2)Store

@Getter
@Setter
public class Store {private Integer id;private Integer cid;private Integer cNum;
}

3.mapper

(1) ComputerMapper

public interface ComputerMapper {//更新Computer表Integer updateComputer(Computer computer);//查询Computer表Computer findComputer(Integer id);
}

(2) StoreMapper

public interface StoreMapper {Integer insertStore(Store store);
}

4.execption

public class NotEnoughException extends RuntimeException {public NotEnoughException() { }public NotEnoughException(String message) {super(message);}
}

5.service

(1)BuyComputerService

public interface BuyComputerService {//购买电脑void buy(Integer computerId,Integer nums);
}

(2)BuyComputerServiceImpl

@Service("BuyComputerService")
public class BuyComputerServiceImpl implements BuyComputerService {@Autowiredprivate StoreMapper storeMapper;@Autowiredprivate ComputerMapper computerMapper;@Overridepublic void buy(Integer computerId, Integer nums) {//1.更新库存表storeStore store = new Store();store.setCid(computerId);// 购买什么电脑store.setCNum(nums);// 购买几台电脑storeMapper.insertStore(store);//2.校验参数//通过电脑编号查询电脑对象Computer computer = computerMapper.findComputer(computerId);if (computer == null){//判断如果没有该电脑,报异常,抛电脑不存在异常throw new NullPointerException("编号是:" + computerId + ",电脑不存在");}else if(computer.getCmount() < nums ){//判断如果购买的电脑的数量大于实际的库存数,则抛出异常,抛电脑库存不足throw new NotEnoughException("编号是:" + computerId + ",电脑库存不足");}//2.更新电脑表数据computerComputer c = new Computer();c.setId(computerId);c.setCmount(nums);computerMapper.updateComputer(c);}
}

注意点:
@Autowired
有这个注释就省去了构造方法或者get,set方法来设置xxx属性

6.xml (cn.kgc.mapper)

(1)ComputerMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.kgc.mapper.ComputerMapper"><!--实时更新,修改原库存:当前数据库现有的库存 扣除购买的电脑数量--><!-- 规约 --><update id="updateComputer" parameterType="cn.kgc.entity.Computer">update t_computer set cmount = cmount - #{cmount} where id = #{id}</update>
<!--    通过电脑的编号id查询电脑对象信息--><select id="findComputer" parameterType="java.lang.Integer" resultType="cn.kgc.entity.Computer">select * from t_computer where id = #{id}</select>
</mapper>

(2)StoreMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.kgc.mapper.StoreMapper">
<!--    每购买一台电脑。会在t_score表中,更新一条新的记录--><insert id="insertStore" parameterType="cn.kgc.entity.Store">insert into t_store(cid,c_num) values (#{cid},#{cNum})</insert>
</mapper>

7.applicationContext.xml

<!--1.配置数据源(利用Druid连接池)--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"init-method="init" destroy-method="close" ><property name="url" value="jdbc:mysql://192.168.177.132:3306/db001?useUnicode=true&amp;characterEncoding=utf-8" /><property name="username" value="root" /><property name="password" value="ok" /></bean><!--2.配置SqlSessionFactoryBean--><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="configLocation" value="classpath:mybatis-config.xml" /></bean><!--3.配置MapperFactoryBean--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="cn.kgc.mapper" /></bean><!--4.扫描器-->
<context:component-scan base-package="cn.kgc" />

8.mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><!--设置mybatis输出日志--><setting name="logImpl" value="STDOUT_LOGGING"/></settings><mappers><mapper resource="cn/kgc/mapper/ComputerMapper.xml"/><mapper resource="cn/kgc/mapper/StoreMapper.xml"/></mappers>
</configuration>

9.TestBuy

public class TestBuy {ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");BuyComputerService buyComputerService = (BuyComputerService) ac.getBean("BuyComputerService");@Testpublic void test01(){//正常传参情况下的测试buyComputerService.buy(111,10);}@Testpublic void test02(){//测试购物错误编码的电脑buyComputerService.buy(112,10);}@Testpublic void test03(){//测试购买数超过库存数buyComputerService.buy(111,1000);}
}

结论:
第一个是能有结果。

而第二个第三个虽然报错,
但是t_score表里面的值会增加,是个bug。

说明:
需要做到报错就不会有变化,这时候就需要用到事务了。

2.企业级常用版A-注解实现事务

2.1 applicationContext.xml

第一步 声明事务管理器对象

set 注入 引入事务的机制
配置事务管理器,事务依赖于数据源所产生的连接对象,
只有连接对象创建成功了才能够处理事务

<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean>

概念:
DataSourceTransactionManager 数据源事务管理器,
用于对单个数据源进行事务管理,通常用于JDBC事务管理或者mybatis事务管理。

第二步 开启事务注解驱动,告诉spring使用注解管理事务

去识别注解 并通过注解开启事务

注意点: 引入标签的时候注意要带有 tx

spring使用AOP机制,创建@Transactional所在的类代理,给方法加入事务的功能

(底层:在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务)

<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

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"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd">

2.2 BuyComputerServiceImpl (第一种)

 @Transactional(propagation = Propagation.REQUIRED,
//配置该属性的含义代表调用该方法默认开启事务(传播性)
//即使不写REQUIRED这句话,也是默认这个。isolation = Isolation.DEFAULT,
//配置该属性的含义代表(支持事务的隔离性)使用默认的隔离级别(多事务之间相互隔离,互不影响)readOnly = false,
//配置该属性的含义代表支持增删改的操作  默认是false
//readOnly配置为true的含义为只读 只能查,不能增删改,所以默认false
//查询没有事务,不需要事务,也不支持事务rollbackFor = {NullPointerException.class,NotEnoughException.class}
//配置该属性的含义代表如果方法中出现对应的两个异常,则该方法执行回滚操作)

说明:
1.spring事务传播机制(共有7种):

  1. REQUIRED
    支持当前事务,如果没有事务会创建一个新的事务
  2. SUPPORTS
    支持当前事务,如果没有事务的话以非事务方式执行
  3. NEVER
    以非事务方式进行,如果存在事务则抛出异常

2.rollbackFor:
表示发生制定的异常一定回滚

上面都是默认的,所以光写一个注解也行,就是第二种方法。

2.3 BuyComputerServiceImpl (第二种)

第二种就运用了AOP特性

2.2 中的事务注解在企业中会替换成
@Transactional(企业中一般都使用默认配置)

注意点:

  1. 在哪需要事务就写在哪的方法,写在类上就是所有的都加事务,可能不太好。

  2. 只有增删改需要事务,而查询不需要。

  3. 事务只放在service层。

写法:
案例中添加在BuyComputerServiceImpl的buy方法上

@Override
@Transactional
public void buy(Integer computerId, Integer nums) { }

结果:
这时候就对了。利用的是事务机制。

引入:
一千个包都需要用事务,写的时候会很烦,所以用下面的B。

下面利用的是AOP机制 》AOP去实现事务

3.特殊项目复杂版B-去除事务注解版

假如很多包都需要事务,那么我们就可以提取出来,
做一个单独的模块,让这个模块横穿所有的小模块,
在这个模块上写事务,让横穿的那些小模块都有这个特性
用的就是AOP特性,AOP去实现事务。

3.0 引用:

原文链接: https://blog.csdn.net/z3881006/article/details/79020166

在使用spring框架配置AOP的时候,
不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点"。

例如 定义切入点表达式 execution(* com.sample.service.impl..*.*(..))

》对哪个包 哪个类需要事务就去切,然后放在切面中,就实现了事务

execution()是最常用的切点函数,其语法如下所示:

整个表达式可以分为五个部分:

  1. execution(): 表达式主体。

  2. 第一个*号:表示返回类型,*号表示所有的类型。

  3. 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。

  4. 第二个*号:表示类名,*号表示所有的类。

  5. *(…):最后这个星号表示方法名,*号表示所有的方法,
    后面括弧里面表示方法的参数,两个句点表示任何参数。

3.1 BuyComputerServiceImpl

去除 @Transactional

3.2 applicationContext.xml

基于1 环境准备中的 applicationContext.xml

<!--1.使用spring的事务处理,声明事务管理器对象-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/>
</bean><!--2.声明业务方法的事务属性 (隔离级别,传播行为,回滚操作) 即事务的通知
注意点:引入标签的时候注意要带有tx -->
<tx:advice id="myAdvice" transaction-manager="transactionManager"><!--tx:attributes:配置事务属性--><tx:attributes><!--tx:method:给具体的方法配置事务属性buy* 就是buy1 buy2 buy3都能识别 也能使用insert* update* del* --><tx:method name="buy*" propagation="REQUIRED" read-only="false" isolation="DEFAULT" rollback-for="java.lang.NullPointerException,cn.kgc.execption.NotEnoughException"/><tx:method name="select*" propagation="SUPPORTS" read-only="true"/><!--查询可以不去配  SUPPORTS 指不支持事务--></tx:attributes>
</tx:advice>
<aop:config><!--3.配置aop配置切入点表达式:指定哪些包中的类,要使用事务,id: 切入点表达式的名称,唯一值expression:切入点表达式,指定哪些类要使用事务--><aop:pointcut id="serviceStrong" expression="execution(* *..service..*.*(..))"/><!--4.配置增强器:关联advice和pointcutadvice-ref:   配置通知,配置tx:advice中的idpointcut-ref:配置pointcut-ref,配置切入点表达式的id--><aop:advisor advice-ref="myAdvice" pointcut-ref="serviceStrong"/>
</aop:config>
</beans>

----2021.12.10&12.11

Spring 4 - AOP相关推荐

  1. Spring.Net Aop

    Spring.Net 有四种通知: IMethodBeforeAdvice,IAfterReturningAdvice,IMethodInterceptor,IThrowsAdvice BeforeA ...

  2. [Spring 深度解析]第4章 Spring之AOP

    第4章 ◄Spring之AOP► 在上一章节中,我们大致了解了Spring核心容器,了解了IOC思想在Spring中的具体应用Bean容器以及Bean的配置与使用,这一章我们将开始学习AOP在Spri ...

  3. Spring对AOP的支持

     Spring对AOP的支持<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" ...

  4. Spring 容器AOP的实现原理——动态代理

    本文来自极客学院 Spring 容器AOP的实现原理--动态代理 之前写了一篇关于IOC的博客--<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大 ...

  5. Spring的AOP使用xml配置

    需要使用spring的包,大家自己全部导入进去即可.省4........ 用户管理接口 package com.rx.spring; public interface UserManager { pu ...

  6. 【SSM框架系列】Spring 的 AOP(面向切面编程)

    什么是 AOP AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP 是 OOP ...

  7. Spring实现AOP的4种方式

    Spring实现AOP的4种方式 先了解AOP的相关术语: 1.通知(Advice): 通知定义了切面是什么以及何时使用.描述了切面要完成的工作和何时需要执行这个工作. 2.连接点(Joinpoint ...

  8. Spring的AOP原理

    AOP是什么? 软件工程有一个基本原则叫做"关注点分离"(Concern Separation),通俗的理解就是不同的问题交给不同的部分去解决,每部分专注于解决自己的问题.这年头互 ...

  9. Spring中AOP相关的API及源码解析,原来AOP是这样子的

    前言 之所以写这么一篇文章主要是因为下篇文章将结束Spring启动整个流程的分析,从解析配置到创建对象再到属性注入最后再将创建好的对象初始化成为一个真正意义上的Bean.因为下篇文章会涉及到AOP,所 ...

  10. spring教程--AOP详解

    1 Spring中的AOP 1.1 Spring的传统AOP : AOP:不是由Spring定义.AOP联盟的组织定义. Spring中的通知:(增强代码) 前置通知 org.springframew ...

最新文章

  1. 你的微服务网关还只在用负载均衡吗?
  2. ajax 下拉刷新 上拉加载更多,局部刷新iscroll控件的具体使用(下拉刷新,上拉加载更多)...
  3. linux mysql 停止,linux 里 重启 和停止 mysql的原理
  4. 二分法查找和普通查找
  5. hilbert曲线序编码matlab,Hilbert曲线扫描矩阵的生成算法及其MATLAB程序代码
  6. [转]Ubuntu以管理员权限打开文件(夹)
  7. 装个discuz论坛
  8. mysql性能优化金字塔法则 下载_千金良方:MySQL性能优化金字塔法则 pdf版
  9. java csv下载_javacsv.jar
  10. 中班音乐计算机反思,幼儿园音乐活动反思10篇
  11. 一篇读懂深度学习中「训练」和「推断」的区别
  12. 几个在线的维恩图制作站点
  13. 华为项目管理金种子培训教材(资料下载)
  14. 金笛全新技术架构,鲲鹏击浪从兹始
  15. 巴塞瓦尔能量守恒定理
  16. 上位机使用python/matlab通过网线VISA/SCPI编程远程控制旧版A.06.04.32的安捷伦agilent矢量网络分析仪(VNA)采集S21参数
  17. Json字符串的转换
  18. 基于springboot的校园二手交易系统-JAVA【毕业设计、论文、源码、开题报告】
  19. 网络空间安全导论实践报告
  20. 【CG】汇总开源的三维图形/计算几何/CAD算法库

热门文章

  1. jQuery三款简约MP3播放器插件
  2. 华东理工大学2022计算机考研,2022社会学考研经验贴
  3. 毒液蛋白质相互作用分析
  4. Android 是Google开发的基于Linux平台的开源手机操作系统
  5. Sharepoint2007自定义Feature(2)--使用Sharepoint自定义Featur
  6. [总结]FFMPEG命令行工具之ffmpeg详解
  7. ffmpeg h264文件转mp4
  8. JavaScript里面的“类”
  9. 计算机网络——Ping实验
  10. SQLServer 时间段分隔,时间段查询,查询时间段内的数据