重点知识复习:Spring+事务

spring IOC&AOP

1. Spring 的四个作用域

singleton 单例—jvm中只会创建一个

Prototype 多例——每次调用如注入或者使用getBean()方法都会创建

Request ——作用于web应用的会话范围,表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效

Session——作用于web应用的会话范围,session和对象进行绑定,session什么时候失效对象就什么时候失效

global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session

1.1 bean对象的生命周期

单例对象
    出生:当容器创建时对象出生
    活着:只要容器还在,对象一直活着
    死亡:容器销毁,对象消亡
    总结:单例对象的生命周期和容器相同
多例对象
    出生:当我们使用对象时spring框架为我们创建
    活着:对象只要是在使用过程中就一直活着。
    死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收

2. Spring容器(IOC)创建对象的几种方式:

调用无参数构造器
带参数构造器

<!-- 无参构造函数 -->
<bean id="user1" class="com.test.entity.UserEntity" scope="prototype" />
<!-- 有参构造函数 -->
<bean id="user2" class="com.test.entity.UserEntity">
	<constructor-arg name="name" type="java.lang.String"
		value="MingRun"></constructor-arg>
</bean>

工厂创建对象:又分成使用静态方法和非静态方法:

<bean id="factory" class="com.test.entity.ObjectFactory"></bean>
<!-- 通过实例工厂方法创建 -->
<bean id="user3" factory-bean="factory" 
	factory-method="getInstance">
</bean>
<!-- 通过静态工厂方法创建 -->
<bean id="user4" class="com.test.entity.ObjectFactory" 
	factory-method="getStaticInstance"></bean>

3. 倚赖注入(DI),给属性赋值的几种方法

使用set方法:

<bean id="userDao" class="cn.test.UserDao"></bean>
<!-- service instance -->
<bean id="userService" class="cn.test.UserService">
	<property name="userDao" ref="userDao"></property>
</bean>

p 名称空间注入属性值 (优化)

<!-- 传统的注入: 
	 <bean id="user" class="cn.test.User" >
	 	<property name="name" value="xxx"></property>
	 </bean>
	-->
	<!-- p名称空间优化后 -->
<bean id="user" class="cn.test.User" p:name="Jack0001"></bean>

使用注解,步骤如下:
1. 先引入context名称空间
xmlns:context=“http://www.springframework.org/schema/context”
2. 开启注解扫描

<context:component-scan base-package="com.test02"></context:component-scan>

3.通过注解的方式,把对象加入ioc容器。

创建对象以及处理对象依赖关系,相关的注解:

@Component   指定把一个对象加入IOC容器
@Repository   作用同@Component; 在持久层使用
@Service      作用同@Component; 在业务逻辑层使用
@Controller    作用同@Component; 在控制层使用 
@Resource     属性注入

@autowired 和 @resource:@autowired默认使用类型注入,如果找不到bean则使用@resource(name=””),进行名称注入,其中需要注意的是,@resource注解是jdk1.6及以上带的,并非是框架所带的注解。@autowired(required=false)@Qualifier("user")//使用名称

4. 切面与切入点

切面其实就是抽取出来的那些重复代码(即关注点)所组成的类。切入点就是需要被执行的方法,如需要添加事务的*del,*add,*update等诸如此类的方法。

5. 注解方式实现AOP编程

<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 开启事物注解权限

@Aspect		指定一个类为切面类		
@Pointcut("execution(* com.test.service.UserService.add(..))")  指定切入点表达式

@Before("pointCut_()")	前置通知: 目标方法之前执行
@After("pointCut_()")	后置通知:目标方法之后执行(始终执行)
@AfterReturning("pointCut_()")	返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing("pointCut_()")	异常通知:  出现异常时候执行
@Around("pointCut_()")	环绕通知: 环绕目标方法执行
@Component
@Aspect
public class Aop {
	@Before("execution(* com.test.service.UserService.add(..))")
	public void begin() {
		System.out.println("前置通知");
	}

	@After("execution(* com.test.service.UserService.add(..))")
	public void commit() {
		System.out.println("后置通知");
	}

	@AfterReturning("execution(* com.test.service.UserService.add(..))")
	public void afterReturning() {
		System.out.println("运行通知");
	}

	@AfterThrowing("execution(* com.test.service.UserService.add(..))")
	public void afterThrowing() {
		System.out.println("异常通知");
	}

	//环绕通知:在方法的之前和之后执行
	@Around("execution(* com.test.service.UserService.add(..))")
	public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
       System.out.println("我是环绕通知-前");
       //表示放心方法,如果注掉,方法将不会执行
       proceedingJoinPoint.proceed();
       System.out.println("我是环绕通知-后");
	}
}

6. XML方式实现AOP编程

1) 引入jar文件  【aop 相关jar, 4个】
2) 引入aop名称空间
3)aop 配置
	* 配置切面类 (重复执行代码形成的类)
	* aop配置
		拦截哪些方法 / 拦截到方法后应用通知代码
<!-- dao 实例 -->
<bean id="userDao" class="com.test.UserDao"></bean>
<bean id="orderDao" class="com.test.OrderDao"></bean>

<!-- 切面类 -->
<bean id="aop" class="com.test.Aop"></bean>

<!-- Aop配置 -->
<aop:config>
	<!-- 定义一个切入点表达式: 拦截哪些方法 -->
	<aop:pointcut expression="execution(* com.test.*.*(..))" id="pt"/>
	<!-- 切面 -->
	<aop:aspect ref="aop">
		<!-- 环绕通知 -->
		<aop:around method="around" pointcut-ref="pt"/>
		<!-- 前置通知: 在目标方法调用前执行 -->
		<aop:before method="begin" pointcut-ref="pt"/>
		<!-- 后置通知: -->
		<aop:after method="after" pointcut-ref="pt"/>
		<!-- 返回后通知 -->
		<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
		<!-- 异常通知 -->
		<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
	</aop:aspect>
</aop:config>

事务

1. 四个特性:

  1. 原子性(Atomicity):原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚
  2. 一致性(Consistency):一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态 ,拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
  3. 隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
  4. 持久性(Durability):持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

2. Spring 事务分类

手动事务(又称编程式事务)----自己begin,自己commit。声明事务----使用xml进行配置(用到了aop技术)。注解方式,添加@transactional

手动式事务:

//Spring声明式事务管理器类:
//Jdbc技术:DataSourceTransactionManager
//Hibernate技术:HibernateTransactionManager
@Component
public class TransactionUtils {
	// 事物管理器
	@Autowired
	private DataSourceTransactionManager dataSourceTransactionManager;
	public TransactionStatus begin() {
		TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());
		return transaction;
	}
	public void commit(TransactionStatus transaction) {
		dataSourceTransactionManager.commit(transaction);
	}
	public void rollback(TransactionStatus transaction) {
		dataSourceTransactionManager.rollback(transaction);
	}
}

xml:

	<!-- 开启注解 -->
	<context:component-scan base-package="com.test"></context:component-scan>
	<!-- 1. 数据源对象: C3P0连接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
		<property name="user" value="root"></property>
		<property name="password" value="root"></property>
	</bean>

	<!-- 2. JdbcTemplate工具类实例 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

   <!-- 配置事物 -->
   <bean  id="DataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   		 <property name="dataSource" ref="dataSource"></property>
   </bean>

在方法中使用:

@Autowired
private UserDao userDao;
@Autowired
TransactionUtils transactionUtils;

public void add() {
	TransactionStatus begin = TransactionUtils.begin();
	userDao.add("gfd", 18);
	int i=1/0;//可能会发生异常
	userDao.add("MingRun", 19);
	TransactionUtils,commit(begin);
}

3. 使用声明式事务

bean.xml (Spring务管理配置)

	<!-- 开启注解 -->
	<context:component-scan base-package="com.test"></context:component-scan>
	<!-- 1. 数据源对象: C3P0连接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
		<property name="user" value="root"></property>
		<property name="password" value="root"></property>
	</bean>
	<!-- 2. JdbcTemplate工具类实例 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 配置事物 -->
	<bean id="dataSourceTransactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
   	<!—配置事物增强-->
	<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true" />
			<tx:method name="find*" read-only="true" />
			<tx:method name="*" read-only="false" />
		</tx:attributes>
	</tx:advice>
	<!-- Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.test.service.*.*(..))"
			id="pt" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
	</aop:config>

4. 注解方式实现

@Transactional注解:
1. 应用事务的注解
2. 定义到方法上: 当前方法应用spring的声明式事务
3. 定义到类上: 当前类的所有的方法都应用Spring声明式事务管理;
4. 定义到父类上: 当执行父类的方法时候应用事务。

Bean.xml

<!-- 开启注解 -->
<context:component-scan base-package="com.test"></context:component-scan>
<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
	<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
	<property name="user" value="root"></property>
	<property name="password" value="root"></property>
</bean>

<!-- 2. JdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置事物 -->
<bean id="dataSourceTransactionManager"
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启注解事物 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

UserService

@Transactional
public void add() {
	try {
		userDao.add("gfd", 18);
		int i = 1 / 0;
		userDao.add("MingRun", 19);
	} catch (Exception e) {
		TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
	}		
}

5. 事物实现原理

事物底层实现通过AOP环绕通知实现
代码:

public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
	TransactionStatus begin = transactionUtils.begin();
	System.out.println("我是环绕通知-开启事物");
	try {
		proceedingJoinPoint.proceed();
		transactionUtils.commit(begin);
		System.out.println("我是环绕通知-提交事物");
	} catch (Exception e) {
		System.out.println("我是环绕通知-回滚事物");
		transactionUtils.rollback(begin);
	}
}

6. 使用事物注意事项

使用事物时,一定要将异常抛出外部,不然AOP环绕通知获取不到异常不能够回滚。
事务开启之后一定要释放,通过提交或者回滚,否则会一直占用内存资源。

7. 传播行为

Propagation(key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。)有以下选项可供使用:

  • PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  • PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
  • PROPAGATION_SUPPORTS :支持当前事务,如果当前没有事务,就以非事务方式执行。(如果当前有事物,我就用当前事物,如果当前没有事物,就以非事物进行执行)
  • PROPAGATION_NOT_SUPPORTED :以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER :以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
@Transactional(
	readOnly = false,  // 读写事务
	timeout = -1,       // 事务的超时时间不限制
	noRollbackFor = ArithmeticException.class,  // 遇到数学异常不回滚
	isolation = Isolation.DEFAULT,              // 事务的隔离级别,数据库的默认
	propagation = Propagation.REQUIRED			// 可以看到事务默认的传播行为REQUIRED
)

例一:

class UserService{
	@Autowired
	LogService logService;
	
	@Transactional(propagation=Propagation.REQUIRED)
	public void  saveUser(){
		logService.insertLog();    // 加入当前事务
		1/0; //.. 异常, 会回滚
		//save User 操作
	}
}
class LogService{
	public void insertLog(){
		//执行插入日志操作
	};  
}

REQUIRED默认事务,方法insertLog()并没有添加事务,但会使用saveUser()的事务,两个方法都会回滚。

例二:

class UserService{
	@Autowired
	LogService logService;
	
	@Transactional(propagation=Propagation.REQUIRED)
	public void  saveUser(){
		logService.insertLog();    // 加入当前事务
		1/0; //.. 异常, 会回滚
		//save User 操作
	}
}
class LogService{
	@Transactional(propagation=Propagation.REQUIRED)
	public void insertLog(){
		//执行插入日志操作 
	};  
}

结果saveUser()方法回滚,insertLog()不回滚,REQUIRED只在没有事务的时候添加事务。

例三:

class UserService{
	@Autowired
	LogService logService;
	
	@Transactional(propagation=Propagation.REQUIRED)
	public void  saveUser(){
		logService.insertLog();    // 加入当前事务
		1/0; //.. 异常, 会回滚
		//save User 操作
	}
}
class LogService{
	@Transactional(propagation=Propagation.REQUIRED_REQUIRES_NEW)
	public void insertLog(){
		//执行插入日志操作 
	};  
}

前者回滚,而insertLog不回滚,REQUIRED_REQUIRES_NEW表示不用当前的事务,也就是不使用saveUser的事务,只用自己的事务。

通过例子剩下的几种就更好理解了
PROPAGATION_SUPPORTS:有事务就还用你的事务,没有就没了。
PROPAGATION_NOT_SUPPORTED:当前方法就不加事务,就算外层添加了事务也不用,直接挂起。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章