学习资料:Spring官方文档
Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。
当我们自己写一个简易的程序的时候,我们总会要new很多对象,但如果要写一个大项目呢,多个类之间互相耦合,关系复杂,不宜处理。这个时候,有人就想到了,搞一个容器,它负责所有的类的生成、注入、装配,来解除耦合,这就是IOC容器。
通俗来讲,就是将new实例这个过程,放到spring(IOC)容器里来做。当项目初始化的时候,spring(IOC)容器会先实例化要所有使用的类,然后需要用到这个实例的时候,从spring(IOC)容器中将这个实例注入即可。那么怎么告诉spring哪些类要实例化、怎么实例化呢?Spring可以通过xml或者注解获取这些信息。
要求:有无参构造和set方法
解释:就是通过调用set方法,将对应的属性注入,可以直接注入值(property),也可以注入bean的引用(ref)或者一些集合。
样例:
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.pojo.Address">
<property name="address" value="xian">property>
bean>
<bean id="student" class="com.pojo.Student">
<property name="name" value="hou"/>
<property name="address" ref="address"/>
<property name="books">
<array>
<value>三国value>
<value>西游value>
<value>水浒value>
array>
property>
<property name="hobbies">
<list>
<value>eatvalue>
<value>drinkvalue>
<value>playvalue>
list>
property>
<property name="card">
<map>
<entry key="1" value="12"/>
<entry key="2" value="23"/>
map>
property>
<property name="game">
<set>
<value>wangzhevalue>
<value>daotavalue>
<value>lolvalue>
set>
property>
<property name="wife">
<null>null>
property>
<property name="infor">
<props>
<prop key="id">20200405prop>
<prop key="name">hdkprop>
props>
property>
bean>
beans>
补充:bean中可以补充name设置别名(分隔符可以用逗号,空格或者分号),也可以直接设置别名
<bean id="usert" class="com.hou.pojo.UsetT" name="u1,u2">
<property name="name" value="kun"/>
bean>
<alias name="user" alias="user2aaa"/>
要求:有一个或多个有参构造
解释:就是通过spring的有参构造(constructor-arg),将对应的属性注入
样例:
<bean id="user" class="com.hou.pojo.User">
<constructor-arg name="name" value="hou">constructor-arg>
bean>
要求:p的命名空间以及set注入要求 c的命名空间以及构造器注入要求
解释:p注入就是对set注入的一个简化封装,c注入就是对构造器注入的简化封装
可以直接通过"p:属性名"或者"c:属性名"的方式直接在bean里进行赋值,
样例:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="use" class="com.pojo.User" p:name="dong" p:age="10">
bean>
<bean id="use2" class="com.pojo.User" c:name="kun" c:age="19" scope="prototype">bean>
beans>
补充:scope作用域
scope是bean里的一个属性,可以指定使用单例模式还是原型模式或者web中的session、request、application、websocket
单例就是只使用一个实例,原型就是每次注入的时候都new一个新的实例,默认是单例
也可以通过注解@scope实现
将一个复杂的类的配置进行简化。
可以使用bean的autowire属性,以byName或者byType的方式进行装配。
但用注释进行装配会更加简单,这里只介绍注释自动装配。
@Autowired注释会先根据类型进行注入,如果有多个满足的类型,再根据name进行注入
注解装配条件:
例子:
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="cat111" class="com.pojo.Cat"/>
<bean id="dog" class="com.pojo.Dog"/>
<bean id="people" class="com.pojo.People">
bean>
beans>
public class People {
@Autowired
private Cat cat;
@Autowired
@Qualifier(value = "dog")
private Dog dog;
private String name;
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
补充:
@Nullable:代表对象可以为null,放在类型前面
@Autowired(required=false):代表@Nullable
@Qualifier(value=""):当@Autowired环境很复杂时,手动指定装配的实例名称,配合@Autowired使用
@Resource:
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。
约束:
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.pojo"/>
beans>
按照不同的指责可以使用 组件@Component,控制器@Controller,DAO@Repository,服务@Service,代表这个类被Spring管理;
用@Value("")来对属性进行赋值,可以放在set方法或者属性上面。
package com.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
//等价于
@Component
@Scope("prototype")
public class User {
@Value("dong")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
XML与注解比较
–XML可以适用任何场景 ,结构清晰,维护方便
–注解不是自己提供的类使用不了,开发简单方便
xml与注解整合开发 :推荐最佳实践
–xml管理Bean
–注解完成属性注入
–使用过程中, 可以不用扫描,扫描是为了类上的注解
补充:
使用JavaConfig的方式配置Spring,可以用一个配置类代替xml配置
写一个配置类
package com.config;
import com.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration //这个也会被spring容器托管,注册到容器中,因为他本来就是一个@Component
@ComponentScan("com.pojo")
@Import(Config2.class)
public class MyConfig {
@Bean
public User getUser(){
return new User();
}
}
将加载xml上下文改成加载config上下文
import com.config.MyConfig;
import com.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Mytest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User) context.getBean("getUser");
System.out.println(user);
}
}
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP可以在不改变原有代码的基础上,通过动态代理从切面增加新的功能。
主流的动态代理方式有两种:java原生 和 cglib
java原生的动态代理需要实现接口,cglib不用
springboot2.0之前默认使用javaJDK的动态代理,2.0以后默认cglib,可以通过手动配置去修改默认的配置。
JDK的动态代理需要了解两个类 : InvocationHandler 和 Proxy
InvocationHandler:调用处理程序
每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用将被编码并分派到其调用的invoke方法。
看例子里面的Client中调用proxy.rent(),相当于就是通过反射,将rent方法的信息作为method参数,调用了代理类中的invoke方法。
Object invoke(Object proxy, 方法 method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理的超类。
自己写一个代理类继承InvocationHandler,重写invoke方法,
写一个getProxy方法,通过Proxy的newProxyInstance静态方法获取proxy实例
package com;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//会这个类,自动生成代理类
public class ProxyInvocation implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces()
,this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
Object result = method.invoke(rent, args);
fare();
return result;
}
public void seeHouse(){
System.out.println("see house");
}
public void fare(){
System.out.println("fare");
}
}
package com;
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色
ProxyInvocation proxyInvocation = new ProxyInvocation();
//通过调用程序处理角色来处理我们要调用的接口对象
proxyInvocation.setRent(host);
Rent proxy = (Rent) proxyInvocation.getProxy(); //这里的proxy是动态生成的
proxy.rent();
}
}
spring使用的是最广泛的aspectJ
提供声明式事务;允许用户自定义切面
以下名词需要了解下:
提供五种类型的Advice:
依赖:
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
和IOC一样,都可以使用配置文件或者注解实现
public class Log implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//objects : 被调用的方法的参数
//Object : 目标对象
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
//method被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + target.getClass().getName()
+"的"+method.getName()+"方法,"
+"返回值:"+returnValue);
}
}
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.kuang.service.UserServiceImpl"/>
<bean id="log" class="com.kuang.log.Log"/>
<bean id="afterLog" class="com.kuang.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>
beans>
也可以将advice写入配置文件里,这样自己定义的切片类就不用继承五个advice了。
第一步 : 写我们自己的一个切入类
public class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}
去spring中配置
<bean id="diy" class="com.kuang.config.DiyPointcut"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="diyPonitcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<aop:before pointcut-ref="diyPonitcut" method="before"/>
<aop:after pointcut-ref="diyPonitcut" method="after"/>
aop:aspect>
aop:config>
测试:
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
注解里的value详解:
从左到右依次是:
execution(): 表达式主体。
第一个*号:表示返回类型,*号表示所有的类型。
包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法,*号表示所有的类。
*(…):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
和上面的区别可以看这个例子
如果用execution拦截main方法,会拦截第11行
如果用call拦截main,会拦截第8行
方法执行:execution(MethodSignature)
方法调用:call(MethodSignature)
构造器执行:execution(ConstructorSignature)
构造器调用:call(ConstructorSignature)
类初始化:staticinitialization(TypeSignature)
属性读操作:get(FieldSignature)
属性写操作:set(FieldSignature)
例外处理执行:handler(TypeSignature)
对象初始化:initialization(ConstructorSignature)
对象预先初始化:preinitialization(ConstructorSignature)
Advice执行:adviceexecution()
切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指示符如下:
execution:用于匹配方法执行的连接点;
within:用于匹配指定类型内的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within:用于匹配所有持有指定***注解***类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的***注解***;
@args:用于匹配当前执行的方法传入的参数持有指定***注解***的执行;
@annotation:用于匹配当前执行方法持有指定***注解***的方法;
package com.kuang.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
接着:在Spring配置文件中,注册bean,并增加支持注解的配置
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
在切面里无法autowired获取bean,想要使用spring容器中的组件,需要使用通过BeanFactory获取bean或者懒加载需要使用的bean!!!具体使用方法可以参考若依里面的SpringUtils。
通过实现接口BeanFactoryPostProcessor可以获取beanFactory;
通过实现接口ApplicationContextAware可以获取applicationContext;
/**
* spring工具类 方便在非spring管理环境中获取bean
*
* @author ruoyi
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;
private static ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
SpringUtils.applicationContext = applicationContext;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
return (T) beanFactory.getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*
*/
public static <T> T getBean(Class<T> clz) throws BeansException
{
T result = (T) beanFactory.getBean(clz);
return result;
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name)
{
return beanFactory.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getAliases(name);
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker)
{
return (T) AopContext.currentProxy();
}
/**
* 获取当前的环境配置,无配置返回null
*
* @return 当前的环境配置
*/
public static String[] getActiveProfiles()
{
return applicationContext.getEnvironment().getActiveProfiles();
}
/**
* 获取当前的环境配置,当有多个环境配置时,只获取第一个
*
* @return 当前的环境配置
*/
public static String getActiveProfile()
{
final String[] activeProfiles = getActiveProfiles();
return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
}
}
spring需要在xml中配置dataSource、sqlSessionFactory
不过现在都用springboot了,就直接讲一下springboot的yml常用配置吧~
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.example.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
typeAliasesPackage可以允许我们在mapper中,resultType只写类名,不需要写完整的com.**的路径
mapperLocations是我们需要扫描的mapper位置,通过通配符可以从不同的地方拿到mapper.xml
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.0version>
dependency>
参考博文:https://blog.csdn.net/wkl305268748/article/details/77619367
在Spring中,事务有两种实现方式,分别是编程式事务管理和声明式事务管理两种方式。
编程式事务管理: 编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
声明式事务管理: 建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务管理不需要入侵代码,通过@Transactional就可以进行事务操作,更快捷而且简单。
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。
只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用Hibernate的时候。
默认为读写事务。