Spring是一个分层的 Java SE/EE full-stack 开源的轻量级 java开发框架,也是目前开发当中最主流的框架。其具有 控制反转 (IoC) 和 面向切面 (AOP) 两大核心,且Spring可以通过 注解、声明 等方式灵活进行 事务 的管理,大大提高了开发效率和质量。
Spring 的优势
- 方便解耦,可以将所有对象的创建和依赖关系的维护交由 Spring 管理;
- 允许集成各种优秀的框架,如 Struts2、Hibernate、MyBatis 等;
- 降低 Java EE API 使用难度,如 JDBC、JavaMail、远程调用等;
- 方便测试,内部支持 JUnit4;
- 支持AOP 编程,方便实现对程序进行权限拦截和运行监控等功能;
- 支持声明式事务,只需通过配置即可,无需手动编写java程序添加。
Spring 的体系结构
Spring 提供了很多模块,方便开发人员根据需要向框架引入相应功能,具体如下:
Core Container 核心容器
- spring-core
该模块提供了框架的基本组成部分,包括IoC和依赖注入功能。 - spring-beans
该模块主要以工厂模式实现,生成所需的对象。 - spring-context
该模块是在 core 和 beans 的基础上建立起来的,它以一种类似于 JNDI 注册的方式访问对象。其中的 ApplicationContext 接口是该模块的焦点,非常常用。Spring-context-support 提供了对第三方集成到 Spring 上下文的支持,比如缓存(EhCache,
Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker,
JasperReports, Velocity)等。 - spring-expression
该模块提供了表达式语言,用于在运行时查询和操作对象映射。它是 JSP2.1 规范中定义的统一表达式语言的扩展,支持 set 和 get 属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从 Spring IoC 容器检索对象,还支持列表的
投影、选择以及聚合等。
他们的依赖关系如下:
Data Access/Integration 数据访问/集成
- JDBC (Java Data Base Connectivity) 模块提供了 JDBC 抽象层,它消除了冗长 JDBC 编码和对数据库供应商特定错误代码的解析。
- ORM (Object Relational Mapping) 模块提供了对流行的对象关系映射 API 的集成,包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 Spring的其它功能整合,比如前面提及的事务管理。
- OXM (Object XML Mapping) 模块提供了对 OXM 实现的支持,比如 JAXB、Castor、XML Beans、JiBX、XStream 等。
- JMS (Java Message Service) 模块包含生产(produce)和消费(consume)消息的功能。从 Spring 4.1 开始,集成了 Spring-messaging 模块。
- Transactions 事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。
Web 网络
- Web 模块提供面向 web 的基本功能和面向 web 的应用上下文,比如多部分(multipart)文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分。
- Web-MVC 模块为 web 应用提供了模型视图控 (MVC)和 REST Web服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离,且可以与 Spring 框架的其它所有功能进行集成。
- Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。
- Web-Portlet 模块提供了用于 Portlet 环境的 MVC 实现,并反映了 Spring-webmvc 模块的功能。
其他
- AOP 模块提供了面向方面(切面)的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。
- Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。
- Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。
- Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。
- Test 模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。
Spring核心 IoC 控制反转
Inversion of Control (IoC) 是指对创建对象的控制权发生反转,即在程序开发中,实例的创建交由Spring容器负责。
获取Spring容器
BeanFactory
它是基础类型的 IoC 容器,是一个管理 Bean 的工厂,主要负责初始化各种 Bean,并调用他们的生命周期方法。
其中 org.Springframework.beans.factory.xml.XmlBeanFactory 是最常用的实现类:
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource(Spring配置文件的名称));
ApplicationContext
它是 BeanFactory 的子接口,也被称为 应用上下文。它不仅提供了 BeanFactory 的所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。
以下有多种实现类:
ClassPathXMLApplicationContext 最常用
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(Spring配置文件的名称);
FileSystemXmlApplicationContext
ApplicationContext applicationContext = new
FileSystemXmlApplicationContext(String configLocation);
它与 ClassPathXMLApplicationContext 的区别:
在读取 Spring 的配置文件时, FileSystemXmlApplicationContext 不再从类路径中读取配置文件,而是通过参数指定配置文件的位
置,它可以获取类路径之外的资源,如 “D:\application.xml”。
通过 Spring 容器可以创建自定义类的对象与非自定义类的对象。
Spring 配置文件操作
bean 标签的属性
- class
指定bean对应类的全路径 - name
bean对应对象的一个标识 - scope
执行bean对象创建模式和生命周期, scope=“singleton” 单例 和 scope=“prototype” 原型(多例) - id
id是bean对象的唯一标识,不能添加特别字符 - lazy-init
是否延时加载 默认值:false。true 延迟加载对象,当对象被调用的时候才会加载,测试的时候,通过getbean()方法获得对象。lazy-init=“false” 默认值,不延迟,无论对象是否被使用,都会立即创建对象,测试时只需要加载配置文件即可。注意:测试的时候只留下id,class属性 - init-method
只需要加载配置文件即可初始化对象的方法 - destroy-method
对象销毁方法
Spring 容器创建对象的方式
配置文件头:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
- 使用默认的构造方法
<bean id="team1" class="com.kbb.pojo.Team"></bean>
- 使用带参数的构造方法
<bean id="team2" class="com.kbb.pojo.Team">
<!--方式一: 通过属性名称传入值-->
<!--name:表示参数的名称-->
<constructor-arg name="id" value="1001"/>
<constructor-arg name="name" value="勇士"/>
<constructor-arg name="location" value="金州"/>
</bean>
<bean id="team3" class="com.kbb.pojo.Team">
<!--方式二: 通过属性下标传入值-->
<!--index:表示参数的下标索引-->
<constructor-arg index="0" value="1002"/>
<constructor-arg index="1" value="热火"/>
<constructor-arg index="2" value="迈阿密"/>
</bean>
- 使用工厂类
<!--通过静态工厂方法-->
<bean id="staticTeam" class="com.kbb.pojo.MyFactory" factory-method="staticFun"/>
<!--通过实例工厂方法-->
<bean id="factory" class="com.kbb.pojo.MyFactory"/>
<bean id="instanceTeam" factory-bean="factory" factory-method="instanceFun"/>
基于XML配置文件中的依赖注入 DI
Dependency Injection (DI) 是通过容器决定在运行时组件之间的依赖关系,即容器会动态地将某个依赖关系注入到组件中。它与 IoC 是同一个概念,前者是实现手段,后者是一种思想。
注入的方法如下:
- 通过set方法
<bean id="teamDao" class="com.kkb.dao.TeamDao"/>
<bean id="teamService" class="com.kkb.service.TeamService">
<!--使用set方法注入属性值-->
<property name="teamDao" ref="teamDao"></property>
</bean>
- 通过构造方法
<bean id="teamService2" class="com.kkb.service.TeamService">
<!--使用构造方法注入属性值-->
<constructor-arg name="teamDao" ref="teamDao"></constructor-arg>
</bean>
- 自动注入 byName byType
<!--按名称自动注入:查找容器中id名与属性名一致的对象进行注入-->
<bean id="teamService3" class="com.kkb.service.TeamService"
autowire="byName"/>
<!--按类型自动注入:查找容器中类型与属性类型相同或者符合is-a关系的对象进行注入,但是要求类
型相同的对象唯一,否则抛出异常:不知道用哪一个匹配-->
<bean id="teamService4" class="com.kkb.service.TeamService"
autowire="byType"/>
基于注解方式的依赖注入 DI
声明 Bean 的注解 @Component
// value可以省略,默认是类名首字母小写
@Component(value = "teamDao")
public class TeamDao {
public void add() {
System.out.println("Team------ add -----");
}
public TeamDao() {
System.out.println("TeamDao ------ 默认的构造方法");
}
}
其中,@Component注解包含了三个子注解:
- @Respository:用于dao实现类的注解
- @Service:用户service实现类的注解
- @Controller:用于controller实现类的注解
注意:使用注解时一定要在XML配置文件中启用包扫描 context:component-scan。
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 多个包扫描 方式一 -->
<!-- <context:component-scan base-package="com.yy.controller"></context:component-scan>-->
<!-- <context:component-scan base-package="com.yy.dao"></context:component-scan>-->
<!-- <context:component-scan base-package="com.yy.service"></context:component-scan>-->
<!-- 多个包扫描 方式二 -->
<!-- <context:component-scan base-package="com.yy.controller, com.yy.service, com.yy.dao"></context:component-scan>-->
<!-- <context:component-scan base-package="com.yy.controller; com.yy.service; com.yy.dao"></context:component-scan>-->
<!-- 多个包扫描 方式三 -->
<context:component-scan base-package="com.yy"></context:component-scan>
</beans>
属性注入 @Value
可以在属性和set方法前注解,value = 注入值;
自动注入 @Autowired
默认 byType,加上 @Qualifier 是 byName;
自动注入 @Resource (JDK 6 及以上版本)
默认 byType,也可以通过 name = “XXX” 实现 byName;
Spring核心 AOP 面向切面编程
Aspect Oriented Programming (AOP) 是指通过预编译方式和运行期动态代理实现程序功能的同一维护技术。
作用:
不修改源码的情况下,程序运行期间对方法进行功能增强。
优点:
- 减少代码的重复,提高开发效率,便于维护;
- 专注核心业务的开发。
AOP的实现机制 - 动态代理
代理模式是指将一些重复且而又必要的事务交由代理实现。
其中有静态代理和动态代理两种形式。
静态代理
静态代理是指编写好一个静态代理类,其中的核心业务方法是可以根据业务对象改变而改变的,每当运行相同静态代理类创建的对象,所执行的事务都是一样的。
这种方式确实可以做到不修改目标对象功能的前提下,实现对目标对象扩展功能的目的。但它也带来了以下缺点:
- 代理对象需要与目标对象实现一样的接口,接口、代理、目标之间耦合程度就加深了,使得改变接口就会对其它实现类影响很大。
动态代理
动态代理是在程序运行的时候,容器根据被代理的对象动态生成代理类,动态代理是Spring主要应用的代理模式。其中包含两种动态代理:
- 基于JDK的动态代理
package com.yy.dynamicproxy;
import com.yy.aop.LogAOP;
import com.yy.aop.TransAOP;
import com.yy.service.Iservice;
import com.yy.service.TeamService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyJDKProxy {
// 结构化方式二: 提取Proxy.newProxyInstance,创建工厂方法
public static void main(String[] args) {
TeamService teamService = new TeamService();
TransAOP transAOP = new TransAOP();
Iservice transService = (Iservice) ProxyFactory.getProxyInstance(teamService, transAOP);
LogAOP logAOP = new LogAOP();
Iservice logService = (Iservice) ProxyFactory.getProxyInstance(transService, logAOP);
logService.add();
}
// 结构化方式一: 提取InvocationHandler
public static void main2(String[] args) {
TeamService teamService = new TeamService();
TransAOP transAOP = new TransAOP();
Iservice transService = (Iservice) Proxy.newProxyInstance(
teamService.getClass().getClassLoader(),
teamService.getClass().getInterfaces(),
new ProxyHandler(teamService, transAOP)
);
LogAOP logAOP = new LogAOP();
Iservice logService = (Iservice) Proxy.newProxyInstance(
transService.getClass().getClassLoader(),
transService.getClass().getInterfaces(),
new ProxyHandler(transService, logAOP)
);
// 代理对象进行操作
logService.add();
}
public static void main1(String[] args) {
// 目标对象--被代理对象
TeamService teamService = new TeamService();
// 返回代理对象 调用JDK中Proxy类中的静态方法newProxyInstance获取动态代理类的实例
Iservice proxyService = (Iservice) Proxy.newProxyInstance(
teamService.getClass().getClassLoader(),
teamService.getClass().getInterfaces(),
new InvocationHandler() { // 回调函数 编写代理规则
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
System.out.println("开始事务");
Object invoke = method.invoke(teamService, args);// 核心业务
System.out.println("提交事务");
return invoke;
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
throw e;
} finally {
System.out.println("finally------------");
}
}
}
);
// 代理对象进行操作
proxyService.add();
System.out.println(teamService.getClass());
System.out.println(proxyService.getClass() + "--------------");
}
}
- 基于CGLIB的动态代理
package com.yy.cglibproxy;
import com.yy.aop.TransAOP;
import com.yy.service.NBAService;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyCglibProxy {
public static void main(String[] args) {
// 目标对象 没有接口
NBAService nbaService = new NBAService();
// 切面对象
TransAOP transAOP = new TransAOP();
// 代理对象 选择cglib动态代理
NBAService cglibProxy = (NBAService) CglibProxyFactory.getCglibProxy(nbaService, transAOP);
int laker = cglibProxy.add("laker", 1001);
System.out.println(laker);
}
public static void main1(String[] args) {
// 目标对象 没有接口
NBAService nbaService = new NBAService();
// 代理对象 选择cglib动态代理
NBAService proxyService = (NBAService) Enhancer.create(
nbaService.getClass(),
new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
try {
System.out.println("开始事务");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("提交事务");
return invoke;
} catch (Exception e) {
System.out.println("事务回滚");
throw e;
} finally {
System.out.println("finally--------------------");
}
}
}
);
// 代理对象进行操作
int res = proxyService.add("laker", 1001);
System.out.println(res);
}
}
两者的差别在于:JDK 是基于接口代理的,目标对象(被代理对象)一定要实现接口,否则无法通过 JDK 方式进行功能拓展。而 CGLIB 不需要目标对象实现接口,是通过创建其子类对象的方式实现代理,底层是通过使用一个 小而快的字节码处理框架 ASM,来转换字节码并生成新的类,达到创建代理类的目的。
Spring AOP
相关术语
- Target 目标对象 指被代理对象。
- Proxy 代理 指代理对象。
- Aspect 切面 指增强的某个功能,因相对于代理对象位置不同而不同。
- Joinpoint 连接点 指一组被拦截的方法(要实现代理的方法),其中被 final 标记的方法不能作为连接点与切入点。
- Pointcut 切入点 指拦截方法位置的集合,其中被 final 标记的方法不能作为连接点与切入点。
- Advice 通知/增强 指拦截 joinpoint 之后要做的事情或实现的功能,根据切入点的不同分为前置通知、后置通知、异常通知、最终通知和环绕通知。
- Weaving 交织 指通知与目标对象连接点结合,创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
AspectJ 对 AOP 的实现
其需要根据不同的切入点表达式,对不同的切入点实现不同的通知功能。切入点表达式格式如下:
execution (访问权限 方法返回值 方法声明(参数) 异常类型)
可通过注解方式实现 AOP
配置文件中引入约束
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/springbeans.
xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--在beans标签中 引入AOP和context约束-->
</beans>
定义切面类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 切面类
*/
@Component //切面对象的创建权限依然交给spring容器
@Aspect //aspectj 框架的注解 标识当前类是一个切面类
public class MyAspect{
/**
* 当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。
* AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。
* 其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均
* 可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。
* 这个使用@Pointcut 注解方法一般使用 private 的标识方法,即没有实际作用的方法。
*/
@Pointcut("execution(* com.kkb.service..*.*(..))")
private void pointCut(){
}
@Pointcut("execution(* com.kkb.service..*.add*(..))")
private void pointCut2(){
}
/**
* 声明前置通知
* @param jp
*/
@Before("pointCut()")
public void before(JoinPoint jp){
System.out.println("前置通知:在目标方法执行之前被调用的通知");
String name = jp.getSignature().getName();
System.out.println("拦截的方法名称:"+name);
Object[] args = jp.getArgs();
System.out.println("方法的参数格式:"+args.length);
System.out.println("方法参数列表:");
for (Object arg : args) {
System.out.println("\t"+arg);
}
}
/**
* AfterReturning 注解声明后置通知
* value: 表示切入点表达式
* returning 属性表示 返回的结果,如果需要的话可以在后置通知的方法中修改结果
*/
@AfterReturning(value = "pointCut2()",returning = "result")
public Object afterReturn(Object result){
if(result!=null){
boolean res=(boolean)result;
if(res){
result=false;
}
}
System.out.println("后置通知:在目标方法执行之后被调用的通知,result="+result);
return result;
}
/**
* Around 注解声明环绕通知
* ProceedingJoinPoint 中的proceed方法表示目标方法被执行
*/
@Around(value = "pointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕方法---目标方法的执行之前");
Object proceed = pjp.proceed();
System.out.println("环绕方法---目标方法的执行之后");
return proceed;
}
/**
* AfterThrowing 注解声明异常通知方法
* value: 表示切入点表达式
* returning 属性表示 返回的结果,如果需要的话可以在后置通知的方法中修改结果
*/
@AfterThrowing(value = "pointCut()",throwing = "ex")
public void exception(JoinPoint jp,Throwable ex){
//一般会把异常发生的时间、位置、原有都记录下来
System.out.println("异常通知:在目标方法执行出现异常的时候才会别调用的通知,否则不执行");
System.out.println(jp.getSignature()+"方法出现异常,异常信息是:"+ex.getMessage());
}
/**
* After 注解声明为最终通知
*/
@After( "pointCut()")
public void myFinally(){
System.out.println("最终通知:无论是否出现异常都是最后被调用的通知");
}
}
注意:在目标类和切面类都要加好相应的注解才能实现切面编程。
在XML配置文件中开启包扫描和aspectj的自动代理
<!--包扫描-->
<context:component-scan base-package="com.kkb.service,com.kkb.aop"/>
<!--开启注解AOP的使用-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--aop:aspectj-autoproxy的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的,
是基于 AspectJ 的注解适配自动代理生成器。
其工作原理是,aop:aspectj-autoproxy通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找
到目标类的目标方法,再由通知类型找到切入的时间点。-->
通过XML方式实现AOP
切面类和目标类都不用添加注解,只需变动XML配置文件
<!-- XML方式实现AOP -->
<aop:config>
<!-- AOP 声明切入点表达式 -->
<aop:pointcut id="pt1" expression="execution(* com.yy.service..*.add*(..))"/>
<aop:pointcut id="pt2" expression="execution(* com.yy.service..*.*(..))"/>
<!-- AOP 声明切入点 -->
<aop:aspect ref="myAOP">
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="pt1"></aop:before>
<!-- 后置通知 -->
<aop:after-returning method="afterReturn" pointcut-ref="pt2" returning="result"></aop:after-returning>
<!-- 异常通知 -->
<aop:after-throwing method="exception" pointcut-ref="pt2" throwing="ex"></aop:after-throwing>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt2"></aop:around>
<!-- 最终通知 -->
<aop:after method="myFinally" pointcut-ref="pt2"></aop:after>
</aop:aspect>
</aop:config>
Spring 管理 jdbc
由于公司项目会有其他框架 (例如 MyBatis) 来处理与数据库交互的工作,所以这里就不详细介绍这种老式繁琐地处理方式了。
Spring 事务管理
事务主要与数据库操作相关,离不开三种操作:提交事务、回滚事务与获取事务的状态信息。
事务管理器接口
事务管理器是 PlatformTransactionManager 接口对象,主要用于完成事务的提交、回滚及获取事务的状态信息。
其常用的实现类为 DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
** Spring的回滚方式
默认的回滚方式是发生运行时异常和 error 时回滚,发生受查 (编译) 异常时提交。对于受查异常,程序员是可以收工设置其回滚方式的。
事务定义接口
事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作。
事务隔离级别常量:
- DEFAULT - 采用DB默认的事务隔离级别。MySQL的默认为 REPEATABLE_READ;Oracle默认为 READ_COMMITTED。
- READ_UNCOMMITTED:读未提交。未解决任何并发问题。
- READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
- REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读。
- ERIALIZABLE:串行化。不存在并发问题。
事务传播行为常量:
- Propagation.REQUIRED
当前没有事务的时候,就会创建一个新的事务;如果当前有事务,就直接加入该事务,比较常用的
设置 - Propagation.SUPPORTS
支持当前事务,如果当前有事务,就直接加入该事务;当前没有事务的时候,就以非事务方式执行 - Propagation.MANDATORY
支持当前事务,如果当前有事务,就直接加入该事务;当前没有事务的时候,就抛出异常 - Propagation.REQUIRES_NEW
创建新事务,无论当前是否有事务都会创建新的 - PROPAGATION_NESTED
- PROPAGATION_NEVER
- PROPAGATION_NOT_SUPPORTED
默认事务超时时限:
常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。
注意,事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。所以,该值一般就使用默认值即可。
声明式事务控制
基于注解的事务
Service类声明事务注解
package com.yy.service;
import com.yy.bean.Person;
import com.yy.dao.PersonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class PersonService {
@Autowired
private PersonDao personDao;
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
public int insert(Person person) {
int num1 = personDao.insert(person);
System.out.println("第一条执行结果: num1 = " + num1);
System.out.println(10/0);
int num2 = personDao.insert(person);
System.out.println("第二条执行结果: num2 = " + num2);
return num1 + num2;
}
}
XML配置文件创建事务管理器并开启事务
<!--添加事务管理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启注解事务权限-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
基于XML的事务
添加依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
XML配置文件中注入依赖
<!--添加事务管理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--xml方式实现事务-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--声明方法作用域-->
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.yy.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>