NOTE
Spring AOP Source Analysis Summary
Created on Monday, July 22, 2019. Source analysis of Spring AOP from @EnableAspectJAutoProxy through auto-proxy creation and target-method interceptor execution.
This is a historical learning note and may contain outdated or incomplete understanding.
Created on Monday, July 22, 2019
Source Analysis
1. @EnableAspectJAutoProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)// imports the AspectJAutoProxyRegistrar component
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
2. AspectJAutoProxyRegistrar
// Implements ImportBeanDefinitionRegistrar and can import Beans into the IoC container.
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// Register the AnnotationAwareAspectJAutoProxyCreator Bean in the container.
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
3. AopConfigUtils
3.1. registerAspectJAnnotationAutoProxyCreatorIfNecessary
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
// Import AnnotationAwareAspectJAutoProxyCreator.
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
3.2. registerOrEscalateApcAsRequired
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// The Bean org.springframework.aop.config.internalAutoProxyCreator already exists.
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
// If it does not exist, create the AnnotationAwareAspectJAutoProxyCreator Bean
// and put it into the IoC container.
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
4. AnnotationAwareAspectJAutoProxyCreator
4.1. Class Hierarchy
AnnotationAwareAspectJAutoProxyCreator mainly implements:
the BeanFactoryAware interface and its setBeanFactory method (inject BeanFactory into the Bean);
and the BeanPostProcessor interface, including postProcessBeforeInitialization (do something before Bean initialization) and postProcessAfterInitialization (do something after Bean initialization).
5. Process Analysis
5.1. Debugging
Set breakpoints on the key methods in the AnnotationAwareAspectJAutoProxyCreator class hierarchy and run the program.
-
Pass in the configuration class and create the IoC container.
-
Register the configuration class and call refresh() to refresh the container.
-
registerBeanPostProcessors(beanFactory): register Bean post-processors to intercept Bean creation.
-
First obtain all BeanPostProcessors in the IoC container.
-
Add other BeanPostProcessors to the container.
-
First register BeanPostProcessors that implement PriorityOrdered.
-
Then register BeanPostProcessors that implement Ordered.
-
Then register BeanPostProcessors that do not implement these ordering interfaces.
-
Registering a BeanPostProcessor actually means creating the BeanPostProcessor and storing it in the container.
Create the BeanPostProcessor internalAutoProxyCreator:
-
Create the Bean instance.
-
populateBean: assign values to the Bean’s properties.
-
initializeBean: initialize the Bean.
-
invokeAwareMethods(): handle callbacks for Aware interfaces.
-
applyBeanPostProcessorsBeforeInitialization().
-
invokeInitMethods(): execute custom initialization methods.
-
applyBeanPostProcessorsAfterInitialization().
-
BeanPostProcessor (AnnotationAwareAspecJAutoProxyCreater).
-
Register the BeanPostProcessor with BeanFactory.
beanFactory.addBeanPostProcessor(processor)
====================== Through the process above, AnnotationAwareAspecJAutoProxyCreater is registered into the container =======================================
5.2. AbstractAutoProxyCreator
5.2.1. postProcessBeforeInstantiation
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
// Obtain the cache key.
Object cacheKey = getCacheKey(beanClass, beanName);
if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
// Determine whether it is already in the enhanced-Bean map.
if (this.advisedBeans.containsKey(cacheKey)) {
// If so, it has already been enhanced; return directly.
return null;
}
// Call AnnotationAwareAspectJAutoProxyCreator.isInfrastructureClass and shouldSkip.
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
if (beanName != null) {
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
this.targetSourcedBeans.add(beanName);
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
}
return null;
}
5.2.2. isInfrastructureClass
protected boolean isInfrastructureClass(Class<?> beanClass) {
// Determine whether it is an Advice, Pointcut, Advisor, or AopInfrastructureBean class.
boolean retVal = Advice.class.isAAopInfrastructureBeanssignableFrom(beanClass) ||
Pointcut.class.isAssignableFrom(beanClass) ||
Advisor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass);
if (retVal && logger.isTraceEnabled()) {
logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
}
return retVal;
}
5.2.3. postProcessAfterInitialization
// This method is called after the Calc object has been created.
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// Wrap Calc when necessary.
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
5.2.4. wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && 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.
// Obtain all advisors for Calc.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// Create a proxy class to enhance the current Bean.
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;
}
5.2.5. createProxy
protected Object createProxy(
Class<?> beanClass, String beanName, 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);
}
// Use the ProxyFactory proxy factory to create the object.
return proxyFactory.getProxy(getProxyClassLoader());
}
5.3. AnnotationAwareAspectJAutoProxyCreator
5.3.1. isInfrastructureClass
protected boolean isInfrastructureClass(Class<?> beanClass) {
// AbstractAutoProxyCreator.isInfrastructureClass || AbstractAspectJAdvisorFactory.isAspect
return (super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass));
}
5.3.2. isAspect
public boolean isAspect(Class<?> clazz) {
// Has the @Aspect annotation and does not have a field whose name begins with ajc$.
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
5.4. AspectJAwareAdvisorAutoProxyCreator
5.4.1. shouldSkip
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// TODO: Consider optimization by caching the list of the aspect names
// Find all enhanced aspects: the advice methods in LogAspect.
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
// If an advisor is an AspectJPointcutAdvisor and the Bean enhanced by the advisor
// is beanName, return true.
if (advisor instanceof AspectJPointcutAdvisor) {
if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
return true;
}
}
}
return super.shouldSkip(beanClass, beanName);
}
5.5. AbstractAdvisorAutoProxyCreator
5.5.1. getAdvicesAndAdvisorsForBean
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
5.5.2. findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// Obtain all advisors.
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// Filter out advisors that can be applied to the current Bean.
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
5.5.3. findAdvisorsThatCanApply
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
// Call AopUtils to obtain the advisors that can be applied.
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
5.6. AopUtils
5.6.1. findAdvisorsThatCanApply
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
// Iterate over all advisors; ultimately getClassFilter().matches() is used for the decision.
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
5.7. ProxyFactory
5.7.1. getProxy
public Object getProxy(ClassLoader classLoader) {
// ProxyCreatorSupport.createAopProxy
return createAopProxy().getProxy(classLoader);
}
5.8. ProxyCreatorSupport
5.8.1. createAopProxy
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
// Call DefaultAopProxyFactory.createAopProxy.
return getAopProxyFactory().createAopProxy(this);
}
5.9. DefaultAopProxyFactory
5.9.1. createAopProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// If there is no interface, use CGLIB inheritance.
return new ObjenesisCglibAopProxy(config);
}
else {
// If there is an interface, use a JDK proxy.
return new JdkDynamicAopProxy(config);
}
}
6. Target Method Execution
6.1. CglibAopProxy
6.1.1. intercept
// Intercept execution of the target method.
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
// Obtain the interceptor chain for the target method:
// AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice().
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
// No interceptor chain.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 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.
// Execute the target method directly.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
// Pass the target object, target method, interceptor chain, and other information
// into a CglibMethodInvocation.
// Then execute proceed on that object.
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
6.2. AdvisedSupportAdvisedSupport
6.2.1. getInterceptorsAndDynamicInterceptionAdvice
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
// DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
6.3. DefaultAdvisorChainFactory
6.3.1. getInterceptorsAndDynamicInterceptionAdvice
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
// Obtain all advisors and wrap them as Interceptors.
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
// Call DefaultAdvisorAdapterRegistry.getInterceptors.
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
6.4. DefaultAdvisorAdapterRegistry
6.4.1. getInterceptors
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
Advice advice = advisor.getAdvice();
// If it is a MethodInterceptor, add it directly.
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Use the adapter.
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}
6.5. ReflectiveMethodInvocation
6.5.1. proceed
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// If there is no interceptor chain, or execution has reached the final interceptor:
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// Execute the target method through reflection: method.invoke(target, args).
return invokeJoinpoint();
}
// Obtain interceptor n.
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
// Call ExposeInvocationInterceptor.invoke.
// The last time this path is taken it calls MethodBeforeAdviceInterceptor.invoke.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
6.6. ExposeInvocationInterceptor
6.6.1. invoke
public Object invoke(MethodInvocation mi) throws Throwable {
// private static final ThreadLocal<MethodInvocation> invocation =
// new NamedThreadLocal<MethodInvocation>("Current AOP method invocation");
MethodInvocation oldInvocation = invocation.get();
// Associate the current interceptor invocation with ThreadLocal.
invocation.set(mi);
try {
// Continue calling ReflectiveMethodInvocation.proceed.
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}
6.7. MethodBeforeAdviceInterceptor
6.7.1. invoke
public Object invoke(MethodInvocation mi) throws Throwable {
// 1. Execute the before advice.
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 2. Continue calling ReflectiveMethodInvocation.proceed;
// at this point the target method is executed.
return mi.proceed();
}
6.8. AspectJAfterAdvice
6.8.1. invoke
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 4. Execute proceed.
return mi.proceed();
}
finally {
// 3. Execute the after advice.
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
6.9. AfterReturningAdviceInterceptor
6.9.1. invoke
public Object invoke(MethodInvocation mi) throws Throwable {
// Execute the method first.
Object retVal = mi.proceed();
// Execute afterReturning only when no problem occurred.
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
6.10. ThrowsAdviceInterceptor
6.10.1. invoke
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
Method handlerMethod = getExceptionHandler(ex);
if (handlerMethod != null) {
// Execute the exception-handling method.
invokeHandlerMethod(mi, ex, handlerMethod);
}
throw ex;
}
}

Discussion
Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub