Spring
一、Spring Aop
【1】为什么用AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
横切关注点,方便解耦合
【2】AOP实现原理
(1)jdk动态代理
Client
public class Client {
public static void main(String args[]) {
// 要代理的目标对象
ArrayList<Person> personArrayList = new ArrayList<Person>();
// 生成目标对象的代理对象,中间插入了新的执行片段
Collection o = (Collection) Proxy.newProxyInstance(ArrayList.class.getClassLoader(),
new Class[] {
Collection.class }, new JdkLogInvokeHandler(personArrayList));
System.out.println(o.getClass().getName());
o.add(new Person(1, "zhang"));
System.out.println(o.size());
}
}
JdkLogInvokeHandler
public class JdkLogInvokeHandler implements InvocationHandler {
private Object target;
public JdkLogInvokeHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("........ log start ......");
Object o = method.invoke(target, args);
System.out.println("........ log end ........");
return o;
}
}
原理
[1] 先动态生成被代理接口增强的代理类的字节码class文件
// 生成字节码数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
// 转成类对象
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
[2] 加载class文件
怎么加载class文件,
怎么生成代理的对象
[3] 调用构造方法,把InvocationHanler对象传递进来,InvocationHanler对象中持有原来的那个被代理对象。
[4] 实际方法执行是在InvocationHandler中invoke时被代理对象执行相应的方法,同时在执行前后实现一定的功能增强
为何jdk动态代理只能代理接口,不能代理类?
因为生成代理类继承了Proxy类,java是单继承的所以不能再继承别的类了。
(2)cglib代理
目标类
public class ClassHasNoInterface {
public void method(){
System.out.println("hello");
}
public void function(){
System.out.println("world");
}
}
代理类
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibTs implements MethodInterceptor{
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz){
//生成指定类对象的子类,也就是重写类中的业务函数,在重写中加入intercept()函数而已。
enhancer.setSuperclass(clazz);
//这里是回调函数,enhancer中肯定有个MethodInterceptor属性。
enhancer.setCallback(this);
//创建这个子类对象
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println(method.getName()+"before method");
Object result = proxy.invokeSuper(obj,args);
System.out.println(method.getName()+"after method");
return result;
}
}
测试类
public class MainTest {
public static void main(String[] args) {
CglibTs ct = new CglibTs();
ClassHasNoInterface chni = (ClassHasNoInterface)ct.getProxy(ClassHasNoInterface.class);
chni.method();
chni.function();
}
}
依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
Cglib是通过继承实现的,通过生成一个继承被代理类的子类的字节码实现。
(3)spring Aop代理方式
普通实例一:日志
Logging
@Aspect
public class Logging {
@Pointcut("execution(* com.suning.aspect.*.*(..))")
private void selectAll() {
}
@Before("selectAll()")
public void beforeAdvice() {
System.out.println("Going to setup student profile.");
}
@After("selectAll()")
public void afterAdvice() {
System.out.println("Student profile has been setup.");
}
@AfterReturning(pointcut = "selectAll()", returning = "retVal")
public void afterReturningAdvice(Object retVal) {
System.out.println("Returning:" + retVal.toString());
}
@AfterThrowing(pointcut = "selectAll()", throwing = "ex")
public void AfterThrowingAdvice(IllegalArgumentException ex) {
System.out.println("There has been an exception: " + ex.toString());
}
}
Student
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age);
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name : " + name);
return name;
}
public void printThrowException() {
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
MainApp
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-aspect.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
spring-aspect.xml
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:aspectj-autoproxy/>
<!-- Definition for student bean -->
<bean id="student" class="com.suning.aspect.Student">
<property name="name" value="Zara"/>
<property name="age" value="11"/>
</bean>
<!-- Definition for logging aspect -->
<bean id="logging" class="com.suning.aspect.Logging"/>
</beans>
输出
Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
更常用的实例二:流控组件
通过注解实现
Filter
@Target({
java.lang.annotation.ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Filter {
String name();
String path() default "/filterConfig";
int requestIndex() default 0;
String caller() default "";
}
FlowCacheThrottleAspect
@Aspect
@Component
public class FlowCacheThrottleAspect {
@Autowired
GenericThrottleCompFactory factory;
public FlowCacheThrottleAspect() {
}
@Pointcut("@annotation(com.suning.cis.component.filter.Filter)")
public void filterPointCut() {
}
@Around("filterPointCut()")
public Object invoke(ProceedingJoinPoint invocation) throws Throwable {
Method method = ((MethodSignature) invocation.getSignature()).getMethod();
Filter filter = (Filter) AnnotationUtils.getAnnotation(method, Filter.class);
dosomething
}
}
实现原理
容器初始化的时候什么时候生成的代理bean并保存到容器中的
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
AbstractAutoProxyCreator
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
创建代理对象
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
proxyFactory.copyFrom(this);
if (!shouldProxyTargetClass(beanClass, beanName)) {
// Must allow for introductions; can't just set interfaces to
// the target's interfaces only.
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
for (Class<?> targetInterface : targetInterfaces) {
proxyFactory.addInterface(targetInterface);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 此处创建
return proxyFactory.getProxy(this.proxyClassLoader);
}
获取代理并创建代理对象
public Object getProxy(ClassLoader classLoader) {
// 获取代理,然后通过代理创建
return createAopProxy().getProxy(classLoader);
}
创建代理,jdk代理还是cglib代理
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
// 生成jdk代理
return new JdkDynamicAopProxy(config);
}
if (!cglibAvailable) {
throw new AopConfigException(
"Cannot proxy target class because CGLIB2 is not available. " +
"Add CGLIB to the class path or specify proxy interfaces.");
}
// 生成cglib代理
return CglibProxyFactory.createCglibProxy(config);
}
else {
// 生成jdk代理
return new JdkDynamicAopProxy(config);
}
}
JdkDynamicAopProxy
jdk代理创建代理对象的方法
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 创建代理对象
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
Cglib2AopProxy
cglib创建代理对象的方法
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB2 proxy: target source is " + this.advised.getTargetSource());
}
try {
Class rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class[] additionalInterfaces = rootClass.getInterfaces();
for (Class additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass);
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setInterceptDuringConstruction(false);
Callback[] callbacks = getCallbacks(rootClass);
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
Class[] types = new Class[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
Object proxy;
if (this.constructorArgs != null) {
proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
}
else {
proxy = enhancer.create();
}
return proxy;
}
catch (CodeGenerationException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Exception ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
二、Spring IOC
【1】什么是IOC和DI
IOC叫控制反转,DI依赖注入是IOC控制反转实现的方式。
反转的是什么?
反转的是bean的创建时机,bean创建的机制。
传统的bean创建方法有两种:
一是直接new 一个对象:通过这种方式创建的话代码的耦合性很强,同时每次都需要new一个新的对象的话产生的垃圾也越多;还有就是不便于修改,比如需要换一个其他的对象来实现的话,改动复杂,不是面向抽象来编程。
另一个是通过工厂模式来创建对象:通过工厂创建可以很大程度上解决new对象的问题,但是还是会存在强耦合的问题。
传统的两种方式都是在需要用到对象的时候去生产一个对象;IOC的思想与之正好相反,它先将对象给到需要用的地方,要用的时候直接拿这个对象来使用即可。
【2】spring bean依赖注入方式
构造方法注入、属性注入、setter方法注入
构造器注入
private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;
@Autowired
public DI(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) {
this.dependencyA = dependencyA;
this.dependencyB = dependencyB;
this.dependencyC = dependencyC;
}
属性注入
@Autowired
private DependencyA dependencyA;
@Autowired
private DependencyB dependencyB;
@Autowired
private DependencyC dependencyC;
setter方法注入
private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;
@Autowired
public void setDependencyA(DependencyA dependencyA) {
this.dependencyA = dependencyA;
}
@Autowired
public void setDependencyB(DependencyB dependencyB) {
this.dependencyB = dependencyB;
}
@Autowired
public void setDependencyC(DependencyC dependencyC) {
this.dependencyC = dependencyC;
}
【3】循环依赖处理
首先明确原型模式不支持循环依赖,会抛出异常;单例模式才支持循环依赖,并且单例模式通过构造函数注入依赖也不支持循环依赖,这跟解决循环依赖的设计相关提前暴露创建中的单例
首先,Spring内部维护了三个Map,也就是通常说的三级缓存。
缓存 | 用途 |
---|---|
singletonObjects | 用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用,单例对象池 |
earlySingletonObjects | 存放原始的 bean 对象(尚未填充属性),用于解决循环依赖 |
singletonFactories | 存放 bean 工厂对象,用于解决循环依赖 |
所谓的”早期引用“是指向原始对象的引用。所谓的原始对象是指刚创建好的对象,但还未填充属性。
在容器再次发现 beanB 依赖于 beanA 时,容器会获取 beanA 对象的一个早期的引用(early reference),并把这个早期引用注入到 beanB 中,让 beanB 先完成实例化。beanB 完成实例化,beanA 就可以获取到 beanB 的引用,beanA 随之完成实例化。
【4】bean创建过程
整体流程如下
从上面的流程图中,可以看到一个 Bean 加载会经历这么几个阶段(用绿色标记):
(a) 获取 BeanName
对传入的 name 进行解析,转化为可以从 Map 中获取到 BeanDefinition 的 bean name。
BeanFactory.getBean
中传入的 name,有可能是这几种情况:
- bean name,可以直接获取到定义 BeanDefinition。
- alias name,别名,需要转化。
- factorybean name, 带
&
前缀,通过它获取 BeanDefinition 的时候需要去除 & 前缀。
为了能够获取到正确的 BeanDefinition,需要先对 name 做一个转换,得到 beanName。
(b) 合并 Bean 定义
对父类的定义进行合并和覆盖,如果父类还有父类,会进行递归合并,以获取完整的 Bean 定义信息。我们从配置文件读取到的 BeanDefinition 是 GenericBeanDefinition。它的记录了一些当前类声明的属性或构造参数,在后续实例化 Bean 的时候,使用的 BeanDefinition 是 RootBeanDefinition 类型而非 GenericBeanDefinition,因此需要合并父类的BeanDefinition .
© 实例化
使用构造或者工厂方法创建 Bean 实例。createBeanInstance()
获取到完整的 RootBeanDefintion 后,就可以拿这份定义信息来实例具体的 Bean。
具体实例创建见 AbstractAutowireCapableBeanFactory.createBeanInstance
,返回 Bean 的包装类 BeanWrapper,一共有三种策略:
[1] 使用工厂方法创建,instantiateUsingFactoryMethod
。
[2] 使用有参构造函数创建,autowireConstructor
。
[3] 使用无参构造函数创建,instantiateBean
。
(d) 属性填充
populateBean(); 寻找并且注入依赖,依赖的 Bean 还会递归调用 getBean
方法获取。
应用 InstantiationAwareBeanPostProcessor 处理器,在属性注入前后进行处理。假设我们使用了 @Autowire 注解,这里会调用到 AutowiredAnnotationBeanPostProcessor 来对依赖的实例进行检索和注入的,它是 InstantiationAwareBeanPostProcessor 的子类。
(e) 初始化
调用自定义的初始化方法。 initializeBean()
[1] 触发 Aware : Spring 在初始化阶段,如果判断 Bean 实现了Aware接口,就会往 Bean 中注入它关心的资源。Aware接口包括:BeanFactoryAware、ApplicationContextAware、ResourceLoaderAware、ServletContextAware
[2] 触发 BeanPostProcessor : 做一些增强操作比如打日志、做校验、属性修改、耗时检测等等。Spring 框架提供了 BeanPostProcessor 来达成这个目标。比如我们使用注解 @Autowire 来声明依赖,就是使用 AutowiredAnnotationBeanPostProcessor
来实现依赖的查询和注入的。
[3] 触发自定义 init : 自定义初始化有两种方式可以选择, a) 实现 InitializingBean。提供了一个很好的机会,在属性设置完成后再加入自己的初始化逻辑。b) 定义 init 方法。自定义的初始化逻辑。见 AbstractAutowireCapableBeanFactory.invokeInitMethods
:
(f) 获取最终的 Bean
如果是 FactoryBean 需要调用 getObject 方法,如果需要类型转换调用 TypeConverter 进行转化。见 AbstractBeanFactory.doGetBean
【5】bean的生命周期
在前面的bean创建过程中也有体现
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 使用
- 销毁 Destruction
【6】获取bean的方式
spring获取bean的几种方式
ApplicationContext常用实现类 | 作用 |
---|---|
AnnotationConfigApplicationContext | 从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式。 |
ClassPathXmlApplicationContext | 从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式。 |
FileSystemXmlApplicationContext | 从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件。 |
AnnotationConfigWebApplicationContext | 专门为web应用准备的,适用于注解方式。 |
XmlWebApplicationContext | 从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。 |
【7】bean的作用域
Bean 的作用域类型:
类型 | 说明 |
---|---|
singleton | 在 Spring 容器中仅存在一个 Bean 实例, Bean 以单例的形式存在。 |
prototype | 每次从容器中调用 Bean 时,都会返回一个新的实例,即相当于执行 new XxxBean() 的实例化操作。 |
request | 每次 http 请求都会创建一个新的 Bean , 仅用于 WebApplicationContext 环境。 |
session | 同一个 http Session 共享一个 Bean ,不同的 http Session 使用不同的 Bean,仅用于 WebApplicationContext 环境。 |
globalSession | 同一个全局 Session 共享一个 bean, 用于 Porlet, 仅用于 WebApplication 环境。 |
低版本的 Spring 中,仅支持两个 Bean 作用域(singleton 与 prototype),所以之前的配置为 singleton=true/false
。Spring 为了向后兼容,依然支持这种配置方式。我们推荐采用新的配置方式 scope=<作用域类型>
。
三、Spring 事务管理
spring事务的属性;通过@Transaction注解可以查看
属性名称 | 含义 | 默认值 |
---|---|---|
事务的标识 | String value() | “” 空 |
隔离级别 | Propagation propagation() | 同数据库隔离级别 |
传播行为 | Isolation isolation() | REQUIRED |
是否只读 | boolean readOnly() | false |
超时时间 | int timeout() | -1,不超时 |
需要进行回滚的异常 | Class<? extends Throwable>[] | 空,所有运行时异常 |
需要进行回滚的异常名称 | String[] rollbackForClassName() | 空 |
不需要进行回滚的异常 | Class<? extends Throwable>[] | 空 |
不需要回滚的异常名称 | String[] noRollbackForClassName() | 空 |
【1】spring事务的隔离级别
默认和数据库的隔离级别一致
包括:
1、读未提交 :可能出现脏读、不可重复读、幻读
2、读已提交 :可能出现不可重复读、幻读
3、可重复读:默认的隔离级别,可能出现幻读
4、串行 :没有上述问题
public enum Isolation {
/**
* Use the default isolation level of the underlying datastore.
* All other levels correspond to the JDBC isolation levels.
* @see java.sql.Connection
*/
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
/**
* A constant indicating that dirty reads, non-repeatable reads and phantom reads
* can occur. This level allows a row changed by one transaction to be read by
* another transaction before any changes in that row have been committed
* (a "dirty read"). If any of the changes are rolled back, the second
* transaction will have retrieved an invalid row.
* @see java.sql.Connection#TRANSACTION_READ_UNCOMMITTED
*/
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
/**
* A constant indicating that dirty reads are prevented; non-repeatable reads
* and phantom reads can occur. This level only prohibits a transaction
* from reading a row with uncommitted changes in it.
* @see java.sql.Connection#TRANSACTION_READ_COMMITTED
*/
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
/**
* A constant indicating that dirty reads and non-repeatable reads are
* prevented; phantom reads can occur. This level prohibits a transaction
* from reading a row with uncommitted changes in it, and it also prohibits
* the situation where one transaction reads a row, a second transaction
* alters the row, and the first transaction rereads the row, getting
* different values the second time (a "non-repeatable read").
* @see java.sql.Connection#TRANSACTION_REPEATABLE_READ
*/
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
/**
* A constant indicating that dirty reads, non-repeatable reads and phantom
* reads are prevented. This level includes the prohibitions in
* <code>ISOLATION_REPEATABLE_READ</code> and further prohibits the situation
* where one transaction reads all rows that satisfy a <code>WHERE</code>
* condition, a second transaction inserts a row that satisfies that
* <code>WHERE</code> condition, and the first transaction rereads for the
* same condition, retrieving the additional "phantom" row in the second read.
* @see java.sql.Connection#TRANSACTION_SERIALIZABLE
*/
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
private final int value;
Isolation(int value) {
this.value = value; }
public int value() {
return this.value; }
}
【2】spring事务的传播方式
需要思考的问题:
如果事务1中方法A的dosomething失败抛出异常,事务2需不需要回滚
事务传播行为类型 | 说明 | 事务1存在 | 事务1不存在 |
---|---|---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择,提交是一起提交的。 | do1+do2+方法B要么一起成功要么一起失败; | 方法B开起独立事务运行 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 | do1+do2+方法B要么一起成功要么一起失败; | methodB不开事务独立运行,相当于没有事务 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 | do1+do2+方法B要么一起成功要么一起失败; | 抛出异常 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 | 新建事务执行,挂起事务1,事务1和新事务互不影响,即do2失败不会回滚事务2 | 新建事务执行 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 | 挂起当前事务,执行methodB | 相当于没有事务执行 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 | 抛出异常 | 相当于没有事务执行 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。嵌套事务比外层事务先提交。 | 嵌套到事务1执行,和required不同的是内层事务2会先提交 | 和required一样创建新的事务执行 |
public enum Propagation {
/**
* Support a current transaction, create a new one if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>This is the default setting of a transaction annotation.
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
/**
* Support a current transaction, execute non-transactionally if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: For transaction managers with transaction synchronization,
* PROPAGATION_SUPPORTS is slightly different from no transaction at all,
* as it defines a transaction scope that synchronization will apply for.
* As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
* will be shared for the entire specified scope. Note that this depends on
* the actual synchronization configuration of the transaction manager.
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
*/
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
/**
* Support a current transaction, throw an exception if none exists.
* Analogous to EJB transaction attribute of the same name.
*/
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
/**
* Create a new transaction, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: Actual transaction suspension will not work on out-of-the-box
* on all transaction managers. This in particular applies to JtaTransactionManager,
* which requires the <code>javax.transaction.TransactionManager</code> to be
* made available it to it (which is server-specific in standard J2EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
/**
* Execute non-transactionally, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: Actual transaction suspension will not work on out-of-the-box
* on all transaction managers. This in particular applies to JtaTransactionManager,
* which requires the <code>javax.transaction.TransactionManager</code> to be
* made available it to it (which is server-specific in standard J2EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
/**
* Execute non-transactionally, throw an exception if a transaction exists.
* Analogous to EJB transaction attribute of the same name.
*/
NEVER(TransactionDefinition.PROPAGATION_NEVER),
/**
* Execute within a nested transaction if a current transaction exists,
* behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
* <p>Note: Actual creation of a nested transaction will only work on specific
* transaction managers. Out of the box, this only applies to the JDBC
* DataSourceTransactionManager when working on a JDBC 3.0 driver.
* Some JTA providers might support nested transactions as well.
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
private final int value;
Propagation(int value) {
this.value = value; }
public int value() {
return this.value; }
}
【3】spring事务的实现方式
声明式事务+编程式事务
(1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
(2)基于 TransactionProxyFactoryBean的声明式事务管理
(3)基于 @Transactional 的声明式事务管理
@Transactional注解不起作用的集中情况:
①Transactional注解标注方法修饰符为非public时,@Transactional注解将会不起作用。
②在类内部调用调用类内部@Transactional标注的方法。这种情况下也会导致事务不开启。
③事务方法内部捕捉了异常,没有抛出新的异常,导致事务操作不会进行回滚。
④ 异常类型是不是unchecked异常。如果我想check异常也想回滚怎么办,注解上面写明异常类型即可。@Transactional(rollbackFor=Exception.class)
(4)基于Aspectj AOP配置事务
【4】spring事务的实现原理
spring aop
四、参考资料
【1】 http://m.imooc.com/article/details?article_id=34150