AOP实现事务和记录日志
AOP (Aspect Oriented Programming)
将非功能性需求从功能性需求中剥离出来,解耦并且解决代码复用的问题,比如说权限控制,事务控制,记录操作日志,全局捕获异常等
@Aspect 切面
@PointCut 描述在哪些类哪些方法织入代码
@Advice 在方法的什么执行时机(之前或者之后)去执行
Advice分为5种
@Before,前置通知
@After(finally) 后置通知,方法执行完后
@AfterReturning,返回通知,方法成功执行之后
@AfterThrowing,异常通知,发生异常之后
@Around,环绕通知
AOP实现的方式有静态代理和动态代理
动态代理实现分为两种:基于接口和基于继承
基于接口的是JDK代理,基于继承的是Gglib代理
Spring中的代理可以使用配置项指定采用哪一种
添加AOP依赖后,编写切面
@Aspect
@Component
public class AdviceAspectConfig { /**
* 每种通配符表示的含义: | *表示任意字符 | ..表示本包和子包 或者是任意参数 |
* 切入点:修饰符是public ,返回值任意类型, service包和他的子包,以Service结尾的类,任意的方法
*/
@Pointcut("execution(public * com.irish.service..*Service.*(..))")
public void matchServiceMethod(){} /**
* 可以在方法执行的前后添加非功能性的代码
*/
@Around("matchServiceMethod()")
public java.lang.Object after(ProceedingJoinPoint joinPoint){
System.out.println("###before");
java.lang.Object result = null;
try{
System.out.println("方法参数:");
java.lang.Object[] args = joinPoint.getArgs();
System.out.println("被代理的对象"+joinPoint.getTarget());
result = joinPoint.proceed(joinPoint.getArgs());
System.out.println("###after returning");
}catch (Throwable e){
System.out.println("###after throwing");
e.printStackTrace(); }finally {
System.out.println("###finally");
}
return result;
} }
(1)编程式事务,手动开启事务,提交事务,回滚事务
@Service
public class UserServiceImpl implements UserService { @Autowired
private UserDao userDao; @Autowired
private TransactionUtils transactionUtils; public void add() {
TransactionStatus transactionStatus = null;
try {
// 开启事务
transactionStatus = transactionUtils.begin();
userDao.add(, "password001","test001");
//System.out.println("开始报错啦!@!!");
//int i = 1 / 0;
System.out.println("################");
userDao.add(, "password002","test002");
// 提交事务
if (transactionStatus != null)
transactionUtils.commit(transactionStatus);
} catch (Exception e) {
System.out.println(e.getMessage());
// 回滚事务
if (transactionStatus != null)
transactionUtils.rollback(transactionStatus);
}
} }
项目结构:

(2)记录操作日志
对dao包的所有以save开头和以delete开头的方法,进行拦截处理,生成操作日志,记录在mongodb中
1 引入依赖jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.</modelVersion> <groupId>com.irish.aop</groupId>
<artifactId>datalog</artifactId>
<version>0.0.-SNAPSHOT</version>
<packaging>jar</packaging> <name>datalog</name>
<description>datalog</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5..RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency> <dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
2 编写切面
@Aspect
@Component
public class DatalogAspect { private static final Logger logger = LoggerFactory.getLogger(DatalogAspect.class); @Autowired
ActionDao actionDao; @Pointcut("execution(public * com.irish.aop.dao.*.save*(..))")
public void save(){ } @Pointcut("execution(public * com.irish.aop.dao.*.delete*(..))")
public void delete(){ } /**
* 1\判断是什么类型的操作,增加\删除\还是更新
* 增加/更新 save(Product),通过id区分是增加还是更新
* 删除delete(id)
* 2\获取changeitem
* (1)新增操作,before直接获取,after记录下新增之后的id
* (2)更新操作,before获取操作之前的记录,after获取操作之后的记录,然后diff
* (3)删除操作,before根据id取记录
* 3\保存操作记录
* actionType
* objectId
* objectClass
* @param pjp
* @return
* @throws Throwable
*/
@Around("save() || delete()")
public Object addOperateLog(ProceedingJoinPoint pjp) throws Throwable {
Object returnObj = null; //TODO BEFORE OPERATION init action
String method = pjp.getSignature().getName();
ActionType actionType = null;
Action action = new Action();
Long id = null;
Object oldObj = null;
try{ if("save".equals(method)){
//insert or update
Object obj = pjp.getArgs()[];
try{
id = Long.valueOf(PropertyUtils.getProperty(obj,"id").toString());
}catch (Exception e){
//ignore
}
if(id == null){
actionType = ActionType.INSERT;
List<ChangeItem> changeItems = DiffUtil.getInsertChangeItems(obj);
action.getChanges().addAll(changeItems);
action.setObjectClass(obj.getClass().getName());
}else{
actionType = ActionType.UPDATE;
action.setObjectId(id);
//pjp.getTarget()被代理的对象,即ProductDao
oldObj = DiffUtil.getObjectById(pjp.getTarget(),id);
action.setObjectClass(oldObj.getClass().getName());
} }else if("delete".equals(method)){
id = Long.valueOf(pjp.getArgs()[].toString());
actionType = ActionType.DELETE;
oldObj = DiffUtil.getObjectById(pjp.getTarget(),id);
ChangeItem changeItem = DiffUtil.getDeleteChangeItem(oldObj);
action.getChanges().add(changeItem);
action.setObjectId(Long.valueOf(pjp.getArgs()[].toString()));
action.setObjectClass(oldObj.getClass().getName());
} returnObj = pjp.proceed(pjp.getArgs()); //TODO AFTER OPERATION save action
action.setActionType(actionType);
if(ActionType.INSERT == actionType){
//new id
Object newId = PropertyUtils.getProperty(returnObj,"id");
action.setObjectId(Long.valueOf(newId.toString())); }else if(ActionType.UPDATE == actionType){
//pjp.getTarget()获取的是被代理的对象 【目标对象(target object):被代理对象】
//在保存方法之后,所以这时候获取的是新值
Object newObj = DiffUtil.getObjectById(pjp.getTarget(),id);
List<ChangeItem> changeItems = DiffUtil.getChangeItems(oldObj,newObj);
action.getChanges().addAll(changeItems);
} action.setOperator("admin"); //dynamic get from threadlocal/session
action.setOperateTime(new Date()); actionDao.save(action); }catch (Exception e){
logger.error(e.getMessage(),e);
} return returnObj;
}
}
项目结构

(3)在springboot项目中使用注解事务
spring集成的事务只是对事务的管理,真正实现事务的是持久层框架,如jdbcTemplate ,hibernate等
测试时候注意mysql的版本要是5.5以上,数据库引擎是InnoDB
在一张表中设置一个字段是唯一约束,插入相同数据时,这时候要么两张表都插入失败,保证了事务的原子性
项目结构

1 @Transactional只能被应用到public方法上,对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能
@Transactional annotations only work on public methods. If you have a private or protected method with this annotation there’s no (easy) way for Spring AOP to see the annotation. It doesn’t go crazy trying to find them so make sure all of your annotated methods are public.
2 默认情况下,一个有事务的方法 遇到RuntimeException 时会回滚 . 但是遇到检查异常 是不会回滚的,要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常})
(4)自定义注解实现事务
思路:注解版本的事务其实是AOP+编程式事务,
1定义注解 @interface ExtTransaction
2在AOP切入点中配置要扫描的类及方法
3在Around通知中,判断方法是否有自定义注解,有就开启事务,方法执行完提交事务,发生异常回滚事务
@Aspect
@Component
public class AopExtTransaction {
// 一个事务实例子 针对一个事务
@Autowired
private TransactionUtils transactionUtils; // 环绕通知 在方法之前和之后处理事情
@Around("execution( * com.irish.service.*.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object result = null;
try {
// 1.获取该方法上是否加上注解
ExtTransaction extTransaction = getMethodExtTransaction(pjp);
TransactionStatus transactionStatus = begin(extTransaction);
// 2.调用目标代理对象方法
result = pjp.proceed();
// 3.判断该方法上是否就上注解
commit(transactionStatus); }catch(Exception e) {
//4发生异常回滚
transactionUtils.rollback();
}
return result;
} private TransactionStatus begin(ExtTransaction extTransaction) {
if (extTransaction == null) {
return null;
}
// 2.如果存在事务注解,开启事务
return transactionUtils.begin();
} private void commit(TransactionStatus transactionStatus) {
if (transactionStatus != null) {
// 5.如果存在注解,提交事务
transactionUtils.commit(transactionStatus);
} } // 获取方法上是否存在事务注解
private ExtTransaction getMethodExtTransaction(ProceedingJoinPoint pjp)
throws NoSuchMethodException, SecurityException {
String methodName = pjp.getSignature().getName();
// 获取目标对象
Class<?> classTarget = pjp.getTarget().getClass();
// 获取目标对象类型
Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
// 获取目标对象方法
Method objMethod = classTarget.getMethod(methodName, par);
ExtTransaction extTransaction = objMethod.getDeclaredAnnotation(ExtTransaction.class);
return extTransaction;
} }
项目结构:

github地址:https://github.com/jake1263/AOP
AOP实现事务和记录日志的更多相关文章
- spring事务:事务控制方式,使用AOP控制事务,七种事务传播行为,声明事务,模板对象,模板对象原理分析
知识点梳理 课堂讲义 1)事务回顾 1.1)什么是事务-视频01 事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败. 1.2)事务的作用 事务特征(ACID) 原子 ...
- Spring基于AOP的事务管理
Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...
- Spring整合JDBC以及AOP管理事务
本节内容: Spring整合JDBC Spring中的AOP管理事务 一.Spring整合JDBC Spring框架永远是一个容器,Spring整合JDBC其实就是Spring提供了一个对象,这个对象 ...
- Spring AOP和事务的相关陷阱
1.前言 2.嵌套方法拦截失效 2.1 问题场景 2.2 解决方案 2.3 原因分析 2.3.1 原理 2.3.2 源代码分析 3.Spring事务在多线程环境下失效 3.1 问题场景 3.2 解决方 ...
- 关于spring 事务 和 AOP 管理事务和打印日志问题
关于spring 事务 和 AOP 管理事务和打印日志问题 1. 就是支持事务注解的(@Transactional) . 可以在server层总使用@Transactional,进行方法内的事务管 ...
- Spring MVC 中使用AOP 进行事务管理--XML配置实现
1.今天写一篇使用AOP进行事务管理的示例,关于事务首先需要了解以下几点 (1)事务的特性 原子性(Atomicity):事务是一个原子操作,由一系列动作组成.事务的原子性确保动作要么全部完成,要么完 ...
- spring AOP 实现事务和主从读写分离
1 切面 是个类 2 切入点 3 连接点 4 通知 是个方法 5 配置文件 <?xml version="1.0" encoding="UTF-8"?&g ...
- Spring学习之AOP与事务
一.概述 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续, ...
- Spring 的 AOP 进行事务管理的一些问题
AspectJ AOP事务属性的配置(隔离级别.传播行为等): <tx:advice id="myAdvice" transaction-manager="mtTx ...
随机推荐
- 微信硬件平台(十) 1 ESP8266通过mqtt交互消息
//----------------------------------------------------------------------------------------// //----- ...
- 2019-2020-1 20199302《Linux内核原理与分析》第十一周作业
缓冲区溢出 缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段.这一漏洞的出现是由于数据缓冲器和返回地址的暂时关闭,溢 ...
- BZOJ 1758: [Wc2010]重建计划 01分数规划+点分治+单调队列
code: #include <bits/stdc++.h> using namespace std; #define setIO(s) freopen(s".in", ...
- CLR 调试体系结构
公共语言运行时 (CLR) 调试 API 专门用作操作系统内核的一部分. 在非托管代码中,当程序生成异常时,内核将暂停执行进程,并使用 Win32 调试 API 将异常信息传递给调试器. CLR 调试 ...
- Xilinx ISE中使用Synplify综合报错的原因
在Xilinx ISE中使用Synopsys Synplify 综合比较方便,但有时会出现如下错误: "ERROR:NgdBuild: - logical block ' ' with ty ...
- 【03NOIP普及组】麦森数(信息学奥赛一本通 1925)(洛谷 1045)
[题目描述] 形如2P-1的素数称为麦森数,这时P一定也是个素数.但反过来不一定,即如果P是个素数,2P-1不一定也是素数.到1998年底,人们已找到了37个麦森数.最大的一个是P=3021377,它 ...
- 【LA 3942】 Remember the word
题意 给定一个字符串和若干个单词,询问能把字符串分解成这些单词的方案数.比如abcd ,有单词a,b,ab,cd:就可以分解成a+b+cd或者ab+cd. 分析 trie树—>DP 代码 (感谢 ...
- Java中定义不了可变长数组怎么办---集合 泛型
一.集合(Collections) Java使用集合来组织和管理对象. 1.Java的集合类 集合类主要负责保存.盛装和管理对象,因此集合类也被称为容器类. 集合类分为Set.List.Map和Que ...
- python 安装离线库
(起因:报错找不到一个module,百度也找不到这个module,机智如我找宁博翻墙看怎么解决,毕竟是歪果仁的代码嘛,果真就在git找到了这个module哈哈哈哈机智如我!) 方法: 进入命令行窗口, ...
- [Beta]第三次 Scrum Meeting
[Beta]第三次 Scrum Meeting 写在前面 会议时间 会议时长 会议地点 2019/5/7 22:00 30min 大运村公寓6F寝室 附Github仓库:WEDO 例会照片 工作情况总 ...