Spring基础用法总结
Spring的基本用法:
spring是一个很普通但很实用的一个框架,它提取了大量实际开发中需要重复解决的步骤,将这些步骤抽象成一个框架。
1.spring简介:
- spring5
- Spring Core Container(即Spring容器)代表了Spring框架的核心机制。
2.spring入门:
21个jar包
实用spring管理bean:
spring核心容器理论:spring核心容器就是一个超级大工厂,所有的对象都会被当成spring核心容器管理的对象。
Spring对Bean没有任何要求,只要是java类,将会当作bean处理
对于Spring框架而言,一切Java对象都是Bean。
<beans> <bean id="" class=""> <property name="" ref=""/> </bean> <bean id="" class=""> <property name="" value=""/> </bean> ... </beans>
spring通过反射机制从xml文件中获取bean对象:
spring底层大概的执行代码:
创建bean:
String idStr=bean中的id; String classStr=bean中的class; Class clazz=Class.forName(classStr); Object obj=clazz.newInstance(); spring的容器container.put(idStr,obj);
设置参数(调用setter方法):
String nameStr=属性的name; String refStr=属性的引用ref,某一个bean的id 或某个value参数值; String setterName="set"+nameStr.subString(0,1).toUpperCase()+nameStr.subString(1);生成方法名 setName Object paramBean=container.get(refStr) Method setter=clazz.getMethod(setterName,paramBean.getClass()); setter.invoke(obj,paramBean);
在程序中访问bean:
ApplicationContext
是最常用的接口, 有两个实现类:ClassPathXmlApplicationContext
从类加载路径下搜索配置文件,并根据配置文件创建spring容器FileSystemXmlApplicationContext
从文件系统的相对路径或绝对路径中搜索配置文件,并根据配置文件创建spring容器ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); Person p=ctx.getBean("person",Person.class);
获取对象的两个方法:
Object getBean(String id)
需要强制类型转换:Person p=(Person)ctx.getBean("person");
T getBean(String id,Class<T> requiredType)
无需进行强制类型转换:Person p=ctx.getBean("person",Person.class);
3.Spring的核心机制:依赖注入:
Spring框架的核心功能有两个:
Spring容器作为超级大工厂,负责创建、管理所有的Java对象,这些Java对象被称为Bean
Spring容器管理容器中Bean之间的依赖关系,使用一种被称为“依赖注入”的方式来管理Bean之间的依赖关系
理解依赖注入:
- 调用者无须主动获取被调用者,只要被动接受Spring容器为其赋值即可(
<property />
通过该子元素赋值) - 调用者获取依赖对象的方式由原来的主动获取,变成了被动接受,所以又被称为控制反转。
- 调用者无须主动获取被调用者,只要被动接受Spring容器为其赋值即可(
依赖注入的两种注入方式:
- 设值注入:IoC容器使用成员变量的setter方法来注入被依赖对象
- 构造注入:IoC容器使用构造器来注入被依赖对象
- Spring IoC容器的三个基本要点:
- 应用程序的各组件面向接口编程
- 应用程序的各组件不再由程序主动创建,而是由Spring容器来负责产生并实例化
- Spring采用配置文件或注解来管理bean的实现类、依赖关系。Spring容器根据配置文件或注解,利用反射来创建梳理,并为之注入依赖关系。
- 两种方式对比:
- 建议采用以设值注入为主,构造注入为辅的注入策略。
- 对于依赖关系无需变化的注入,尽量采用构造注入;
- 而对于其他依赖关系的注入,则考虑采用设值注入。
- 建议采用以设值注入为主,构造注入为辅的注入策略。
4.使用spring容器:
- 核心接口(获取spring容器):
BeanFactory
和ApplicationContext
ApplicationContext
是BeanFactory
的子接口
ApplicationContext
:当系统创建
ApplicationContext
容器时,默认会预初始化所有的singleton Bean 可在<bean>
中设置lazy-init="true"
改变默认预初始化ApplicationContext
国际化ApplicationContext
的事件机制:- 通过
ApplicationEvent
类和ApplicationListener
接口实现事件处理 - 如果容器中有一个
ApplicationListener Bean
,每当ApplicationContex
t发布ApplicationEvent
时,ApplicationListener Bean
将自动被触发。 - 容器事件监听器可以监听事件。
- 除此之外,程序也就二调用
ApplicationContex
的publishEvent()
方法来触发容器事件。
- 除此之外,程序也就二调用
- 通过
让Bean获取Spring容器:
接口有一个方法:
setBeanFactory(BeanFactory beanFactory)
spring容器会检测容器中的所有Bean,如果发现某个Bean实现了
ApplicationContextAware
接口,Spring容器会在创建该Bean后,自动调用该方法,调用该方法时,会将容器本身作为参数传给该方法。
示例:
public class Person implements ApplicationContextAware{ private ApplicationContext ctx; public void setApplicationContext(ApplicationContext ctx) throws BeansException{ this.ctx=ctx; } ... }
5.Spring容器中的Bean:
Bean的基本定义和Bean别名:
<bean id="person" class="..." name="#abc,@123,abc*" />
<alias name="person" alias="jack" />
<alias name="jack" alias="jackee" />
容器中Bean的作用域:
singleton 单例模式
prototype 每调用一次getBean就创建一个新的Bean实例
<bean id="..." class="..." scope="prototype" />
配置依赖:
设置普通属性值:
value用于指定基本类型及其包装、字符串类型的参数值
<property name="" value=""/>
配置合作者Bean:
属性值是另一个Bean的实例
<property name="" ref=""/>
使用自动装配注入合作者Bean:
不推荐使用
注入嵌套Bean:
<bean ...>
<property ...>
<bean class="..."/>无需id属性,spring容器不能访问嵌套bean
</property>
</bean>
组合属性:
先getBean() 然后再setter()
<bean id="a" class="...">
<property name="foo.bar.x.y" value="xxx" />
</bean>
相当于:
a.getFoo().getBar().getX().setY("xxx");
Spring的Bean 和JavaBean:
javabean通常作为DTO(数据传输对象),用来封装值对象,再各层之间传递数据。
spring的Bean 无所不包,任何组件都可被称为Bean
6.Spring提供的Java配置管理:
- 以xml为主,Java类配置为辅
- 以Java类配置为主,xml为辅
7.创建bean的三种方式:
调用构造器创建Bean
调用静态工厂方法创建Bean
<bean id="..." class="静态工厂类" factory-method="工厂方法" />
调用实例工厂方法创建Bean
先将工厂实例化为bean
然后:
<bean id="..." factory-bean="工厂bean的id" factory-method="方法" />
8.深入理解容器中的Bean:
抽象Bean和子bean:
抽象:
<bean id="parentId" class="..." abstract="true" />
子:
<bean id="..." class="..." parent="parentId" />
Bean继承与Java继承:
不同
容器中的工厂Bean:
FactoryBean
获取的是工厂bean的getObject()方法获取的值
获得Bean本身的id:
实现BeanNameAware接口
提供方法:
setBeanName(String name)
强制初始化Bean:
可以在初始化主调Bean之前,强制初始化一个或多个Bean
<bean id="a" class="..." depends-on="b" />
<bean id="b" class="..." />
9.容器中Bean的生命周期:
singleton作用域的Bean,Spring 可完全管理
prototype作用域的Bean,Spring 只负责创建,然后交给客户端后,容器不在跟踪其生命周期
管理Bean的生命周期行为主要有如下两个时机:
1.注入依赖关系之后
2.即将销毁Bean之前
依赖关系注入之后的行为:
使用init-method属性 指定某个方法在Bean全部依赖关系设置结束后自动执行。
实现InitializingBean接口
Bean销毁之前的行为:
使用destory-method属性 指定某个方法在Bean销毁之前被自动执行
实现DisposableBean接口
协调作用域不同步的Bean:
方法注入
<lookup-method name="..." bean="..."/>
10.高级依赖关系配置:
将任意方法的返回值、类或对象的Field值、其他Bean的getter方法返回值,直接定义成容器中的一个Bean
11.基于XML Schema的简化配置方式:
使用p:命名空间简化配置:【简化设值注入】
p:命名空间不需要特定的Schema定义,它直接存在于Spring内核中
可直接使用属性来配置参数值
<beans ...
xmlns:p="http://www.springframework.org/schema/p">
<bean id="..." class="..." p:age="22" p:axe-ref="Bean的id" />
...
</beans>
如果某属性名是以"-ref"结尾,则会引起冲突。
使用c:命名空间简化配置:【简化构造注入】
<beans ...
xmlns:c="http://www.springframework.org/schema/c">
<bean id="..." class="..." c:age="22" c:axe-ref="Bean的id" />
...
</beans>
使用util:命名空间简化配置:
必须导入最新的spring-util.xsd
util Schema提供如下几个元素:
constant 获取指定类的静态Field的值
property-path 获取指定对象的getter方法的返回值
list 定义一个List Bean
set 定义一个Set Bean
map 定义一个Map Bean
properties 加载一份资源文件,并根据加载的资源文件创建一个Properties Bean实例
<util:constant id="chin.age" static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE" />
<util:properties id="confTest" location="classpath:test_zh_CN.properties" />
<util:list id="chin.schools" list-class="java.util.LinkedList">
<value>小学</value>
<value>中学</value>
<value>大学</value>
</util:list>
<util:set id="chin.axes" set-class="java.util.HashSet">
<value>字符串</value>
<bean class="..."/>
<ref bean="stoneAxe"/>
</util:set>
<util:map id="chin.scores" map-class="java.util.TreeMap">
<entry key="语文" value="99" />
<entry key="数学" value="99" />
<entry key="英语" value="99" />
</util:map>
12.Spring提供的表达式语言(SpEL):
在运行时查询和操作对象图
使用Expression接口进行表达式求值:
SpEL的三个接口:
ExpressionParser: 负责解析一个SpEL表达式,返回一个Expression对象
Expression: 该接口的实例代表一个表达式
EvaluationContext: 代表计算表达式值得上下文
Expression包含的方法:
Object getValue(): 计算表达式的值
<T> T getValue(Class<T> desiredResultType): 计算表达式的值,并尝试将该值当成desiredResultType类型处理
Object getValue(EvaluationContext context): 用指定的EvaluationContext来计算表达式的值
<T> T getValue(EvaluationContext context,Class<T> desiredResultType)
Object getValue(Object rootObject): 以rootObject作为表达式的root对象来计算表达式的值
<T> T getValue(Object rootObject,Class<T> desiredResultType):
Bean定义中的表达式语言支持:
在XML配置文件和注解中使用SpEL时,在表达式外面增加#{}包围即可
SpEL语法详述:
1.直接量表达式:
就是在表达式中使用Java语言支持的直接量,包括字符串、日期、数值、boolean值和null。
2.在表达式中创建数组:
支持使用静态初始化、动态初始化两种语法来创建数组
3.在表达式中创建List集合:
{ele1,ele2,ele3 ...}
4.在表达式中访问List,Map等集合元素:
list[index]
map[key]
5.调用方法:
和Java一样
6.算术、比较、逻辑、赋值、三目等运算符:
7.类型运算符:
T()将括号内的字符串当成“类”处理
8.调用构造器:
直接使用new 来调用构造器
9.变量:
允许通过EvaluationContext来使用变量,该对象包含了一个setVariable(String name,Object value)方法,该方法用于设置一个变量。
在EvaluationContext中设置了变量,就可以在SpEL中通过#name来访问该变量。
#name
#this 引用SpEL当前正在计算的对象
#root 引用SpEL的EvaluationContext的root对象
10.自定义函数:
为Java方法重新起个名字
registerFunction(String name,Method m) 将m方法注册成自定义函数,该函数的名称为name
11.Elvis运算符:
name != null? name:"newVal"
简化为:
name?:"newVal"
12.安全导航操作:
foo?.bar 若foo为空,直接返回null,不会导致NullPointerException
13.集合选择:
collection.?[condition_expr]
"#myList.?[length()>7]"
14.集合投影:
collection.![condition_expr]
把collection集合中的元素依次传入condition_expr中,每个元素得到一个新结果,所有计算出来的结果组成的新结果就是该表达式的返回值
15.表达式模板:
允许在“直接量表达式”中插入一个或多个#{expr},#{expr}将会被动态计算出来。
深入使用Spring:
1.两种后处理器:
开发者可以通过后处理器对IoC容器进行扩展
Bean后处理器:对容器中的Bean进行后处理,对Bean进行额外加强
容器后处理器:对IoC容器进行后处理,用于增强容器功能
Bean后处理器:
必须实现BeanPostProcessor接口
接口包含两个方法:
Object postProcessBeforeInitialization(Object bean,String name) throws BeansException
Object postProcessAfterInitialization(Object bean,String name) throws BeansException
Spring常用的两个后处理器:
BeanNameAutoProxyCreator: 根据Bean实例的name属性,创建Bean实例的代理
DefaultAdvisorAutoProxyCreator: 根据提供的Advisor,对容器中的所有Bean实例创建代理
容器后处理器:
必须实现BeanFactoryPostProcessor接口
接口包含一个方法:
postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
PropertyPlaceholderConfigurer:属性占位符配置器
<context:property-placeholder location="classpath:dbconn.properties" />
使用:
"$(jdbc.url)"
属性文件dbconn.properties:
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=
jdbc.username=
jdbc.password=
PropertyOverrideConfigurer:重写占位符配置器 【直接覆盖bean的值】
<context:property-override location="classpath:db.properties" />
db.properties:
dataSource.driverClass=
dataSource.jdbcUrl=
dataSource.user=
dataSource.password=
2.Spring的“零配置”支持:
搜索Bean类:
注解:
@Component 标注一个普通的SpringBean类
@Controller 标注一个控制器组件类
@Service 标注一个业务逻辑组件类
@Respository 标注一个DAO组件类
<context:component-scan base-package="app.service"/>
指定Bean的作用域:
@Scope("prototype")
使用@Resource和@Value配置依赖:
@Resource name值,需要被注入的Bean实例的id 相当于<property.../>元素的ref属性
@Value 相当于<property.../>元素的value属性
使用@PostConstruct和@PreDestory定制生命周期行为:
前者用于注解Bean的初始化方法
后者用于注解Bean销毁之前的方法
使用@DependsOn和@Lazy改变初始化行为:
前者用于强制初始化其他Bean
后者用于指定该Bean是否取消预初始化
@DependsOn({"steelAxe","abc"})
@Lazy(true)
自动装配和精确装配:
@Autowired 标注setter时,默认采用byType自动装配策略
@Primary 修饰特定Bean类,当自动装配出现冲突时,选择该注解修饰的Bean
使用@Required检查注入:
@Required修饰方法,若被修饰的方法没有配置注入,则报出异常,以防止程序运行时引发NPE异常,而找不到出错点。
3.资源访问:
程序访问资源(各种类型的文件,二进制流)
URL:
java.net.URL
Spring改进Java资源访问的策略,提供Resource接口
该接口是所有资源访问类所实现的接口:
Resource接口的方法;
getInputStream()
exists()
isOpen()
getDescription()
getFile()
getURL()
Spring提供的Resource实现类:
UrlResource
ClassPathResource 对于web应用,ClassPathResource可以自动搜索位于WEB-INF/classes下的资源文件
FileSystemResource
ServletContextResource 访问应用的相关资源
InputStreamResource
ByteArrayResource 访问字节数组
针对不同的底层资源,这些实现类提供响应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问
示例:
UrlResource ur=new UrlResource("file:book.xml");
ClassPathResource:
显示创建:ClassPathResource cr=new ClassPathResource("book.xml");
常用隐式创建:spring识别字符串参数包含classpath:前缀后,系统会自动创建ClassPathResource对象
FilePathResource:
显示创建:FilePathResource fr=new FilePathResource("book.xml");
常用隐式创建:spring识别字符串参数包含file:前缀后,系统会自动创建FilePathResource对象
ServletContextResource:
// 从Web Context下的WEB-INF路径下读取book.xml资源
ServletContextResource src = new ServletContextResource(application , "WEB-INF/book.xml");
可以访问WEB-INF中的资源了
访问字节数组资源:
InputStreamResource效率低,尽量使用ByteArrayResource或FileSystemResource替代它
先从InputStream流中读出字节数组,然后以字节数组来创建ByteArrayResource。这样,InputStreamResource被转换为ByteArrayResource,从而方便多次读取。
ResourceLoader接口和ResourceLoaderAware接口:
ResourceLoader接口:实现类的示例可以获得一个Resource示例
ResourceLoaderAware接口:实现类的示例可以获得一个ResourceLoader的引用
ResourceLoader接口的方法:
Resource getResource(String location)
ApplicationContext的实现类都实现了ResourceLoader接口,因此可直接获取Resource示例
使用Resource作为属性:
<bean id="test" class="org.crazyit.app.service.TestBean"
p:res="classpath:book.xml"/>
在ApplicationContext中使用资源:
显式声明:
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans*.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("beans.xml");
4.Spring的AOP:
OOP将程序分解成各个层次的对象,从静态角度考虑程序结构
AOP将程序运行过程分解成各个切面,从动态角度考虑程序运行过程
为什么需要AOP:
- AOP专门用于处理系统中分布于各模块(不同方法)中的交叉关注点的问题。
- 在Java EE应用中,常常用AOP来处理一些具有横切性质的系统级服务,
- 如事务管理、安全检查、缓存、对象池管理等。
使用AspectJ实现AOP:
AspectJ是一个基于Java语言的AOP框架
AspectJ主要包括两部分:
- 定义如何表达、定义AOP编程中的语法规范
- 工具部分,包括编译器、调试工具等
// 指定在执行org.crazyit.app.service包中任意类的、任意方法之前执行下面代码块 // 第一个星号表示返回值不限;第二个星号表示类名不限; // 第三个星号表示方法名不限;圆括号中..代表任意个数、类型不限的形参 before(): execution(* org.crazyit.app.service.*.*(..)) { System.out.println("模拟进行权限检查..."); } // 定义一个PointCut,其名为logPointcut, // 该Pointcut代表了后面给出的切入点表达式,这样可复用该切入点表达式 pointcut logPointcut() :execution(* org.crazyit.app.service.*.*(..)); after():logPointcut() { System.out.println("模拟记录日志..."); } // 指定在执行org.crazyit.app.service包中任意类的、任意方法时执行下面代码块 Object around():call(* org.crazyit.app.service.*.*(..)) { System.out.println("模拟开启事务..."); // 回调原来的目标方法 Object rvt = proceed(); System.out.println("模拟结束事务..."); return rvt; }
AOP的基本概念:
- AOP框架能处理程序执行中特定的切入点(Pointcut),而不与具体类耦合
- AOP框架两个特征:
- 1.各步骤之间的良好隔离性
- 2.源代码无关性
- 关于切面编程的一些术语:
- 切面(Aspect):切面用于组织多个Advice,Advice放在切面中定义
- 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出。
- 增强处理(Advice):AOP框架在特定的切入点执行的增强处理。处理有“around”,“before”,“after”等类型
- 切入点(Pointcut): 可以增强处理的连接点。连接点被添加增强处理,该连接点就变成了切入点。如何使用表达式来定义切入点是AOP的核心。
- 引入:将方法或字段添加到被处理的类中
- 目标对象:被AOP框架进行增强处理的对象
- AOP代理:AOP框架创建的对象,代理就是对目标对象的加强。
- 可以是JDK动态代理,也可以是cglib代理。前者为实现接口的目标对象的代理,后者不实现接口。
- 织入(Weaving):将增强处理添加到目标对象中,并创建一个被增强的对象(AOP代理)的过程就是织入。
- 织入的两种实现方式:
- 1.编译时增强,如AsoectJ
- 2.运行时增强,如Sping AOP
- 织入的两种实现方式:
Spring的AOP支持:
- spring目前仅支持将方法调用作为连接点
- AOP编程三件事:
- 定义普通业务组件
- 定义切入点,一个切入点可能横切多个业务组件
- 定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作
- AOP代理的方法 = 增强处理 + 目标对象的方法
- Spring的两种选择来定义切入点和增强处理:
- 1.基于注解的“零配置”方式:使用@Aspect,@Pointcut等注解标注切入点和增强处理。
- 2.基于XML配置文件的管理方式:使用spring配置文件来定义切入点和增强处理
基于注解的“零配置”方式:
<!-- 启动@AspectJ支持 -->
aop:aspectj-autoproxy/Jar包: aspectjweaver.jar aspectjrt.jar aopalliance.jar
定义切面Bean: @Aspect 切面类 定义Before增强处理:@Before("切入点表达式") 示例: @Before("execution(* org.crazyit.app.service.impl.*.*(..))") 定义切面Bean: @Aspect 切面类 定义Before增强处理: @Before("切入点表达式") 示例: @Before("execution(* org.crazyit.app.service.impl.*.*(..))") 定义AfterReturning增强处理: // 匹配org.crazyit.app.service.impl包下所有类的、 // 所有方法的执行作为切入点 @AfterReturning(returning="rvt", pointcut="execution(* org.crazyit.app.service.impl.*.*(..))") @AfterReturning的两个常用属性: pointcut/value:指定切入点对应的切入表达式 returning:指定一个形参名,该形参可用于访问目标方法的返回值 如果增强方法的形参类型定义好了,若于目标方法返回值类型不同, 则增强方法不会织入。 定义AfterThrowing增强处理: 主要用于处理程序中未处理的异常 @AfterThrowing(throwing="ex", pointcut="execution(* org.crazyit.app.service.impl.*.*(..))") pointcut/value:指定切入点对应的切入表达式 throwing:指定一个形参名,该形参可用于访问目标方法的返回值 如果增强方法的形参异常类型定义好了,若于目标方法返回值异常类型不同, 则增强方法不会织入。 After: 不管目标方法成功完成,还是异常结束,该Advice都会织入 这种Advice类似于finally块 通常用来释放资源 @After("execution(* org.crazyit.app.service.*.*(..))") Around: 这种Advice功能最强大,它既可访问、修改目标方法调用参数,也可访问、修改目标方法的返回值。 它甚至完全组阻止目标方法的执行 它既可在目标方法之前织入,也可以在目标方法之后织入。 弱点:它是线程不安全的,能用其他Advice搞定的,就不要把Around Adivce拿出来。 Around Advice方法必须满足如下三点: 1.必须声明返回值类型 2.必须声明一个类型为ProceedingJoinPoint类型的形参 3.必须调用ProceedingJoinPoint形参的proceed()方法,这就是回调目标方法。如果没有这一行,目标方法不会执行。 @Around("execution(* org.crazyit.app.service.impl.*.*(..))") public Object processTx(ProceedingJoinPoint jp) throws java.lang.Throwable{ System.out.println("执行目标方法之前,模拟开始事务..."); // 获取目标方法原始的调用参数 Object[] args = jp.getArgs(); if(args != null && args.length > 1) { // 修改目标方法的第一个参数 args[0] = "【增加的前缀】" + args[0]; } // 以改变后的参数去执行目标方法,并保存目标方法执行后的返回值 Object rvt = jp.proceed(args); System.out.println("执行目标方法之后,模拟结束事务..."); // 如果rvt的类型是Integer,将rvt改为它的平方 if(rvt != null && rvt instanceof Integer) rvt = (Integer)rvt * (Integer)rvt; return rvt;
}一般来说,需要修改目标方法的调用参数、修改目标方法的返回值,此时只能用Around 组织目标方法执行,用before 访问目标方法的参数: JoinPoint类型: 方法: Object[] getArgs() 返回执行目标方法时的参数 Signature getSignature() 返回被增强的方法的相关信息 Object getTarget() 返回目标对象 Object getThis() 返回代理对象 定义切入点: 切入点定义: 一个切入点表达式 一个包含名字和任意参数的方法签名 @Aspect public class SystemArchitecture { @Pointcut("execution(* org.crazyit.app.service.impl.*.*(..))") public void myPointcut(){} } // 直接使用SystemArchitecture切面类的myPointcut()切入点 @AfterReturning(returning="rvt", pointcut="SystemArchitecture.myPointcut()") 切入点指示符,组合符号: // 下面的args(arg0,arg1)会限制目标方法必须有2个形参 @AfterReturning(returning="rvt" , pointcut="execution(* org.crazyit.app.service.impl.*.*(..)) && args(arg0,arg1)")
5.Spring的缓存机制:
6.Spring的事务:
@Transactional(propagation=Propagation.REQUIRED ,isolation=Isolation.DEFAULT , timeout=5)
<!-- 配置JDBC数据源的局部事务管理器,使用DataSourceTransactionManager 类 -->
<!-- 该类实现PlatformTransactionManager接口,是针对采用数据源连接的特定实现-->
<!-- 配置DataSourceTransactionManager时需要依注入DataSource的引用 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
<!-- 根据Annotation来生成事务代理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>