我們知道,使用面向對象編程OOP有一些弊端,當需要多個不具有繼承關係的對象引入同一個公共行為時,例如日誌、安全檢測等,我們只有在每個對象裏引用公共行為,這樣程序中就產生了大量重複代碼,程序就不便於維護了,所以就有了一個面向對象的補充,即面向方面編程AOP,AOP所關注的方向是橫向的,不同於OOP的縱向

動態AOP使用示例

1、創建用於攔截的bean

在實際工作中,此bean可能是滿足業務需要的核心邏輯,例如test方法中可能會封裝著某個核心業務,但是,如果我們想在test前後加入日誌來跟蹤調試,如果直接修改源碼並不符合面向對象的設計方法,而且隨意改動原有代碼也會造成一定的風險,還好spring幫我們做到了這點

package test;

public class TestBean {

private String test;

public String getTest() {
return test;
}

public void setTest(String test) {
this.test = test;
}

public void test(){
System.out.println("test");
}

}

2、創建Advisor

package test;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class AspectJTest {

@Pointcut("execution(* *.test(..))")
public void test1(){

}
@Before("test1()")
public void beforeTest(){
System.out.println("beforeTest");
}
@After("test1()")
public void afterTest(){
System.out.println("afterTest");
}
@Around("test1()")
public Object arountTest(ProceedingJoinPoint p){
System.out.println("arountBefore");
Object o = null;
try {
o = p.proceed();
}
catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("arountAfter");
return o;
}
}

3、創建配置文件

要在spring中開啟AOP功能,還需要在配置文件中作如下聲明:

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

<aop:aspectj-autoproxy />

<bean id="test" class="test.TestBean"/>

<bean class="test.AspectJTest"/>

</beans>

測試

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("AopTests.xml");
TestBean bean = (TestBean)ac.getBean("test");
bean.test();
}

}

結果

spring實現了對所有類的test方法進行增強,使輔助功能可能獨立於核心業務之外,方便與程序的擴展和解耦

那麼spring究竟是如何實現AOP的呢?首先我們知道,spring是否支持註解的AOP是由一個配置文件控制的,也就是<aop:aspectj-autoproxy />,當在配置文件中聲明瞭這句配置的時候,spring就會支持註解的AOP,那麼我們的分析就從這句註解開始。

動態AOP自定義標籤

前面的文章說過spring中的自定義註解,如果聲明瞭自定義的註解,那麼就一定會在程序中的某個地方註冊了對應的解析器。我們搜索整個代碼,全局搜索後發現了AOPNamespaceHandler中對應著這樣一段函數

public class AopNamespaceHandler extends NamespaceHandlerSupport {

/**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* {@code config}, {@code spring-configured}, {@code aspectj-autoproxy}
* and {@code scoped-proxy} tags.
*/
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}

}

接著看看AspectJAutoProxyBeanDefinitionParser的內部實現

註冊AspectJAnnotationAutoProxyCreator

所有解析器,因為是對BeanDefinitionParser介面的統一實現,入口都是從parse函數開始的

public BeanDefinition parse(Element element, ParserContext parserContext) {
//註冊AspectJAnnotationAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
//對於註解中的子類的處理
extendBeanDefinition(element, parserContext);
return null;
}

繼續進入註冊函數

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
//註冊或升級AutoProxyCreator定義為beanName為org.Springframework.sop.config.internalAutoProxyCreator的BeanDefinition
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//對於proxy-target-class以及expose-proxy屬性的處理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
//註冊組件並通知,便於監聽器做進一步處理
registerComponentIfNecessary(beanDefinition, parserContext);
}

這裡主要做了3件事情,基本上每一行代碼就是一個完整的邏輯

1、註冊或者升級AnnotationAwareAspectJAutoProxyCreator

對於AOP的實現,基本上都是靠AnnotationAwareAspectJAutoProxyCreator去完成,它可以根據@Point註解定義的切點來自動代理相匹配的bean。但是為了配置簡便,spring使用了自定義配置來幫助我們自動註冊AnnotationAwareAspectJAutoProxyCreator,註冊過程如下

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//如果已經存在了自動代理創建器且存在的自動代理創建器與現在的不一致那麼需要根據優先順序來判斷到底需要使用哪個
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//AUTO_PROXY_CREATOR_BEAN_NAME = org.springframework.aop.config.internalAutoProxyCreator
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) {
//改變bean最重要的就是改變bean所對應的className
apcDefinition.setBeanClassName(cls.getName());
}
}
//如果已經存在自動代理器並且與將要創建的一致,那麼無需再此創建
return null;
}
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;
}

2、處理proxy-target-class以及expose-proxy屬性

useClassProxyingIfNecessary實現了proxy-target-class屬性以及expose-proxy屬性的處理

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
if (sourceElement != null) {
//對於proxy-target-class屬性的處理
boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
//對於expose-proxy屬性的處理
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}

強制使用的過程其實也是一個屬性設置的過程

//強制使用的過程其實也是一個屬性設置的過程
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}

關於JDK動態代理和CGLIB動態代理可以參考我之前的兩篇文章

時光飛逝of:JDK 動態代理 原理分析?

zhuanlan.zhihu.com
圖標
時光飛逝of:CGLIB 動態代理 原理分析?

zhuanlan.zhihu.com
圖標

proxy-target-class:SpringAOP部分使用JDK動態代理或者CGLIB動態代理來為目標對象創建代理(盡量建議使用JDK的動態代理),如果被代理的模板對象實現了至少一個介面,則會使用JDK動態代理。所有該模板類型實現的介面都將被代理。若該模板對象沒有實現任何介面,則創建一個CGLIB代理,如果你希望強制使用CGLIB代理,(例如希望代理模板對象的所有方法,而不只是實現自介面的方法)那也可以。但是需要考慮一下兩個問題

  1. 無法通知Final方法,因為它們不能被覆蓋
  2. 你需要CGLIB的依賴jar包

與之相比,JDK本身就提供了動態代理,強制使用CGLIB代理需要將<aop:config>的proxy-target-class屬性設為true

<aop:config proxy-target-class="true"> ...</aop:config>

當需要使用CGLIB代理和@AspectJ自動代理支持,可以按照一下方式設置<aop:aspectj-autoproxy>的proxy-target-class屬性

<aop:aspectj-autoproxy proxy-target-class="true"/>

而實際使用的過程中才會發現細節問題的差別

  • JDK動態代理:其代理對象必須是某個介面的實現,它是通過在運行期間創建一個介面的實現類來完成對目標對象的代理
  • CGLIB代理:實現原理類似於JDK動態代理,只是它在運行期間生成的代理對象是針對目標類擴展的子類。CGLIB是高效的代碼生成包,底層依靠ASM(開源的java位元組碼編輯類庫)操作位元組碼實現的,性能比JDK強。
  • expose-proxy有時候目標對象內部的自我調用將無法實施切面中的增強

創建AOP代理

前面通過自定義配置完成了對AnnotationAwareAspecJAutoProxyCreator類型的自動註冊,那這個類到底做了什麼工作來完成AOP的操作呢,先看看這個類的層次結構

我們看到其實現了BeanPostProcessor介面,當spring載入這個bean時會在實例化調用其postProcessAfterInitialization方法,而我們對於AOP邏輯的分析也由此開始,在父類AbstractAutoProxyCreator的postProcessAfterInitialization中代碼如下:

/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
//根據給定的bean的class和name構建出個key,格式:beanClassName_beanName
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.containsKey(cacheKey)) {
//如果它適合被代理,則需要封裝指定bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

/**
* 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 (beanName != null && this.targetSourcedBeans.containsKey(beanName)) {
return bean;
}
//無需增強
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//給定的bean類是否代表一個基礎設施類,基礎設施類不應代理,或者配置了指定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;
}

函數中我們已經看到了代理創建的雛形。當然,真正開始之前還需要經過一些判斷,比如是否已經處理過或者是否是需要跳過的bean,而真正創建代理的代碼是從getAdvicesAndAdvisorsForBean開始的

創建代理主要包含了兩個步驟

  1. 獲取增強方法或者增強器
  2. 根據獲取的增強進行代理

首先看看獲取增強方法的實現邏輯

@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
List advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}

/**
* Find all eligible Advisors for auto-proxying this class.
* @param beanClass the clazz to find advisors for
* @param beanName the name of the currently proxied bean
* @return the empty List, not {@code null},
* if there are no pointcuts or interceptors
* @see #findCandidateAdvisors
* @see #sortAdvisors
* @see #extendAdvisors
*/
protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}

對於指定bean的增強方法的獲取一定是包含兩個步驟的,獲取所有的增強以及尋找所有增強中適用於bean的增強並應用,那麼findCandidateAdvisors與findAdvisorsThatCanApply便做了這兩件事情。當然,如果無法找到對應的增強器便返回DO_NOT_PROXY,這個值為null

獲取增強器

由於我們分析的是使用註解進行的AOP,所以對於findCandidateAdvisors的實現其實是由AnnotationAwareAspectJAutoProxyCreator類完成的,我們繼續findCandidateAdvisors方法

@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
//當使用註解方式配置AOP的時候並不是丟棄了對XML配置的支持
//在這裡調用父類方法載入配置文件中的AOP聲明
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;

AnnotationAwareAspectJAutoProxyCreator繼承了AbstractAdvisorAutoProxyCreator,在實現獲取增強的方法中除了保留父類的獲取配置文件中定義的增強外,同時添加了獲取Bean的註解增強的功能,正是由buildAspectJAdvisors 實現的

解析的流程如下

  1. 獲取所有beanName,這一步驟中所有在beanFactory中註冊的Bean都會被提取出來
  2. 遍歷所有beanName,並找出聲明AspectJ註解的類,進行進一步處理
  3. 對標記為AspectJ註解的類進行增強器的提取
  4. 將提取結果假如緩存

/**
* 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 = null;

synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<Advisor>();
aspectNames = new LinkedList<String>();
//獲取所有的beanName
String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
//循環所有的beanName找出對應的增強方法
for (String beanName : beanNames) {
//不合法的bean則略過,由子類定義規則,默認返回true
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
//獲取對應的bean的類型
Class beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
//如果存在Aspect註解
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);
//解析標記AspectJ註解中的增強方法
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.EMPTY_LIST;
}
//記錄在緩存中
List<Advisor> advisors = new LinkedList<Advisor>();
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;
}

至此,我們已經完成了Advisor的提取,在上面的步驟中最為重要也最為繁雜的就是增強器的獲取。而這一功能委託給了getAdvisors方法去實現

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
//獲取標記為AspectJ的類
final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
//獲取標記為AspectJ的name
final String aspectName = maaif.getAspectMetadata().getAspectName();
//驗證
validate(aspectClass);

// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(maaif);

final List<Advisor> advisors = new LinkedList<Advisor>();
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}

// If its a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
//如果尋找的增強器不為空而且又配置了增強延遲初始化那麼需要在首位假如同步實例化增強器
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}

// Find introduction fields.
//獲取DeclareParents註解
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}

return advisors;
}

中間調用的getAdvisorMethods方法

private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new LinkedList<Method>();
ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException {
// Exclude pointcuts
//聲明為Pointcut的方法不處理
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
}
});
Collections.sort(methods, METHOD_COMPARATOR);
return methods;
}

函數中首先完成了對增強器的獲取,包括獲取註解以及根據註解生成增強的步驟,然後考慮在配置中可能會將增強配置成延遲初始化,那麼需要在首位假如同步實例化增強器以保證增強使用之前的實例化,最後是對DeclareParents註解的獲取

普通增強器的獲取

普通增強器的獲取邏輯通過getAdvisor方法實現,實現步驟包括對切點的註解的獲取以及根據註解信息生成增強

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif,
int declarationOrderInAspect, String aspectName) {

validate(aif.getAspectMetadata().getAspectClass());

//切點信息的獲取
AspectJExpressionPointcut ajexp =
getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
if (ajexp == null) {
return null;
}
//根據切點信息生成增強器
return new InstantiationModelAwarePointcutAdvisorImpl(
this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
}

切點信息的獲取。所謂獲取切點信息就是指定註解的表達式信息的獲取,如@Before("test()")

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
//獲取方法上的註解
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
//使用AspectJExpressionPointcut實例封裝獲取的信息
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
//提取得到的註解中的表達式如:
//@Pointcut("execution(* *.test(..))")中的execution(* *.test(..))
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
return ajexp;
}

/**
* Find and return the first AspectJ annotation on the given method
* (there <i>should</i> only be one anyway...)
*/
@SuppressWarnings("unchecked")
protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method) {
//設置敏感的註解類
Class<? extends Annotation>[] classesToLookFor = new Class[] {
Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
for (Class<? extends Annotation> c : classesToLookFor) {
AspectJAnnotation foundAnnotation = findAnnotation(method, c);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
//獲取指定方法上的註解並使用AspectJAnnotation封裝
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
A result = AnnotationUtils.findAnnotation(method, toLookFor);
if (result != null) {
return new AspectJAnnotation<A>(result);
}
else {
return null;
}
}

根據切點信息生成增強。所有的增強都由Advisor的實現類InstantiationModelAwarePointcutAdvisorImpl統一封裝的

public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp,
MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) {

this.declaredPointcut = ajexp;
this.method = method;
this.atAspectJAdvisorFactory = af;
this.aspectInstanceFactory = aif;
this.declarationOrder = declarationOrderInAspect;
this.aspectName = aspectName;

if (aif.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut =
Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
// If its not a dynamic pointcut, it may be optimized out
// by the Spring AOP infrastructure after the first evaluation.
this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif);
this.lazy = true;
}
else {
// A singleton aspect.
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
this.pointcut = declaredPointcut;
this.lazy = false;
}
}

在封裝過程中只是簡單地將信息封裝在類的實例中,所有的信息單純的賦值,在實例初始化的過程中還完成了對於增強器的初始化。因為不同的增強所體現的邏輯是不同的,而根據註解中的信息初始化對應的增強器就是在instantiateAdivce函數中實現的

private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
return this.atAspectJAdvisorFactory.getAdvice(
this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {

Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);

AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}

// If we get here, we know we have an AspectJ method.
// Check that its an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method " + candidateAdviceMethod + " in class [" +
candidateAspectClass.getName() + "]");
}

if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}

AbstractAspectJAdvice springAdvice;
//根據不同的註解類型封裝不同的增強器
switch (aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut " + candidateAdviceMethod.getName() + "");
}
return null;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method " + candidateAdviceMethod);
}

// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrderInAspect);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}

我們嘗試分析幾個常用的增強器的實現

MethodBeforeAdviceInterceptor

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

private MethodBeforeAdvice advice;

/**
* Create a new MethodBeforeAdviceInterceptor for the given advice.
* @param advice the MethodBeforeAdvice to wrap
*/
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}

public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}

}

其中的屬性MethodBeforeAdvice代表著前置增強的AspectJMethodBeforeAdvice,接著before方法

public void before(Method method, Object[] args, Object target) throws Throwable {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}

protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// TODO AopUtils.invokeJoinpointUsingReflection
//激活增強方法
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
this.aspectJAdviceMethod + "]; pointcut expression [" +
this.pointcut.getPointcutExpression() + "]", ex);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}

AspectJAfterAdvice

後置增強與前置增強有稍許不一樣的地方。前置增強大致的結構是在攔截器鏈中放置MethodBeforeAdviceInterceptor,而在MethodBeforeAdviceInterceptor中又放置了AspectJMethodBeforeAdvice,並在調用invoke時首先串聯調用。但是在後置增強的時候卻不一樣,沒有提供中間的類,而是直接在攔截器鏈中使用了中間的AspectJAfterAdvice

public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice {

public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

super(aspectJBeforeAdviceMethod, pointcut, aif);
}
//激活增強方法
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}

public boolean isBeforeAdvice() {
return false;
}

public boolean isAfterAdvice() {
return true;
}

}

增加同步實例化增強器

如果尋找的增強器不為空而且又配置了增強延遲初始化,那麼就需要在首位加入同步實例化增強器。

@SuppressWarnings("serial")
protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor {

public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) {
super(aif.getAspectMetadata().getPerClausePointcut(), new MethodBeforeAdvice() {
public void before(Method method, Object[] args, Object target) {
// Simply instantiate the aspect
aif.getAspectInstance();
}
});
}
}

獲取DeclareParents註解

DeclareParentsAdvisor主要用於引介增強的註解形式的實現,而其實現方式與普通增強類很類似,只不過使用DeclareParentsAdvisor對功能進行封裝

private Advisor getDeclareParentsAdvisor(Field introductionField) {
DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
if (declareParents == null) {
// Not an introduction field
return null;
}

if (DeclareParents.class.equals(declareParents.defaultImpl())) {
// This is what comes back if it wasnt set. This seems bizarre...
// TODO this restriction possibly should be relaxed
throw new IllegalStateException("defaultImpl must be set on DeclareParents");
}

return new DeclareParentsAdvisor(
introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}

尋找匹配的增強器

前面的函數已經完成了所有增強器的解析,但是對於所有增強器來說,並不一定都適用於當前的Bean,還要挑取出適合的增強器,也就是滿足我們配置的通配符的增強器

protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class beanClass, String beanName) {

ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
//首先處理引介增強
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;
}
//對於普通bean的處理
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}

上面函數的主要功能是尋找所有增強器中適用於當前class的增強器。引介增強與普通增強的增強是處理不一樣的。所以分開處理。而對於真正的匹配在canApply中實現

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesnt have a pointcut so we assume it applies.
return true;
}
}
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}

MethodMatcher methodMatcher = pc.getMethodMatcher();
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class> classes = new LinkedHashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}

return false;
}

未完待續。。。。

推薦閱讀:

相關文章