Spring-aop

AOP是指面向切面的程序设计,该设计理念可以让程序同时关注相似类型的所有业务逻辑(在AOP中也称为切面),而不必每个都要处理,从而让程序可以对切面进行统一处理,降低了代码冗余,也降低了耦合度。


1 Spring中的AOP

在Spring中声明AOP只需在类型加上@Aspect注解(AOP的jar包要先引进来),再使用@Component注解,将AOP放入到容器中被Spring管理。

接着可以在声明的AOP中,定义Pointcut和Advice。

定义Pointcut时可以使用切点表达式来定义,切点的含义就是指AOP要在哪些地方生效。

Advice在Spring中有五种,分别为Before、Around、After、AfterReturning、AfterThrowing。

以下是声明AOP的简单例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Aspect
@Component
public class ExampleAop {

@Pointcut("@annotation(com.chuyu.live.common.annotation.Login)")
private void checkLogin() {
}


@Before("checkLogin()")
public void doCheckUser(JoinPoint joinPoint) {

}
}

2 spring-AOP的实现分析

在spring中,aop的实现其实就是对原对象A生成了一个代理对象A-Procxy。并在代理对象中,根据定义好的切点将增强织入到相应位置,也就是说,spring的对象容器在进行spring-bean的初始化时,创建好的对象就是代理对象,我们实际使用过程中,用到的也是代理对象。

所以aop的实现是和spring-bean的创建过程紧密关联的,对于对象创建过程可以参考上一篇文章。

在对象创建过程中,spring会在相应的扩展点调用对象的回调函数,也就是BeanPostProcess。aop也是在这一步,去创建了代理对象,并替代了原始对象进行返回,所以aop在spring中,本质上就是一个BeanPostProcess,其名为AbstractAutoProxyCreator,该抽象类实现了InstantiationAwareBeanPostProcessor接口,并使用BeanPostProcess的postProcessAfterInitialization方法进行aop的具体实现(正常情况,非正常情况使用postProcessBeforeInstantiation)

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
// earlyProxyReferences中存放的都是还未初始化好的bean
// 根据beanName从earlyProxyReferences获取bean
// 如果不等于,表名bean不在其中,没有进行过提前暴露,可以进行创建代理
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

先进行判断,bean有无真正初始化完成,如果初始化结束了,再调用wrapIfNecessary方法进行进一步处理

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
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}

// 如果是系统类则不需要代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}

this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

wrapIfNecessary方法一开始是一些判断逻辑,如果该类不需要代理(之前已经判断过了,放advisedBeans缓存中了)或者是系统类,则直接返回。

如果判断通过了,则通过getAdvicesAndAdvisorsForBean方法获取该bean的Class类型所对应的Advisor对象列表(Advisor对象是对切面的封装,包含了该Class的某个Adivice的所有信息),如果不为空,表名要进行代理,使用createProxy创建代理对象,并将代理对象返回。

通过本方法可知,spring的AOP基本上可以分为两部分,一部分为将切面信息封装为Advisor,另一部分则是代理对象的创建。

2.1 组装AOP类的Advisor对象

我们先看getAdvicesAndAdvisorsForBean方法,getAdvicesAndAdvisorsForBean方法负责了将切面信息封装为Advisor这一步,getAdvicesAndAdvisorsForBean调用了findEligibleAdvisors方法进行寻找Advisors。

1
2
3
4
5
6
7
8
9
10
11
12
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 获取所有的Advisor列表
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 在所有Advisor列表寻找符合class类型的
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 交给子类进行扩展,可以对Advisor列表进行二次处理
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}

再具体看下是如何进行查找所有的Advisor的,至于之后的筛选就没必要看了,无非就是拿当前类的名称和AOP类的切点表达式进行匹配,看看满不满足条件。

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
/**
* Find all eligible Advisor beans in the current bean factory,
* ignoring FactoryBeans and excluding beans that are currently in creation.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
String[] advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the auto-proxy creator apply to them!
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}

List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
// Ignore: indicates a reference back to the bean we're trying to advise.
// We want to find advisors other than the currently created bean itself.
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}

可以看到方法注释中,首先点名了会忽略FactoryBeans和正在创建中的bean。

再然后,居然直接从beanFactory获取Advisor类型的类就完事了?? WTF?我第一次看这段代码直接懵了,这些Advisor怎么到容器里面的?基于这个问题,我们可以讨论一下spring的框架设计了,我们目前所看到的方法只是AbstractAdvisorAutoProxyCreator抽象类的实现,并且findAdvisorBeans方法是public的(如果是模板方法设计的话,一般模板方法都是private的),所以就可以猜测,有子类覆写了该方法完成了最终的业务实现,在IDEA下找抽象类的实现类,最后找到了一个名字有点靠近的AnnotationAwareAspectJAutoProxyCreator类,名称上带注解两个字,再看其findAdvisorBeans方法,果然和父类的不同了

1
2
3
4
5
6
7
8
9
10
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}

看到该方法的实现就很明了了,我们平时用@AspectJ注解定义的aop都是在该类实现中被加载的。

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
75
76
77
78
79
/**
* 扫描所有aop类并组装成Advisor
* Look for AspectJ-annotated aspect beans in the current bean factory,
* and return to a list of Spring AOP Advisors representing them.
* <p>Creates a Spring Advisor for each AspectJ advice method.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;

if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}

if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}

buildAspectJAdvisors方法注释中也写明了该方法的主要作用。

我们看一下具体的组装逻辑,首先从BeanFactory的工具类方法获取所有的Object对象,过滤掉不带有AspectJ注解的对象,然后将定义的pointCut和Advice封装为Advisor对象(封装过程就不详细介绍了,跟AspectJ有很大的关联,比较复杂,可以简单理解为Advisor对象中包含了一个Advice对象,而Advice对象则存放了我们在AOP中定义的Before方法之类的),并将解析过的bean所对应的Advisor放到缓存中,那么到此为止,Advisor的组装就算结束了,接下来就是代理对象的创建逻辑了

2.2 创建代理对象

回到本节一开始的部分,在一开始讲了,在AbstractAutoProxyCreator的postProcessAfterInitialization方法中,首先判断该bean是否有Advisor对象列表,如果有则代表该bean需要被代理,要去创建代理对象。也就是以下这一行代码,是AOP的重要部分。

1
2
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

该行调用了createProxy方法,并将代理对象的目标类(原对象)、原对象的类型、原对象的Advisor列表传递了过去,用于创建代理对象。

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
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 = new ProxyFactory();
proxyFactory.copyFrom(this);

if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}

Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}

return proxyFactory.getProxy(getProxyClassLoader());
}

在createProxy方法中,主要是为了创建出ProxyFactory对象。ProxyFactory是用于创建代理类的对象工厂,其中有段逻辑buildAdvisors方法对原始的Advisor列表进行了更改,加入了公共的Advisor,并尽可能的将每个Advisor转变为DefaultPointcutAdvisor。至于其他语句则只是一些简单的赋值操作,重点逻辑在ProxyFactory的getProxy方法中。

1
2
3
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

getProxy方法首先创建了AopProxy对象(也就是不同的代理实现)。

在Spring中,如果一个bean有实现接口或者本身为代理类则会使用JDK动态代理方法进行创建,对应到类则为JdkDynamicAopProxy,如果不满足该条件则使用Cglib代理方式进行创建,对应到类则为ObjenesisCglibAopProxy。

以JdkDynamicAopProxy方式来详细描述,代理对象是如何创建的。

1
2
3
4
5
6
7
8
  public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

首先调用completeProxiedInterfaces方法获取到需要被代理对象的接口列表,然后将类加载器classLoader、接口列表、和JdkDynamicAopProxy(JdkDynamicAopProxy实现了InvocationHandler接口)当作参数,调用JDK动态代理方法Proxy.newProxyInstance去生成代理对象,

重点在于JdkDynamicAopProxy实现的InvocationHandler接口

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
 /**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
@Override
@Nullable
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;

try {
// 快速执行一些不需要代理的方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}

Object retVal;

if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}

// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);

// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}

// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
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);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}

在invoke方法中,首先对于equals方法、hashCode方法、来自接口的方法和来自Advised的接口直接执行,跳过代理的步骤。

紧接着调用getInterceptorsAndDynamicInterceptionAdvice方法获取到该方法上的拦截器链(AOP中定义的切面都会被转换成MethodInterceptor),如果拦截器链为空,直接反射调用对象,不需要进行代理。如果拦截器链存在,则依次调用该方法的所有代理方法,并返回。

到此,JDK的动态代理对象就已经创建完成了,Spring会把创建好的代理对象加入到容器中,所以我们在使用容器中的对象时,其实都已经变成了代理对象了。