Skip to content

Commit 41d3f0e

Browse files
committed
Spring AOP: aop切面拦截的坑
1 parent 61c574e commit 41d3f0e

File tree

8 files changed

+142
-17
lines changed

8 files changed

+142
-17
lines changed

note/images/cglib_invocation.png

2.99 KB
Loading

note/spring-aop.md

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,4 +1145,60 @@ public boolean isAspect(Class<?> clazz) {
11451145

11461146
## 总结
11471147

1148-
Spring对于AspectJ风格AOP的支持停留在外表(注解)上面,内部的实现仍然是自己的东西。
1148+
Spring对于AspectJ风格AOP的支持停留在外表(注解)上面,内部的实现仍然是自己的东西。
1149+
1150+
# 拾遗
1151+
1152+
## AOP切面的坑
1153+
1154+
1. 定义在private方法上的切面不会被执行,这个很容易理解,毕竟子类不能覆盖父类的私有方法。
1155+
2. 同一个代理子类内部的方法相互调用不会再次执行切面。
1156+
1157+
这里以Cglib为例对第二点进行说明,cglib的相关核心组件可以参考前面CallbackFilter & Callback部分。对于配置了一个切面的典型场景,Spring内部的执行流程可总结如下图:
1158+
1159+
![Cglib调用流程](images/cglib_invocation.png)
1160+
1161+
核心便是对目标方法的调用上,这里由CglibMethodInvocation的invokeJoinpoint实现:
1162+
1163+
```java
1164+
@Override
1165+
protected Object invokeJoinpoint() throws Throwable {
1166+
if (this.publicMethod) {
1167+
return this.methodProxy.invoke(this.target, this.arguments);
1168+
} else {
1169+
return super.invokeJoinpoint();
1170+
}
1171+
}
1172+
```
1173+
1174+
如果是非public方法,那么Spring将使用反射的方法对其进行调用,因为反射将其可访问性设为true。MethodProxy是Cglib对方法代理的抽象,这里的关键是**方法调用的对象(目标)是我们的原生类对象,而不是Cglib代理子类的对象,这就从根本上决定了对同类方法的调用不会再次经过切面**
1175+
1176+
### 总结
1177+
1178+
前面aop:aspectj-autoproxy-属性-expose-proxy一节提到了,Spring允许我们将代理子类暴露出来,可以进行如下配置:
1179+
1180+
```xml
1181+
<aop:config expose-proxy="true">
1182+
<aop:advisor advice-ref="simpleMethodInterceptor" pointcut="execution(* aop.SimpleAopBean.*(..))" />
1183+
</aop:config>
1184+
```
1185+
1186+
当我们需要在一个被代理方法中调用同类的方法时(此方法也需要经过切面),可以这样调用:
1187+
1188+
```java
1189+
public void testB() {
1190+
System.out.println("testB执行");
1191+
((SimpleAopBean) AopContext.currentProxy()).testC();
1192+
}
1193+
```
1194+
1195+
这里其实是一个ThreadLocal,当Cglib代理子类创建调用链之间便会将代理类设置到其中,DynamicAdvisedInterceptor.intercept相关源码:
1196+
1197+
```java
1198+
if (this.advised.exposeProxy) {
1199+
// Make invocation available if necessary.
1200+
oldProxy = AopContext.setCurrentProxy(proxy);
1201+
setProxyContext = true;
1202+
}
1203+
```
1204+

pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@
8787
<artifactId>mysql-connector-java</artifactId>
8888
<version>5.1.20</version>
8989
</dependency>
90+
91+
<dependency>
92+
<groupId>cglib</groupId>
93+
<artifactId>cglib</artifactId>
94+
<version>3.2.5</version>
95+
</dependency>
96+
9097
</dependencies>
9198

9299
<build>
@@ -95,6 +102,7 @@
95102
<groupId>org.apache.maven.plugins</groupId>
96103
<artifactId>maven-compiler-plugin</artifactId>
97104
<configuration>
105+
<debug>false</debug>
98106
<source>1.8</source>
99107
<target>1.8</target>
100108
</configuration>

src/main/java/aop/Bootstrap.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package aop;
2+
3+
import org.springframework.context.support.ClassPathXmlApplicationContext;
4+
5+
/**
6+
* AOP测试启动类.
7+
*
8+
* @author skywalker
9+
*/
10+
public class Bootstrap {
11+
12+
public static void main(String[] args) {
13+
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
14+
SimpleAopBean bean = context.getBean(SimpleAopBean.class);
15+
bean.testB();
16+
System.out.println(bean.getClass().getSimpleName());
17+
}
18+
19+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package aop;
2+
3+
import org.springframework.aop.framework.AopContext;
4+
5+
/**
6+
* @author skywalker
7+
*/
8+
public class SimpleAopBean {
9+
10+
public void boo() {
11+
System.out.println("testA执行");
12+
testB();
13+
}
14+
15+
public void testB() {
16+
System.out.println("testB执行");
17+
((SimpleAopBean) AopContext.currentProxy()).testC();
18+
}
19+
20+
public void testC() {
21+
System.out.println("testC执行");
22+
}
23+
24+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package aop;
2+
3+
/**
4+
* @author skywalker
5+
*/
6+
public class SimpleChildAopBean extends SimpleAopBean {
7+
8+
@Override
9+
public void testC() {
10+
System.out.println("child testC");
11+
}
12+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package aop;
2+
3+
import org.aopalliance.intercept.MethodInterceptor;
4+
import org.aopalliance.intercept.MethodInvocation;
5+
6+
/**
7+
* @author skywalker
8+
*/
9+
public class SimpleMethodInterceptor implements MethodInterceptor {
10+
11+
@Override
12+
public Object invoke(MethodInvocation invocation) throws Throwable {
13+
System.out.println("SimpleMethodInterceptor被调用: " + invocation.getMethod().getName());
14+
return invocation.proceed();
15+
}
16+
17+
}

src/main/resources/config.xml

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,12 @@
1414
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
1515
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">
1616

17-
<!-- 数据源以Sping自带为例,每次请求均返回一个新的连接 -->
18-
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
19-
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
20-
<property name="url" value="jdbc:mysql://localhost:3306/test" />
21-
<property name="username" value="tiger" />
22-
<property name="password" value="tiger" />
23-
</bean>
17+
<bean id="simpleMethodInterceptor" class="aop.SimpleMethodInterceptor" />
2418

25-
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
26-
<property name="dataSource" ref="dataSource"/>
27-
</bean>
19+
<bean class="aop.SimpleAopBean" />
2820

29-
<bean id="nestedBean" class="base.transaction.NestedBean" />
30-
<bean class="base.transaction.TransactionBean">
31-
<property name="nestedBean" ref="nestedBean" />
32-
</bean>
33-
34-
<tx:annotation-driven transaction-manager="transactionManager" mode="proxy"/>
21+
<aop:config expose-proxy="true">
22+
<aop:advisor advice-ref="simpleMethodInterceptor" pointcut="execution(* aop.SimpleAopBean.*(..))" />
23+
</aop:config>
3524

3625
</beans>

0 commit comments

Comments
 (0)