Spring源码解读之AOP

前言


Spring中的AOP用于在程序运行期间,不修改源码对已有方法进行增强,其优势在于减少重复代码,提高开发效率,方便维护,比如说通常用在日志、事务等场景下,AOP的实现是基于动态代理技术,本章会先从AOP的概念出发, 到AOP的使用,最终涉及到相关的源码解析部分。

一、简单的aop术语

通知(advice):就是想要的方法,比如之前提到的事务、日志等等。

连接点(JoinPoint):spring中允许使用通知的地方,通俗点讲就是那些需要被增强的方法。比如下面例子中的save、update、delete方法。

切入点(Pointcut):比如一个类中有几个方法(也可以叫连接点),但是并不是每一个方法都需使用通知,那些想要被增强的方法就叫做切入点。

切面(Aspect):就是通知和切入点的结合。

引入(introduction):允许我们向现有的类添加新方法属性。就是把切面(也就是新方法属性:通知定义的)用到目标类中。

目标(target):引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。

代理(proxy):aop的实现机制。

织入(waving):把切面应用到目标对象来创建新的代理对象的过程。

二、几个入门案例与简单的aop使用方式

基于XML的AOP配置

首先定义这么一个接口

1
2
3
4
5
6
7
8
package hdu.service;
public interface IAccountService {
void saveAccount();

void updateAccount(int i);

int deleteAccount();
}

实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package hdu.service.impl;
import hdu.service.IAccountService;
public class AccountServiceImpl implements IAccountService {
@Override
public void saveAccount() {
System.out.println("执行了保存");
}

@Override
public void updateAccount(int i) {
System.out.println("执行了更新" + i);
}

@Override
public int deleteAccount() {
System.out.println("执行了删除");
return 0;
}
}

要执行的公共代码,也就是要增强的部分

1
2
3
4
5
6
7
8
9
package hdu.utils;
public class Logger {
/**
* 用于打印日志:计划在切入点方法执行之前执行
*/
public void printLog() {
System.out.println("Logger开始记录日志");
}
}

在xml文件中我们可以这么配置:

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--配置spring的IOC把service对象配置进来-->
<bean id="accountService" class="hdu.service.impl.AccountServiceImpl"></bean>

<!--spring中基于xml的AOP配置步骤
1、把通知bean也交给spring来管理
2、使用aop:config标签表明开始AOP配置
3、使用aop:aspect标签表明配置切面
id属性:是给切面提供一个唯一标识
ref属性:是指定通知类bean的id
4、在aop:aspect标签内部使用对应标签来配置通知的类型
我们现在在实例是让printLog方法在切入点方法执行之前执行,所以是前置通知
aop:before:表示配置前置通知
method属性:用于指定Logger类中哪个方法是前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强

切入点表达式的写法:
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
标准的表达式写法
public void hdu.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略
void hdu.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配值,表示返回任意值
* hdu.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包。但是有几级包就需要写几个*.
* *.*.*.AccountServiceImpl.saveAccount()
包名可以使用..当前当前包以及子包
* *..AccountServiceImpl.saveAccount()
类名和方法名都可以使用*来实现通配
* *..*.*()
参数列表:可以直接写数据类型(基本类型直接写名称[int] 引用类型写包名.类名的方式[java.lang.String])
可以使用通配符表示任意类型,但是必须有参数
可以使用..表示有无参数均可,有参数可以是任意类型
* *..*.*(int)
全通配写法
* *..*.*(..)

实际开发中切入点表达式的通常写法:
切到业务层实现类下的所有方法
* hdu.service.impl.*.*(..)

-->

<!--配置Logger类-->
<bean id="logger" class="hdu.utils.Logger"></bean>

<!--配置AOP-->
<aop:config>
<aop:aspect id="logAdvice" ref="logger">
<aop:before method="printLog" pointcut="execution(* hdu.service.impl.*.*(..))"></aop:before>
</aop:aspect>
</aop:config>
</beans>

除了前置通知,我们还能在其他地方进行增强

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
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--配置spring的IOC把service对象配置进来-->
<bean id="accountService" class="hdu.service.impl.AccountServiceImpl"></bean>

<!--配置Logger类-->
<bean id="logger" class="hdu.utils.Logger"></bean>

<!--配置AOP-->
<aop:config>
<!--配置切入点表达式 id用于指定表达式的唯一标注 expression用于指定表达式内容
此标签写在<aop:aspect>内部只能用于当前切面使用。
它还可以写在<aop:aspect>外面,此时就变成了所有切面可用
-->
<aop:pointcut id="pt1" expression="execution(* hdu.service.impl.*.*(..))"/>
<aop:aspect id="logAdvice" ref="logger">
<!--前置通知
<aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>-->
<!--后置通知
<aop:after-returning method="afterReturnPrintLog" pointcut-ref="pt1"></aop:after-returning>-->
<!--异常通知
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>-->
<!--最终通知
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>-->
</aop:aspect>
</aop:config>
</beans>

另外可以配置环绕通知

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
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--配置spring的IOC把service对象配置进来-->
<bean id="accountService" class="hdu.service.impl.AccountServiceImpl"></bean>

<!--配置Logger类-->
<bean id="logger" class="hdu.utils.Logger"></bean>

<!--配置AOP-->
<aop:config>
<!--配置切入点表达式 id用于指定表达式的唯一标注 expression用于指定表达式内容
此标签写在<aop:aspect>内部只能用于当前切面使用。
它还可以写在<aop:aspect>外面,此时就变成了所有切面可用-->
<aop:pointcut id="pt1" expression="execution(* hdu.service.impl.*.*(..))"/>
<aop:aspect id="logAdvice" ref="logger">
<!--配置环绕通知 注释在log类中-->
<aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
</aop:aspect>
</aop:config>
</beans>

此时logger类中添加如下的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 环绕通知
* 问题:当配置了环绕通知后,切入点方法没有执行,而通知方法执行了
* 分析:
* 通过对比动态代理中的环绕通知代码,发现动态代理中的环绕通知有明确的切入点方法调用,而我们的没有。
* 解决:
* Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于切入点方法。
* 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口实现类供我们使用。
* spring中的环绕通知:
* 它是spring框架为我们提供的一种可以在代码中手动控制增强代码何时执行的方式。
*/
public void aroundPrintLog(ProceedingJoinPoint pjp) {
try {
System.out.println("Logger beforePrintLog 开始记录日志");
pjp.proceed();//明确调用业务层方法(切入点方法)
System.out.println("Logger afterReturnPrintLog 开始记录日志");
} catch (Throwable e) {
System.out.println("Logger afterThrowingPrintLog 开始记录日志");
} finally {
System.out.println("Logger afterPrintLog 开始记录日志");
}
}

基于AOP的注解配置

首先xml中需要先配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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"
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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<!--配置spring创建容器时要扫描的包-->
<context:component-scan base-package="hdu"></context:component-scan>

<!--配置spring开启AOP支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

logger类中只需要改为

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package hdu.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @Author: hqf
* @description: 用于记录日志的类,里面提供了公共的代码
* @Data: Create in 23:02 2020/4/14
* @Modified By:
*/
@Component("logger")
@Aspect// 表示当前类是一个切面类
public class Logger {

@Pointcut("execution(* hdu.service.impl.*.*(..))")
public void pt1() {

}
/**
* 用于打印日志:计划在切入点方法执行之前执行
*/
@Before("pt1()")
public void beforePrintLog() {
System.out.println("Logger beforePrintLog 开始记录日志");
}

/**
* 后置通知
*/
@AfterReturning("pt1()")
public void afterReturnPrintLog() {
System.out.println("Logger afterReturnPrintLog 开始记录日志");
}

/**
* 异常通知
*/
@AfterThrowing("pt1()")
public void afterThrowingPrintLog() {
System.out.println("Logger afterThrowingPrintLog 开始记录日志");
}

/**
* 最终通知
*/
@After("pt1()")
public void afterPrintLog() {
System.out.println("Logger afterPrintLog 开始记录日志");
}

/**
* 环绕通知
* 问题:当配置了环绕通知后,切入点方法没有执行,而通知方法执行了
* 分析:
* 通过对比动态代理中的环绕通知代码,发现动态代理中的环绕通知有明确的切入点方法调用,而我们的没有。
* 解决:
* Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于切入点方法。
* 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口实现类供我们使用。
* spring中的环绕通知:
* 它是spring框架为我们提供的一种可以在代码中手动控制增强代码何时执行的方式。
*/
@Around("pt1()")
public void aroundPrintLog(ProceedingJoinPoint pjp) {
try {
System.out.println("Logger beforePrintLog 开始记录日志");
pjp.proceed();//明确调用业务层方法(切入点方法)
System.out.println("Logger afterReturnPrintLog 开始记录日志");
} catch (Throwable e) {
System.out.println("Logger afterThrowingPrintLog 开始记录日志");
} finally {
System.out.println("Logger afterPrintLog 开始记录日志");
}
}
}

三、源码解析

回忆一下IOC部分的代码,在位置:AbstractAutowireCapableBeanFactory.java

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
// 之前的代码忽略
......

try {
// 直接来到这个方法
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Finished creating instance of bean '" + beanName + "'");
}

return beanInstance;
} catch (ImplicitlyAppearedSingletonException | BeanCreationException var7) {
throw var7;
} catch (Throwable var8) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", var8);
}
}

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
}

if (instanceWrapper == null) {
// 一般的bean的实例化方法,很重要。
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}

Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}

... // 忽略一部分代码

// 依赖循环问题
boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
}

this.addSingletonFactory(beanName, () -> {
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}

Object exposedObject = bean;

// 这边是bean的初始化,也就是值的装载,因为这个方法里的createBeanInstance()这个函数就已经实例化了bean。
try {
this.populateBean(beanName, mbd, instanceWrapper);
// 关键方法,初始化。这里面还涉及了BeanPostProcessor的回调,就是之前说的。
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
throw (BeanCreationException)var18;
}

throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
}

... // 忽略一部分代码。
}

initializeBean方法中涉及到了BeanPostProcessor的回调,也就是

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
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(() -> {
this.invokeAwareMethods(beanName, bean);
return null;
}, this.getAccessControlContext());
} else {
this.invokeAwareMethods(beanName, bean);
}

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 前置方法
wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
}

try {
// 初始化方法
this.invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable var6) {
throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
}

if (mbd == null || !mbd.isSynthetic()) {
// 后置方法
wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

return wrappedBean;
}

AOP的关键就是在后置方法中进行实现的,下面详细来看。

由继承关系,BeanPostProcessor(接口)中的postProcessAfterInitialization方法被AbstractAutoProxyCreator中进行重写,如下。

1
2
3
4
5
6
7
8
9
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

进入类AbstractAutoProxyCreator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
} else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
} else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
// 这边得到所有的可用于拦截当前 bean 的 advisor、advice、interceptor。见下图
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理
Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}

AOP的通知与切入点

这边可以很清除的看到通知printLog和我之前在XML文件中定义的切入点表达式。

继续来看创建代理的过程,仍然在类AbstractAutoProxyCreator

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
protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);
}
// 创建一个ProxyFactory
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (this.shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
} else {
this.evaluateProxyInterfaces(beanClass, proxyFactory);
}
}

Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
this.customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (this.advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 关键方法
return proxyFactory.getProxy(this.getProxyClassLoader());
}

我们知道,如果被代理的目标类实现了一个或多个自定义的接口,那么就会使用 JDK 动态代理,如果没有实现任何接口,会使用 CGLIB 实现代理,如果设置了 proxy-target-class=”true”,那么都会使用 CGLIB。JDK 动态代理基于接口,所以只有接口中的方法会被增强,而 CGLIB 基于类继承,需要注意就是如果方法使用了 final 修饰,或者是 private 方法,是不能被增强的。

在此之前就会用以上原则判断是用jdk代理还是cglib代理,代码就省略掉了。

1
2
3
4
5
6
7
8
9
10
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}

Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
this.findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 第二个参数代表需要实现哪些接口,第三个参数最重要,是 InvocationHandler 实例,因为 JdkDynamicAopProxy 本身实现了 InvocationHandler 接口。
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

来看jdk动态代理中重写的invoke方法。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;

Class var8;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
Boolean var18 = this.equals(args[0]);
return var18;
}

if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
Integer var17 = this.hashCode();
return var17;
}

if (method.getDeclaringClass() != DecoratingProxy.class) {
Object retVal;
if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
return retVal;
}

if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}

target = targetSource.getTarget();
Class<?> targetClass = target != null ? target.getClass() : null;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}

Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
}

Object var12 = retVal;
return var12;
}

var8 = AopProxyUtils.ultimateTargetClass(this.advised);
} finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}

if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}

}

return var8;
}

另外,代理的方法还有CGLib,这个是基于字节码ASM框架来实现的,这边暂时不讨论了。