Spring的AOP配置
1.第一种:注解配置AOP
2.第二种:xml配置AOP
使用Spring AOP实现声明式事务管理
1.基于XML配置,现在也有很多通过注解的方式来配置事务管理类
(1)配置事务管理类
-
- <bean id="transactionManager"
- class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- bean>
在spring的配置中配置数据源(dataSource)、事务管理器,事务管理器使用不同的orm框架事务管理器类就不同,mybatis 是org.springframework.jdbc.datasource.DataSourceTransactionManager 。而hibernate事务管理器为org.springframework.orm.hibernate3.HibernateTransactionManager
或者通过注解配置事务管理类
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
}
@Bean
public PlatformTransactionManager transactionManager() {
LocalContainerEntityManagerFactoryBean factoryBean = entityManagerFactory();
if (factoryBean != null) {
return new JpaTransactionManager(factoryBean.getObject());
}
return null;
}
(2)配置事务属性
-
- <tx:advice id="TestAdvice" transaction-manager="transactionManager">
-
- <tx:attributes>
- <tx:method name="search*" propagation="REQUIRED" read-only="true" isolation="DEFAUT" TIMEOUT="-1" />
- <tx:method name="del*" propagation="REQUIRED" />
- <tx:method name="update*" propagation="REQUIRED" />
- <tx:method name="add*" propagation="REQUIRED" />
- tx:attributes>
- tx:advice>
事务属性在中进行设置,Spring支持对不同的方法设置不同的事务属性,所以可以为一个设置多个,其中name属性指定匹配的方法(这里需要对这些方法名进行约定,如果事务切入点在service上,则最好和Dao的方法命名区分开,也不要使用get set关键字,防止和属性的getter setter发生混淆)
事务有以下几个常用属性:
a.read-only:设置该事务中是否允许修改数据。(对于只执行查询功能的事务,设置为TRUE可以提高事务的执行速度)
b.propagation:事务的传播机制。一般设置为required。可以保证在事务中的代码只在当前事务中运行,防止创建多个事务。
c.isolation:事务隔离级别。不是必须的。默认值是default。
d.timeout:允许事务运行的最长时间,以秒为单位。
e.rollback-for:触发回滚的异常。
f.no-rollback-for:不会触发回滚的异常。
Table 9.1. 有关的设置
属性 |
是否需要? |
默认值 |
描述 |
name |
是 |
|
与事务属性关联的方法名。通配符(*)可以用来指定一批关联到相同的事务属性的方法。 如:'get*'、'handle*'、'on*Event'等等。 |
propagation |
不 |
REQUIRED |
事务传播行为 |
isolation |
不 |
DEFAULT |
事务隔离级别 |
timeout |
不 |
-1 |
事务超时的时间(以秒为单位) |
read-only |
不 |
false |
事务是否只读? |
rollback-for |
不 |
|
将被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException' |
no-rollback-for |
不 |
|
不 被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException' |
***实际开发中,对于只执行查询功能的事务,要设置read-only为TRUE,其他属性一般使用默认值即可。
(3)配置事务的AOP切入点
- <aop:config>
-
- <aop:pointcut id="services"
- expression="execution(public* com.pb.service.*.*(..))" />
- <aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />
- aop:config>
该设置的含义是:对于com.pb.service.impl包及子包下的所有类的所有公共方法进行切入。(被切入的 方法经过筛选)web应用程序最合适的事务切入点是Service的方法上。
----通过以上三个步骤设置好声明式事务后,当Service中 的业务方法被调用之前,Spring会获取事务对象并启动事务。并使用try-catch-finally来处理异常。业务方法执行成功则会提交事务,默认情况下如果抛出了RuntimeException 或者Rrror 对象就会回滚事务。(注意: 这里注意一下,在tx:method中配置了rollback_for 中配置的Exception 这个是运行时的异常才会回滚不然其他异常是不会回滚的!)
2.使用annotation配置
*1.在事务管理的dao实现类之前标注@Transactional
*2.在要进行事务管理的方法前加上@Transactional(propagation= Propagation.REQUIRED)
*3.在配置文件中指定驱动:
- package demo.spring.dao;
-
- import java.util.Iterator;
- import java.util.List;
-
- import javax.sql.DataSource;
-
- import org.springframework.jdbc.core.JdbcTemplate;
- import org.springframework.transaction.annotation.Propagation;
- import org.springframework.transaction.annotation.Transactional;
-
- import demo.spring.entity.Person;
- @Transactional//将此类进行事务管理
- public class PersonDaoImpl implements PersonDao {
- private JdbcTemplate jt;
-
- public void setDataSource(DataSource dataSource){
- jt = new JdbcTemplate(dataSource);
- }
- @Override
- public void insert(long id, String name, int age) {
- jt.update("insert into person values('"+id+"','"+name+"','"+age+"')");
-
- }
-
- @Transactional(propagation= Propagation.REQUIRED)//定义要事务管理的方法,指定传播行为
- public void batchInsert(List persons) {
- for(Iterator it = persons.iterator(); it.hasNext(); ){
- Person p = (Person) it.next();
- insert(p.getId(),p.getName(),p.getAge());
- }
- }
-
- }
以上内容转载于:https://blog.csdn.net/c_w_d/article/details/63252340
思考:在配置文件中对com.pb.service.impl包及子包下的所有类的所有公共方法进行切入,但是在这个包下面的要进行事务管理的方法没有加@Transactional(propagation= Propagation.REQUIRED),还能对该方法进行事务管理吗?
Spring中常用事务的传播机制(事务类型):
- 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也要回滚
- PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
- 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事务仍然可能提交。
- PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- 解释如下:比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,
那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。
- PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
- 解释如下:假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,
那么ServiceB.methodB就要抛出异常了。
- PROPAGATION_REQUIRED类似的操作。
- PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
解释如下: 理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与 他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚, 他也要回滚的。而Nested事务的好处是他有一个savepoint。
*****************************************
ServiceA {
void methodA() {
try {
//savepoint
ServiceB.methodB(); //PROPAGATION_NESTED 级别
} catch (SomeException) {
// 执行其他业务, 如 ServiceC.methodC();
}
}
}
********************************************
也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如
ServiceC.methodC,继续执行,来尝试完成自己的事务。
Spring中常用事务的隔离级别:
- ISOLATION_DEFAULT:用底层数据库的默认隔离级别,数据库管理员设置什么就是什么
- ISOLATION_READ_UNCOMMITTED(未提交读):最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)
- ISOLATION_READ_COMMITTED(提交读):一个事务提交后才能被其他事务读取到(该隔离级别禁止其他事务读取到未提交事务的数据、所以还是会造成幻读、不可重复读)、sql server默认级别
- ISOLATION_REPEATABLE_READ(可重复读):可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(该隔离基本可防止脏读,不可重复读(重点在修改),但会出现幻读(重点在增加与删除))(MySql默认级别,更改可通过set transaction isolation level 级别)
- ISOLATION_SERIALIZABLE(序列化):代价最高最可靠的隔离级别(该隔离级别能防止脏读、不可重复读、幻读)
- 丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的;
- 幻读:同样的事务操作过程中,不同时间段多次(不同事务)读取同一数据,读取到的内容不一致(一般是行数变多或变少)。
- 脏读:一个事务读取到另外一个未提及事务的内容,即为脏读。
- 不可重复读:同一事务中,多次读取内容不一致(一般行数不变,而内容变了)。
幻读与不可重复读的区别:幻读的重点在于插入与删除,即第二次查询会发现比第一次查询数据变少或者变多了,以至于给人一种幻象一样,而不可重复读重点在于修改,即第二次查询会发现查询结果比第一次查询结果不一致,即第一次结果已经不可重现了。
数据库隔离级别越高,执行代价越高,并发执行能力越差,因此在实际项目开发使用时要综合考虑,为了考虑并发性能一般使用提交读隔离级别,它能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁或乐观锁来解决这些问题。
悲观锁与乐观锁可参考:http://blog.csdn.net/liaohaojian/article/details/62416972
第三部分
关于AOP切入点pointcut中的expression的理解
- <aop:pointcut id="services"
- expression="execution(public* com.pb.service.*.*(..))" />
execution(* com.spring.aop.*.*(..))
这是 com.spring.aop包下所有的类的所有方法。。
第一个*代表所有的返回值类型
第二个*代表所有的类
第三个*代表类所有方法
最后一个..代表所有的参数。
网上其它示例1:
第一个* 表示任意返回值类型
第二个* 表示以任意名字开头的package. 如 com.xx.
第三个* 表示 通配 *service下的任意类
第四个* 表示 以任意名字开头的方法名
最后二个.. 表示通配 方法可以有0个或多个参数
部分转载于:https://blog.csdn.net/qq525099302/article/details/53996344,
https://blog.csdn.net/wwh578867817/article/details/51736723