Spring AOP 失效的原因

分析AOP在一定情况下会失效的原因。

背景

Spring提供了事务注解Transactional,在一定的使用场景下会发生事务不生效的情况。

注:下列场景中,updateInDB为数据库更新操作。

场景1(事务生效)

一般的使用方式如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Component
class Service {
@Transactional
public Long updateA(A a) {
// Update A
updateInDB(a)
b = ...
// Then update B
updateB(b)
}

public Long updateB(B b) {
// Update B
updateInDB(b)
}
}

class ServiceTest {

@Autowired
private Service service;

@Test
public void test() {
A a = new A(...);
service.updateA(a)
}
}

由于updateA包含了updateB的操作,因此updateBupdateA会在一个事务中。

此时,updateAupdateB会同时生效或失效。

场景2(事务失效)

考虑内部调用的情况如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Component
class Service {

public Long update(A a) {
return updateA(A)
}

@Transactional
public Long updateA(A a) {
// Update A
updateInDB(a)
b = ...
// Then update B
updateB(b)
}

public Long updateB(B b) {
// Update B
updateInDB(b)
}
}

class ServiceTest {

@Autowired
private Service service;

@Test
public void test() {
A a = new A(...);
service.update(a)
}
}

与场景1不同,此时通过无事务标识的update调用有事务标识的updateA

此时,updateAupdateB的事务会失效。

场景3(事务失效)

考虑继承调用的场景如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

class BaseService {

@Transactional
public Long updateA(A a) {
// Update A
updateInDB(a)
b = ...
// Then update B
updateB(b)
}

public Long updateB(B b) {
// Update B
updateInDB(b)
}
}

@Component
class Service extends BaseService {

@Override
public Long updateA(A a) {
// Invoke super.method
super.updateA(a)
....
}
}

class ServiceTest {

@Autowired
private Service service;

@Test
public void test() {
A a = new A(...);
service.updateA(a)
}
}

BaseService.updateA标记了Transactional注解。

Service继承自BaseService,覆盖了updateA,但没有事务标识。

此时,service.updateA调用super.updateA是无法保证事务的有效性。

原因

首先,Spring的核心IOC控制反转AOP面向切面编程

IOC控制反转也被称为DI依赖注入,负责管理Spring项目中所有对象的生命周期依赖关系

AOP面向切面编程是通过预编译动态代理来改变原有模块功能的技术方式。

IOC是实现AOP的基础。

AOP无论哪种实现方式都会为模块生成新的代理模块,而模块之间的依赖注入是依赖IOC的。

也就是说,如果希望AOP生效,必须通过IOC管理的对象实例才可以生效。


内部调用: 通过thissuper的调用。

通过thissuper产生的函数调用与Spring IOC无关,因此AOP无效。

场景2场景3中,虽然updateAsuper.updateA都带有Spring的Transactional注解,

但是通过thissuper来调用的,因此,Transactional注解无效。

只有通过代理调用,AOP才会生效,而代理Spring IOC的一部分。