SpringAOP和Spring事物管理
作者: / 2019-08-05 / 浏览次数:

spring aop :

pointcut表达式: designators-指示器 wildcards-通配符 operators-操作符
wildcards: * -- 匹配任意数量的字符
+ -- 匹配制定类及其子类
..-- 一般用于匹配任意数的子包或参数
operator: || !
wildcards
* 匹配任意数量的字符
+ 匹配指定类及其子类
.. 一般用于匹配任意数的子包或参数











pointcut :切面表达式
designators:指示器,描述通过什么方式去匹配java的那些方法
execution:匹配方法
匹配注解
@target
@args
@within
@annotation
within:匹配包/类型
this/bean/target:匹配对象
args:匹配参数

wildcards:通配符
operators: 运算符

匹配包/类型 :
// 匹配productservice类里头的所有方法
@pointcut")
public void matchtype {
}

// 匹配com.imooc包及子包下所有类的方法
@pointcut")
public void matchpackage{}

匹配对象:
1:this:匹配aop对象的目标对象指定类型的方法;
2:target:匹配实现接口的目标对象的方法;
3:bean:匹配所有以service结尾的bean里头的方法;





























/**
public class demodao implements idao{}
*/
// 匹配aop对象的目标对象为指定类型的方法,即demodao的aop代理对象的方法
@pointcut")
public void thisdemo {}

// 匹配实现idao接口的目标对象的方法,这里即demodao的方法
@pointcut")
public void targetdemo {}

// 匹配所有以service结尾的bean里头的方法
@pointcut")
public void beandemo {}

匹配参数
// 匹配任何以find开头而且只有一个long参数的方法
@pointcut)")
public void argsdemo1 {}

// 匹配任何只有一个long参数的方法
@pointcut")
public void argsdemo2 {}

// 匹配任何find开头的而且第一个参数为long型的方法
@pointcut")
public void argsdemo3 {}

// 匹配第一个参数为long型的方法
@pointcut")
public void argsdemo4 {}

匹配注解:
1:匹配方法级别的;
2:匹配类级别的;
3:匹配参数级别的;

@annotion:匹配方法级别的注解方法;
@within:匹配标注有beta的类底下的方法;
@target:匹配标注有repository的类底下的方法;
@args:匹配传入的参数类标注有repository注解的方法;

示例 :
1: // 匹配方法标注有adminonly的注解的方法
@pointcut")
public void annodemo{}

2: // 匹配标注有beta的类地下的方法,要求的anntation的retentionpolity级别的class
@pointcut")
public void annowithdemo{}

3: // 匹配标注有repository的类地下的方法,要求的annotation的retentionpolicy级别为runtime
@pointcut")
public void annotargetdemo {}

4: //匹配传入的参数类标注有repository注解的方法;
@pointcut")
public void annoargsdemo{}

@before,表示在切点之前执行的插入的逻辑;
@aspect切面类,标注界面类;
@component,标注在类上,表示这个类交给spring托管;
execution表达式:
拦截方法:
@pointcut)");
拦截抛出的异常:
@pointcut throws java.lang.illegalaccessexception)");
//匹配任何公共方法
@pointcut)")





































































//匹配com.imooc包及子包下service类中无参方法
@pointcut)")


//匹配com.imooc包及子包下service类中的任何只有一个参数的方法
@pointcut)")


//匹配com.imooc包及子包下任何类的任何方法
@pointcut)")


//匹配com.imooc包及子包下返回值为string的任何方法
@pointcut)")


//匹配异常
execution throws java.lang.illegalaccessexception)

5种advice:
1:@before,前置通知;
2:@after,后置通知,方法执行完成之后;
3:@afterreturning,返回通知,成功执行之后执行;
4:@afterthrowing,异常通知,抛出异常之后执行
5: @around : 环绕通知

原理概述 : 织入的时机
1: 编译期
2: 类加载时
3: 运行时
运行时织入:
1:静态代理与动态代理;
2:基于接口代理与基于继承代理;

代理模式;
组成:调用者,统一的接口、真实对象、代理者;
原理:通过接口,实现这样一个过程,在调用真实对象的时候,调用者并不直接与真实对象打交道,而是通过一个代理者与真实对象通信,代理者能够负责真实
对象的非业务逻辑,如日志管理、访问控制 、异常处理等,使得真实对象专注于业务逻辑的实现,不受非业务逻辑的干扰。
代理对象会把正真的业务逻辑委托给实际对象 而代理对象只会进行一写边缘的附加操作 这就是aop的实现原理.
代理对象调用目标对象的方法产生的异常需要抛出去,不能处理,因为代理对象的作用是对目标对象进行增强,不会对目标对象进行改变.
























静态代理就是一个代理类根据被代理类的情况,被代理类有几个方法,代理类就需要有几个方法,每个方法都要对被代理类进行代理,这样会出现代码重复的情况,
如多个被代理方法其实前后需要执行的逻辑是一样的,但是静态代理还是需要为每个方法写一个代理方法,造成代码重复。动态代理根据放射得到被代理类执行的方
法和参数,避免了代码的重复。
静态代理的代码,静态代理的弊端:代理的对象越多.
jdk实现要点:
1类:java.lang.reflect.proxy;
2接口:invoctionhandler;
3只能基于接口进行动态代理.
动态代理实现时,需要的接口,invocationhandler接口。
注意:在捕获异常之后,执行插入程序,然后还需要将异常在catch代码块内抛出去!

jdk运行期动态代理源码解析:其实就是真实类实现一个接口,我们再写一个类似于切面的类,实现invocationhandler接口且实现invoke方法,同时要保存真实类对象,
初始化时赋值对象,invoke方法中反射方式调用真是对象方法,在方法前后可以加上定制的逻辑,这个方法其实是动态代理对象调用的,动态代理对象是客户端通过动
态代理类实例化的,而动态代理类是真实对象方法执行前的运行期生成的.class类,这个类实现了和真实对象一样的接口,所以也有真实对象的方法,调用代理对象方
法时也就可以传入参数,然后代理对象再将方法和参数传递给invocationhandler的实例对象。















通过system.setproperties可以设置保存jdk动态代理生成的字节码文件.

jdk与cglib代理对比:
1:jdk只能针对有接口的类的接口方法进行动态代理;
2:cglib基于继承来实现代理,无法对static、final类进行代理;
3:cglib基于继承来实现代理,无法对private、static方法进行代理。

cglib实现:
1:生成指定类对象的子类,也就是重写类中的业务函数。
2:执行回调函数,加入intercept函数。
3:创建这个类的子类对象。
-----------------------------------------
反射技术实现;
methodproxy.invokesuper;
jdk通过接口代理,所以只能代理实现接口的类的方法,而cglib通过继承实现代理,所以不能代理final类,也无法代理private和static方法.
无法对final类进行代理,因为final类不能被继承
关于cglib无法对static类和方法进行代理:
单一的类是没有static修饰符的,只有静态类内部可以用static修饰;
对于static方法,它是属于类的,子类在不重写的情况下,是可以调用的,但是一旦重写了就无法调用了,普通的public方法可以通过super.method调用,但是
static方法不行.

spring如何创建代理bean?
jdk动态代理与cglib代理是如何选用的?
-----------------------------
1:如果目标对象实现了接口,则默认采用jdk动态代理;
2:如果目标对象没有实现接口,则采用cglib进行动态代理;
3:如果目标对象实现了接口,且强制cglib代理,则使用cglib代理;

继承jparepository对数据操作实现自己的事务控制,@transactional会在子事务外层加一层事务控制,对事务整体进行控制,在方法执行前后判断事务需要进行回滚操作。
springboot对缓存的一个控制:
1、引入依赖:
dependency
groupid org.springframework.boot /groupid
artifactid spring-boot-starter-cache /artifactid
/dependency
2、使用注解:
@cacheable
public list string getmenulist{
system.out.println;
return arrays.aslist;
}

使用springaop的注意事项/坑
1: 不宜把重要的业务逻辑放到aop中处理;
2: 无法拦截static,final方法,private方法;
3: 无法拦截内部方法调用.

实现aop的方法被方法内部调用时是不会走aop的织入逻辑的,因为内部调用aop方法的调用对象是this,而this不是增强的代理对象,所以不会走织入代码,解决方法
就是用applicationcontent获取到增强的代理对象的bean,然后用这个bean来执行aop方法,就可以走织入的代码逻辑了.
spring aop 的坑,无法拦截方法内部调用,因为内部调用是this调用的,而不是代理类调用的.
无法拦截内部调用的原因:aop通过代理实现的,而省略的this是指该对象,而不是代理后的对象
解决aop无法内部调用的问题:通过spring的applicationcontext获取该类的对象,就是代理对象,使用代理对象去调用内部方法.
解决方法:通过applicationcontext来获取代理bean,通过代理bean调用方法。



















































课程总结:

1.面向切面编程是对面向对象编程的补充,主要用于日志记录,权限验证,事务配置等功能。

2.使用aspectj实现aop,aspectj是一个面向切面的框架,它扩展了java语言。

3.主要注解:

@aspect 标注说明java类是切面配置的类 由@pointcut和@advice组成

@pointcut 描述在哪些类的哪些方法植入代码

@advice表达在pointcut表达式的什么时间执行

4. pointcut中的通配符,运算符,指示器

通配符:* 匹配任意数量的字符 +匹配指定类及其子类 ..一般用于匹配任意数的子包或参数

运算符: 与操作符 || 或操作符 !非操作符

指示器:

a. @within 匹配包/类型

@pointcut) //匹配productservice类里的所有方法

@pointcut") //匹配com.imooc包及子包下所有类的方法

b. execution 表达式:方法的修饰符 返回值 包名.类名.方法

@pointcut)")//表示com.imooc.service包下以service字符结尾的类中任意参数的所有方法

5.advice 注明要在哪个切点执行什么操作,有几种方式

@before //意思是在切点执行前执行

@after //意思是在切点执行前执行

@around //表明在切点方法的前后都要执行该处理器方法

spring事务 :
spring事务管理接口:
platformtransactionmanager:事务管理器
transactiondefinition:事务定义信息
transactionstatus:事务具体运行状态

事务管理器platformtransactionmanager
spring为不同的持久化框架提供了不同platformtransactionmanager接口实现;
org.springframework.jdbc.datasource.datasourcetransactionmanager : 使用spring jdbc或ibatis进行持久化数据时使用.
org.springframework.orm.hibernate3.hibernatetransactionmanager : 使用hibernate3.0版本进行持久化数据时使用.
org.springframework.orm.jpa.jpatransactionmanager : 使用jpa进行持久化时使用
org.springframework.jdo.jdotransactionmanager : 当持久化机制是jdo时使用
org.springframework.transaction.jta.jtatransactionmanager : 使用一个jta实现管理事务,在一个事务跨越多个资源时必须使用.
transactiondefinition定义事务隔离级别
如果不考虑隔离性,会引发如下的安全问题:
1.脏读。
一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
2.不可重复读。
在同一个事务中,多次读取同一数据返回的结果有所不同。
3.幻读。
一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。
























隔离级别的出现就是为了解决以上问题的。


数据库提供的事务的隔离级别:
1.read_uncommited;
允许你读取还未提交的改变了的数据,可能导致脏,幻,不可重复读。
2.read_comminted:
允许在并发事务已经提交后读取,可防止脏读,但幻读和不可重复读还是有可能发生。
3.repeatable_read:
对相同字段的多次读取是一致的,除非数据被事务本身改变,可防止脏读,不可重复读,但幻读仍有可能出现。
4.serilizable:
完全服从acid的隔离级别,确保不发生脏读,幻读,不可重复读,这在所有的隔离级别中是最慢的,它是典型的完全通过锁定在事务中涉及的数据表来完成的。









除了以上的数据库提供的事务隔离级别,spring提供了default隔离级别,该级别表示spring使用后端数据库默认的隔离级别。

mysql默认事务隔离级别:repatable_read
oracle默认:read_committed

propagation_required:a存在事务,则b与a同一事务,如果a没有事务,则b新起事务
propagation_supported:a存在事务,则b与a同事务,如果a没有事务,则b也没有事务
propagation_mondatary:a存在事务,则b与a同事务,如果a没有事务,则抛异常
propagation_required_new:如果a存在事务,b新起事务
propagation_not_supported:如果a存在事务,b不已事务运行
propagation_never:如果出现事务,直接抛出异常
propagation_nested:a事务运行结束后,会有保存点,这边可以自定事务,b出错,a可以回滚或者就到a的保存点

transactiondefinition定义事务之事务的传播行为 br
br
事务的传播行为有七种,这七种行为可以分为三类,图中前三个分为一类和bbb操作在同一事务里),中间三个为一类和bbb操作在不同一事务里),最后为一类发生异常,将回滚到保存点或初始状态)。 br
br
重点记住propagation_required, propagation_requires_new, propagation_nested

相关代码:
//package cn.muke.spring.demo1;
@accountdao.java
/**
* 转账案例的dao层的接口
*/
public interface accountdao {
/**
* out :转出账号
* money:转出金额
*/
public void outmoney;

/**
* in :转入账号
* money:转入金额
*/
public void inmoney;
}




































@accountservice.java
/**
* 转账案例的业务层接口
*/
public interface accountservice {
/**
* out :转出账号
* in :转入账号
* money:转账金额
*/
public void transfer;
}












@accountdaoimpl.java
/**
* 转账案例的dao层的实现类
*/
public class accountdaoimpl extends jdbcdaosupport implements accountdao {
/**
* out :转出账号
* money:转出金额
*/
public void outmoney {
string sql="update account set money=money-? where name=?";
this.getjdbctemplate.update;
}













/**
* in :转入账号
* money:转入金额
*/
public void inmoney {
string sql="update account set money=money+? where name=?";
this.getjdbctemplate.update;
}
}
事务的传播行为和隔离级别[transaction behavior and isolated level]
spring中事务的定义:
一、propagation :
key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:
propagation_required--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
propagation_supports--支持当前事务,如果当前没有事务,就以非事务方式执行。
propagation_mandatory--支持当前事务,如果当前没有事务,就抛出异常。
propagation_requires_new--新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never--以非事务方式执行,如果当前存在事务,则抛出异常。



















很多人看到事务的传播行为属性都不甚了解,我昨晚看了j2ee without ejb的时候,看到这里也不了解,甚至重新翻起数据库系统的教材书,但是也没有找到对这个的分析。今天搜索,找到一篇极好的分析文章,虽然这篇文章是重点分析propagation_required 和 propagation_required_nested的
解惑 spring 嵌套事务
********transactiondefinition 接口定义*******************



int propagation_required = 0;




int propagation_supports = 1;


int propagation_mandatory = 2;




int propagation_requires_new = 3;




int propagation_not_supported = 4;


int propagation_never = 5;



int propagation_nested = 6;

*************************************************************************
在这篇文章里,他用两个嵌套的例子辅助分析,我这里直接引用了。
********************sample***********************
servicea {


void methoda {
serviceb.methodb;
}

}

serviceb {


void methodb {
}

}
*************************************************
我们这里一个个分析吧
1: propagation_required
加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务
比如说,serviceb.methodb的事务级别定义为propagation_required, 那么由于执行servicea.methoda的时候,
servicea.methoda已经起了事务,这时调用serviceb.methodb,serviceb.methodb看到自己已经运行在servicea.methoda
的事务内部,就不再起新的事务。而假如servicea.methoda运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
这样,在servicea.methoda或者在serviceb.methodb内的任何地方出现异常,事务都会被回滚。即使serviceb.methodb的事务已经被
提交,但是servicea.methoda在接下来fail要回滚,serviceb.methodb也要回滚
2: propagation_supports
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行
这就跟平常用的普通非事务的代码只有一点点区别了。不理这个,因为我也没有觉得有什么区别
3: propagation_mandatory
必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常。
4: propagation_requires_new
这个就比较绕口了。 比如我们设计servicea.methoda的事务级别为propagation_required,serviceb.methodb的事务级别为propagation_requires_new,
那么当执行到serviceb.methodb的时候,servicea.methoda所在的事务就会挂起,serviceb.methodb会起一个新的事务,等待serviceb.methodb的事务完成以后,
他才继续执行。他与propagation_required 的事务区别在于事务的回滚程度了。因为serviceb.methodb是新起一个事务,那么就是存在
两个不同的事务。如果serviceb.methodb已经提交,那么servicea.methoda失败回滚,serviceb.methodb是不会回滚的。如果serviceb.methodb失败回滚,
如果他抛出的异常被servicea.methoda捕获,servicea.methoda事务仍然可能提交。
5: propagation_not_supported
当前不支持事务。比如servicea.methoda的事务级别是propagation_required ,而serviceb.methodb的事务级别是propagation_not_supported ,
那么当执行到serviceb.methodb时,servicea.methoda的事务挂起,而他以非事务的状态运行完,再继续servicea.methoda的事务。
6: propagation_never
不能在事务中运行。假设servicea.methoda的事务级别是propagation_required, 而serviceb.methodb的事务级别是propagation_never ,
那么serviceb.methodb就要抛出异常了。
7: propagation_nested
理解nested的关键是savepoint。他与propagation_requires_new的区别是,propagation_requires_new另起一个事务,将会与他的父事务相互独立,
而nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而nested事务的好处是他有一个savepoint。
*****************************************
servicea {


void methoda {
try {
//savepoint
serviceb.methodb; //propagation_nested 级别
} catch {
// 执行其他业务, 如 servicec.methodc;
}
}

}
********************************************
也就是说serviceb.methodb失败回滚,那么servicea.methoda也会回滚到savepoint点上,servicea.methoda可以选择另外一个分支,比如
servicec.methodc,继续执行,来尝试完成自己的事务。
但是这个事务并没有在ejb标准中定义。



































































二、isolation level:
1、serializable:最严格的级别,事务串行执行,资源消耗最大;


2、repeatable read:保证了一个事务不会修改已经由另一个事务读取但未提交的数据。避免了 脏读取 和 不可重复读取 的情况,但是带来了更多的性能损失。

3、read committed:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了 脏读取 。该级别适用于大多数系统。

4、read uncommitted:保证了读取过程中不会读取到非法数据。
隔离级别在于处理多事务的并发问题。
我们知道并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行,这需要查看数据库教材的可串行化条件判断了。
这里就不阐述。
我们首先说并发中可能发生的3中不讨人喜欢的事情
1: dirty reads--读脏数据。也就是说,比如事务a的未提交的数据被事务b读走,如果事务a失败回滚,会导致事务b所读取的的数据是错误的。






2: non-repeatable reads--数据不可重复读。比如事务a中两处读取数据-total-的值。在第一读的时候,total是100,然后事务b就把total的数据改成200,事务a再读一次,结果就发现,total竟然就变成200了,造成事务a数据混乱。

3: phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了,但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。
dirty reads non-repeatable reads phantom reads
serializable 不会 不会 不会
repeatable read 不会 不会 会
read committed 不会 会 会
read uncommitted 会 会 会






三、readonly
事务属性中的readonly标志表示对应的事务应该被最优化为只读事务。这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用object/relational映射工具时避免dirty checking。
四、timeout
在事务属性中还有定义 timeout 值的选项,指定事务超时为几秒。在jta中,这将被简单地传递到j2ee服务器的事务协调程序,并据此得到相应的解释。

事务的api介绍






一、简介
platformtransactionmanager - 平台事务管理器,真正用来管理事务的接口,包含事务的提交,回滚等信息
transactiondefinition - 事务定义信息
transactionstatus - 事务具体的运行状态




关系:
首先会根据transactiondefinition事务定义的信息,由platformtransactionmanager对事务进行管理,进行事务管理的过程中,事务会产生一些相应的状态,这些状态在transactionstatus中


二、platformtransactionmanager 接口
1. datasourcetransactionmanager
使用spring jdbc 或ibatis进行持久化数据时使用
2. hibernatetransactionmanager
使用hibernate3.0版本进行持久化数据时使用





三、transactiondefinition
1.事务隔离级别
解决脏读、不可重复读、幻读等安全问题



事务隔离级别:
default
read_uncommited
read_commited
repeatable_read
serializable






2.事务的传播行为
解决业务层的方法之间的相互调用的问题


事务的传播行为有七种,又分为三类:
第一类共同点:a和b方法在同一个事务中。
*propagation_required
propagation_supports
propagation_mandatory





第二类共同点:a 方法和 b 方法不在同一个事务里面。
*propagation_requires_new
propagation_not_supported
propagation_never




第三类:如果 a 方法有的事务执行完,设置一个保存点,如果 b 方法中事务执行失败,可以滚回保存点或初始状态。
*propagation_nested


四、transactionstatus

transactionstatus接口用来记录事务的状态

该接口定义了一组方法,用来获取或判断事务的相应状态信息.

平台事务管理器会根据transactiondefinition中定义的事务信息来进行事务的管理,在管理的过程中事务可能
产生了保存点或事务是新的事务等情况,那么这些信息都会记录在transactionstatus的对象中.
transactionstatus记录事务的状态信息





【某某业务】网站建设、网站设计、服务器空间租售、网站维护、网站托管、网站优化、百度推广、自媒体营销、微信公众号
如有意向---联系我们
热门栏目
热门资讯

网站建设 网站托管 成功案例 新闻动态 关于我们 联系我们 服务器空间 加盟合作 网站优化

备案号: 

公司地址:江苏省南京市玄武区玄武湖 咨询QQ:9490489 手机: 电话: