이 기사는 Spring 트랜잭션에 대한 심층 분석을 제공합니다(예시 포함). 필요한 친구가 참고할 수 있기를 바랍니다.
모든 사람이 Spring 트랜잭션 관리를 많이 사용한다고 생각하지만 @Transactional
주석이나 XML
에서 트랜잭션 관련 항목을 구성하는 것만으로 제한될 수 있습니다. 어쨌든 우리가 매일 사용하기에는 충분할 것 같습니다. 그러나 프로그래머로서 인터뷰를 위한 것이든 작성한 코드를 더 잘 제어하기 위한 것이든 Spring 트랜잭션의 세부 사항에 대해 더 많이 배워야 합니다. @Transactional
注解或者在XML
中配置事务相关的东西。不管怎么说,日常可能足够我们去用了。但作为程序员,无论是为了面试还是说更好把控自己写的代码,还是应该得多多了解一下Spring事务的一些细节。
这里我抛出几个问题,看大家能不能瞬间答得上:
如果嵌套调用含有事务的方法,在Spring事务管理中,这属于哪个知识点?
我们使用的框架可能是Hibernate/JPA
或者是Mybatis
,都知道的底层是需要一个session/connection
对象来帮我们执行操作的。要保证事务的完整性,我们需要多组数据库操作要使用同一个session/connection
对象,而我们又知道Spring IOC所管理的对象默认都是单例的,这为啥我们在使用的时候不会引发线程安全问题呢?内部Spring到底干了什么?
人家所说的BPP又是啥东西?
Spring事务管理重要接口有哪几个?
一、阅读本文需要的基础知识
阅读这篇文章的同学我默认大家都对Spring事务相关知识有一定的了解了。(ps:如果不了解点解具体的文章去阅读再回到这里来哦)
我们都知道,Spring事务是Spring AOP的最佳实践之一,所以说AOP入门基础知识(简单配置,使用)是需要先知道的。如果想更加全面了解AOP可以看这篇文章:AOP重要知识点(术语介绍、全面使用)。说到AOP就不能不说AOP底层原理:动态代理设计模式。到这里,对AOP已经有一个基础的认识了。于是我们就可以使用XML/注解方式来配置Spring事务管理。
在IOC学习中,可以知道的是Spring中Bean的生命周期(引出BPP对象)并且IOC所管理的对象默认都是单例的:单例设计模式,单例对象如果有"状态"(有成员变量),那么多线程访问这个单例对象,可能就造成线程不安全。那么何为线程安全?,解决线程安全有很多方式,但其中有一种:让每一个线程都拥有自己的一个变量:ThreadLocal
如果对我以上说的知识点不太了解的话,建议点击蓝字进去学习一番。
二、两个不靠谱直觉的例子
之前朋友问了我一个例子:
在Service层抛出Exception,在Controller层捕获,那如果在Service中有异常,那会事务回滚吗?
// Service方法 @Transactional public Employee addEmployee() throws Exception { Employee employee = new Employee("3y", 23); employeeRepository.save(employee); // 假设这里出了Exception int i = 1 / 0; return employee; } // Controller调用 @RequestMapping("/add") public Employee addEmployee() { Employee employee = null; try { employee = employeeService.addEmployee(); } catch (Exception e) { e.printStackTrace(); } return employee; }
我第一反应:不会回滚吧。
我当时是这样想的:因为Service层已经抛出了异常,由Controller捕获。那是否回滚应该由Controller的catch代码块中逻辑来决定,如果catch代码块没有回滚,那应该是不会回滚。
但朋友经过测试说,可以回滚阿。(pappapa打脸)
看了一下文档,原来文档有说明:
By default checked exceptions do not result in the transactional interceptor marking the transaction for rollback and instances of RuntimeException and its subclasses do
结论:如果是编译时异常不会自动回滚,如果是运行时异常,那会自动回滚!
第二个例子来源于知乎@柳树文章,文末会给出相应的URL
我们都知道,带有@Transactional
注解所包围的方法就能被Spring事务管理起来,那如果我在当前类下使用一个没有事务的方法去调用一个有事务的方法,那我们这次调用会怎么样?是否会有事务呢?
用代码来描述一下:
// 没有事务的方法去调用有事务的方法 public Employee addEmployee2Controller() throws Exception { return this.addEmployee(); } @Transactional public Employee addEmployee() throws Exception { employeeRepository.deleteAll(); Employee employee = new Employee("3y", 23); // 模拟异常 int i = 1 / 0; return employee; }
我第一直觉是:这跟Spring事务的传播机制有关吧。
其实这跟Spring事务的传播机制没有关系,下面我讲述一下:
Spring事务管理用的是AOP,AOP底层用的是动态代理。所以如果我们在类或者方法上标注注解@Transactional
여기서 모든 사람이 즉시 답변할 수 있는지 확인하기 위해 몇 가지 질문을 던집니다.
Hibernate/JPA
또는 Mybatis
일 수 있습니다. 우리 모두는 맨 아래 계층에 세션/연결
이 필요하다는 것을 알고 있습니다. > 개체는 작업을 수행하는 데 도움이 됩니다. 트랜잭션의 무결성을 보장하려면 여러 데이터베이스 작업 세트에 동일한 🎜session/connection
개체를 사용해야 하며 Spring IOC에서 관리하는 개체는 기본적으로 모두 싱글톤이라는 것을 알고 있습니다. 이것을 사용할 때 스레드 안전 문제가 발생하지 않습니까? Spring은 내부적으로 정확히 무엇을 하고 있나요? 🎜🎜🎜1. 이 글을 읽는 데 필요한 기본 지식🎜🎜🎜이 글을 읽는 우리 반 친구들은🎜기본적으로🎜모두가 Spring 트랜잭션 관련 지식을 어느 정도 이해하고 있습니다. (ps: 구체적인 기사를 모르신다면 읽어보시고 여기로 오세요)🎜🎜우리 모두 Spring 트랜잭션이 Spring AOP의 모범 사례 중 하나라는 것을 알고 있으므로 AOP에 대한 기본 지식(간단한 구성)에 대해 이야기해 보겠습니다. , 사용) 먼저 알아야 합니다. AOP를 보다 포괄적으로 이해하려면 AOP의 중요한 지식 포인트(용어 소개, 포괄적인 사용) 문서를 읽어보세요. AOP에 대해 이야기할 때 우리는 AOP의 기본 원칙인 동적 프록시 디자인 패턴에 대해 이야기해야 합니다. 이 시점에서 당신은 이미 AOP에 대한 기본적인 이해를 갖고 있습니다. 따라서 우리는 XML/주석을 사용하여 Spring 트랜잭션 관리를 구성할 수 있습니다. 🎜🎜IOC 학습에서 알 수 있는 것은 Spring의 Beans 수명 주기(BPP 객체로 이어짐)이며 IOC에서 관리하는 객체는 기본적으로 싱글톤입니다. 싱글톤 디자인 패턴, 싱글톤 객체에 "🎜status🎜"( 멤버 변수를 사용하면 이 싱글톤 개체에 액세스하는 스레드가 너무 많아 스레드 보안이 저하될 수 있습니다. 그렇다면 스레드 안전성이란 무엇입니까? , 스레드 안전성을 해결하는 방법은 여러 가지가 있지만 그 중 하나는 각 스레드에 자체 변수를 갖도록 하는 것입니다: ThreadLocal🎜
🎜 위에서 언급한 지식 포인트에 대해 잘 모르는 경우 클릭하는 것이 좋습니다. 파란색 단어에 비트 팬을 배울 수 있습니다. 🎜
🎜2. 신뢰할 수 없는 직관의 두 가지 예🎜🎜
@Service public class TestService { @Autowired private EmployeeRepository employeeRepository; @Transactional public Employee addEmployee() throws Exception { employeeRepository.deleteAll(); Employee employee = new Employee("3y", 23); // 模拟异常 int i = 1 / 0; return employee; } } @Service public class EmployeeService { @Autowired private TestService testService; // 没有事务的方法去调用别的类有事务的方法 public Employee addEmployee2Controller() throws Exception { return testService.addEmployee(); } }
🎜기본적으로 확인된 예외는 롤백 트랜잭션을 표시하는 트랜잭션 인터셉터를 발생시키지 않으며 RuntimeException 및 해당 하위 클래스의 인스턴스는 수행합니다. 🎜 blockquote>🎜결론: 컴파일 타임 예외라면 자동으로 롤백되지 않습니다. 🎜런타임 예외라면 자동으로 롤백됩니다🎜! 🎜2.2 두 번째 예
🎜두 번째 예는 Zhihu@LiuShu 기사에서 나온 것이며, 해당 URL은 기사 끝에 제공됩니다🎜🎜우리 모두는 < code >@Transactional 주석으로 둘러싸인 메소드는 Spring 트랜잭션에 의해 관리될 수 있습니다. 그렇다면 현재 클래스에서 트랜잭션이 없는 메소드를 사용하여 트랜잭션이 있는 메소드를 호출하면 이번 호출은 어떻게 될까요? ? 사업이 있을까요? 🎜🎜코드로 설명하기: 🎜rrreee🎜내 첫 번째 직감은: 이것은 Spring 트랜잭션의 전파 메커니즘과 관련이 있습니다. 🎜🎜사실 이것은 Spring 트랜잭션 전파 메커니즘🎜🎜과는 아무런 관련이 없습니다. 아래에 설명하겠습니다.🎜
- 🎜Spring 트랜잭션 관리는 AOP를 사용하며, 그 하단에는 AOP 계층은 동적 프록시를 사용합니다. 따라서 클래스나 메서드에
@Transactional
주석을 표시하면 🎜프록시 개체🎜가 생성됩니다. 🎜🎜🎜🎜 다음으로 사진을 사용하여 설명하겠습니다. 🎜显然地,我们拿到的是代理(Proxy)对象,调用
addEmployee2Controller()
方法,而addEmployee2Controller()
方法的逻辑是target.addEmployee()
,调用回原始对象(target)的addEmployee()
。所以这次的调用压根就没有事务存在,更谈不上说Spring事务传播机制了。Spring 트랜잭션에 대한 심층 분석(예제 포함):
测试结果:压根就Spring 트랜잭션에 대한 심층 분석(예제 포함)
2.2.1再延伸一下
从上面的测试我们可以发现:如果是在本类中没有事务的方法来调用标注注解
@Transactional
方法,最后的结论是没有事务的。那如果我将这个标注注解的方法移到别的Service对象上,有没有事务?@Service public class TestService { @Autowired private EmployeeRepository employeeRepository; @Transactional public Employee addEmployee() throws Exception { employeeRepository.deleteAll(); Employee employee = new Employee("3y", 23); // 模拟异常 int i = 1 / 0; return employee; } } @Service public class EmployeeService { @Autowired private TestService testService; // 没有事务的方法去调用别的类有事务的方法 public Employee addEmployee2Controller() throws Exception { return testService.addEmployee(); } }로그인 후 복사로그인 후 복사测试结果:
因为我们用的是代理对象(Proxy)去调用
addEmployee()
方法,那就当然有事务了。看完这两个例子,有没有觉得3y的直觉是真的水!
三、Spring事务传播机制
如果嵌套调用含有事务的方法,在Spring事务管理中,这属于哪个知识点?
在当前含有事务方法内部调用其他的方法(无论该方法是否含有事务),这就属于Spring事务传播机制的知识点范畴了。
Spring事务基于Spring AOP,Spring AOP底层用的动态代理,动态代理有两种方式:
基于接口代理(JDK代理)
基于接口代理,凡是类的方法非public修饰,或者用了static关键字修饰,那这些方法都不能被Spring AOP增强
基于CGLib代理(子类代理)
基于子类代理,凡是类的方法使用了private、static、final修饰,那这些方法都不能被Spring AOP增强
至于为啥以上的情况不能增强,用你们的脑瓜子想一下就知道了。
值得说明的是:那些不能被Spring AOP增强的方法并不是不能在事务环境下工作了。只要它们被外层的事务方法调用了,由于Spring事务管理的传播级别,内部方法也可以工作在外部方法所启动的事务上下文中。
至于Spring事务传播机制的几个级别,我在这里就不贴出来了。这里只是再次解释“啥情况才是属于Spring事务传播机制的范畴”。
四、多线程问题
我们使用的框架可能是
Hibernate/JPA
或者是Mybatis
,都知道的底层是需要一个session/connection
对象来帮我们执行操作的。要保证事务的完整性,我们需要多组数据库操作要使用同一个session/connection
对象,而我们又知道Spring IOC所管理的对象默认都是单例的,这为啥我们在使用的时候不会引发线程安全问题呢?内部Spring到底干了什么?回想一下当年我们学Mybaits的时候,是怎么编写Session工具类?
没错,用的就是ThreadLocal,同样地,Spring也是用的ThreadLocal。
以下内容来源《精通 Spring4.x》
我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态的“状态性对象”采用ThreadLocal封装,让它们也成为线程安全的“状态性对象”,因此,有状态的Bean就能够以singleton的方式在多线程中工作。
我们可以试着点一下进去TransactionSynchronizationManager中看一下:
5. BPP란 무엇인가요?
BBP의 전체 이름은 다음과 같습니다. BeanPostProcessor, 일반적으로 객체 후처리 프로세서
간단히 말하면, 우리 객체는 BeanPostProcessor를 통해 "처리"될 수 있습니다.
봄 빈 관리(또는 빈 수명 주기)도 자주 테스트되는 지식 포인트입니다. 또한 가을 모집 단계를 재정리하는 것이 더 중요하므로 여기에 게시하겠습니다.
를 호출합니다. BeanPostProcessor의 after 메소드로 Bean이 구성되면
- ResouceLoader는 구성 정보를 로드합니다.
- BeanDefintionReader는 구성 정보를 구문 분석하여 BeanDefintion을 하나씩 생성합니다.
- BeanDefintion은 BeanDefintionRegistry
- BeanFactoryPostProcessor에서 쌍을 이루어 구성 정보를 처리합니다. , 일반적으로 PropertyPlaceholderConfigurer를 통해 구현됨)
- Instantiate Bean
- Bean이
구성/구현
InstantiationAwareBean인 경우 해당 메서드를 호출합니다
配置/实现
了InstantiationAwareBean,则调用对应的方法使用BeanWarpper来完成对象之间的属性配置(依赖)
如果该Bean
配置/实现了
Aware接口,则调用对应的方法如果该Bean配置了BeanPostProcessor的before方法,则调用
- BeanWarpper를 사용하여 개체 간 속성을 완료합니다. 구성(종속성)
如果该Bean配置了
init-method
- Bean이 Aware 인터페이스를
구성/구현하면 해당 메서드가 호출됩니다. <li><p></p></li>Bean이 BeanPostProcessor의 이전 메서드로 구성되면 <li><p></p></li>이 호출됩니다. Bean이 <code>init-method
로 구성되거나 InstantiationBean을 구현하면 해당 메소드을 호출하여 객체를 HashMap에 넣습니다.
마지막으로 destroy 또는 DisposableBean 메소드가 구성되면 파괴 작업이 수행됩니다.
BPP에 대한 그림도 있습니다:
5.1 BPP에 대해 구체적으로 이야기하는 이유는 무엇입니까?
Spring AOP 프로그래밍의 최하위 계층은 동적 프록시 기술을 사용하며 호출 시
프록시 객체를 사용해야 합니다. 그렇다면 Spring은 어떻게 이를 수행하는가?
- BPP만 작성하면 되며 postProcessBeforeInitialization 또는 postProcessAfterInitialization 메서드에서 객체를 판단하여 측면 로직으로 엮어야 하는지 확인하면 이 객체를 기반으로 프록시 객체를 생성한 다음 반환합니다. 이 프록시 개체가 있으면 궁극적으로 컨테이너에 주입되는 것은 당연히 프록시 개체입니다.
- "할 수 있게 해주는 BeanPostProcessor를 제공합니다!
Spring은 필요한 객체를 "
process6. Spring 트랜잭션의 몇 가지 중요한 인터페이스 이해
Spring 트랜잭션은 두 가지 유형으로 나눌 수 있습니다:
- 선언적 트랜잭션(구성을 통해) 트랜잭션을 구현하려면
프로그래밍 방식 트랜잭션(코드를 통해 트랜잭션 구현)
프로그래밍 방식 트랜잭션은 Spring에서 구현하기가 비교적 간단한 반면, 선언적 트랜잭션은 많은 것을 캡슐화하므로(일반적으로 간단하게 사용하지만 내부는 매우 복잡함) 선언적 트랜잭션은 구현하기가 훨씬 더 어렵습니다. 프로그래밍 트랜잭션에는 다음과 같은 중요한 인터페이스가 있습니다.
TransactionDefinition: Spring 호환 트랜잭션 속성 정의(예: 트랜잭션 격리 수준, 트랜잭션 전파, 트랜잭션 시간 초과, 읽기 전용 상태)
TransactionStatus: 특정 거래 실행 상태
를 나타냅니다(이 인터페이스를 통해 거래 실행 상태 정보를 얻는 것도간접적으로
거래 롤백 가능).🎜 🎜🎜TransactionProxyFactoryBean: 프록시 객체 생성🎜🎜 🎜🎜TransactionInterceptor: 객체 차단 구현🎜🎜🎜🎜TransactionAttrubute: 트랜잭션 구성 데이터🎜🎜🎜
- PlatformTransactionManager: 거래 관리자 인터페이스(동작 집합을 정의하고 특정 구현은 다음과 같습니다) 다양한 지속성 프레임워크로 완성 ---
analogy- JDBC)
- 선언적 트랜잭션에는 TransactionStatus 및 PlatformTransactionManager 인터페이스 외에도 몇 가지 중요한 인터페이스가 있습니다:
위 내용은 Spring 트랜잭션에 대한 심층 분석(예제 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!