Spring-AOP原始碼分析
一、概述
Spring的兩大特性:IOC和AOP。
AOP是面向切面程式設計,Spring內建了自己實現的基於動態代理技術的AOP,同時還支援成熟的AspectJ框架,我們這裡主要講述的還是內建的基於動態代理的AOP實現。因為面對一些普通的需求,Spring內建的AOP已經綽綽有餘。
AOP一般用於增強原來的程式碼的功能,這種增強體現在輔助方面,比如安全、日誌、事務等。
二、術語
1、連線點(JoinPoint)
連線點就是具體的程式碼中的切入點,指的是具體的一處程式碼位置。
2、切點(PointCut)
切點是對一系列代表同種功能(目的)的切入點(連線點)的統稱,切點不是一個點,而是代表某一功能的一系列連線點的集合。
3、通知(Advice)
通知就是我們要在切點執行的操作,就是我們要實現的目的,是要實現的功能的程式碼實現。一般通知又稱為增強,所謂增強就是對原來功能的基礎上新增新功能,進行功能增強。
4、切面(Aspect)
切面是通知和切點的綜合體,定義好了一個切面,那麼我們就知道,這個AOP要實現的功能是什麼,需要切入到程式中的那些地方。抽象層次中,切面是程式碼功能的橫切面,這個橫切面的位置就是切點、這個切面的功能就是通知。
5、織入(Weaving)
織入就是切面作用到目的碼中的方式,Spring內建的AOP採用的是動態代理的方式來織入,還可以採用編譯器織入和載入期織入,後兩者分別需要特定的編譯器和類載入器來完成,後兩種方式是AspectJ所支援的織入方式。
6、引介(Introduction)
引介是另一種型別的增強,它可以為類新增一些屬性和方法。它和通知是並列的兩種不同的增強。
7、目標物件(Target)
目標物件就是我們想要織入的物件,一般不會是一個,通常是一批符合條件的物件。
8、代理(Proxy)
代理就好理解了,Spring內建的AOP就是通過動態代理的方式實現織入的,建立目標物件的代理類,在代理類中執行通知的內容,然後在合適的位置呼叫目標物件的方法,來達到織入的目的。
三、Spring AOP概述
Spring AOP是基於動態代理技術實現的切面程式設計,代理技術包括JDK動態代理和CGLIB動態代理,前者基於介面實現,後者基於類實現。
1、Spring中使用AOP技術的方式:
1)定義切面類,使用@Aspect註解
2)在切面類中定義切點方法,使用@PointCut註解
3)在切面類中定義通知方法,使用@Before、@After、@Around等註解
4)在通知方法的註解中使用切點方法
5)在切面類上加設註解@Component
6)啟動AOP功能,兩種方式:原始的XML配置方式和註解方式
XMl方式:aop:aspectj-autoproxy/
註解方式:@EnableAspectJAutoProxy
配置方式:spring.auto.proxy=true
2、Spring AOP支援的增強型別
通知增強:Advice
前置通知:MethodBeforeAdvice-在連線點之前執行
後置通知:AfterReturningAdvice-在連線點正常執行完後執行,如果還未到連線點就異常,不會執行
環繞通知:AroundAdvice-在連線點前後執行
異常通知:AfterThrowingAdvice-在連線點丟擲異常後執行
finally通知:AfterAdvice-最終執行,無論是否異常,方法執行結束都會執行
引介增強:IntroductionInterceptor->DelegatingIntroductionInterceptor
前五種很好理解,重點是最後一種引介增強,這種增強並不是針對方法的增強,而是針對類的增強,它會為目標新增屬性和方法,比如它可以為目標類新增一個介面的實現。目標類原本沒有實現某個介面,但是我們可以使用引介增強的方式為目標類建立一個實現該介面的代理,在這個代理中可以使用介面的功能來作用於目標物件。
3、原理圖
四、原始碼分析
1、入口
1.1 入口一:aop:aspectj-autoproxy/
原始碼1-來自:AopNamespaceHandler
@Override
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());
}
上面原始碼就是針對XML配置中aop:XXX/配置的解析,紅色部分正是針對aop:aspectj-autoproxy/的解析,如果配置了aspectj-autoproxy,則註冊Bean定義解析器:AspectJAutoProxyBeanDefinitionParser。
AspectJAutoProxyBeanDefinitionParser是一個實現了BeanDefinitionParser介面的類,專門用於解析切面自動代理的Bean定義的解析工作,重點在其parse方法。
原始碼2-來自:AspectJAutoProxyBeanDefinitionParser
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 1-註冊AnnotationAwareAspectJAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
// 2-擴充套件BeanDefinition
extendBeanDefinition(element, parserContext);
return null;
}
private void extendBeanDefinition(Element element, ParserContext parserContext) {
// 獲取BeanName為internalAutoProxyCreator的BeanDefinition,其實就是之前註冊的自動代理構建器
BeanDefinition beanDef =
parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
if (element.hasChildNodes()) {
// 如果當前元素有子節點,則給上面獲取的Bean定義新增子節點中明確定義的型別值(填充BeanDefinition)
addIncludePatterns(element, parserContext, beanDef);
}
}
private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
ManagedList<TypedStringValue> includePatterns = new ManagedList<>();
NodeList childNodes = element.getChildNodes();//獲取子節點
for (int i = 0; i < childNodes.getLength(); i++) {
// 遍歷子節點,獲取子節點中name屬性值,封裝到TypeStringValue中
// 在上下文中提取子節點includeElement的元資料儲存到TypedStringValue的source屬性中
// 最後封裝好的TypeStringValue儲存到includePatterns列表中
Node node = childNodes.item(i);
if (node instanceof Element) {
Element includeElement = (Element) node;
TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
valueHolder.setSource(parserContext.extractSource(includeElement));
includePatterns.add(valueHolder);
}
}
if (!includePatterns.isEmpty()) {
// 從解析上下文parserContext中提取指定節點element的元資料儲存到includePatterns的source屬性中,
// 然後將includePatterns儲存到BeanDefinition的propertyValues屬性中
includePatterns.setSource(parserContext.extractSource(element));
beanDef.getPropertyValues().add("includePatterns", includePatterns);
}
}
}
上面程式碼中有兩個重點,首先就是registerAspectJAnnotationAutoProxyCreatorIfNecessary方法呼叫,用於註冊AnnotationAwareAspectJAutoProxyCreator構建器;另一點就是在構建器註冊完成後,為其填充一些必要內容,這些內容為XML配置中子節點的配置內容,具體內容參照原始碼,這裡重點看看第一步,註冊構建器的原始碼:
原始碼3-來自:AopNamespaceUtils
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 1-註冊或升級AnnotationAwareAspectJAutoProxyCreator
// parserContext.getRegistry()獲取到的是BeanDefinitionRegistry註冊器,第二個引數是提取的指定元素的元資料
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 2-校驗並設定是否適用基於CGLIB的動態代理實現AOP,和是否要暴露代理類
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 3-註冊成元件
registerComponentIfNecessary(beanDefinition, parserContext);
}
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if (sourceElement != null) {
boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));//獲取XML中設定的proxy-target-class屬性的值,解析為Boolean值
if (proxyTargetClass) {
// 如果為true,則強制自動代理構建器使用基於類的動態代理CGLIB,需要將屬性設定到自動代理構建器的BeanDefinition中
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));//獲取XML中配置的expose-proxy屬性的值,同樣解析為Boolean值
if (exposeProxy) {
// 如果為true,強制自動代理構建器暴露代理類,需要將屬性設定到自動代理構建器的BeanDefinition中
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
if (beanDefinition != null) {
// 將自動代理構建器包裝成為一個Bean元件定義。
// Bean元件定義是將一個BeanDefinition中包含的所有的屬性的值(可能為一個BeanDefinition或者BeanReference)全部封裝起來成為一個元件包,然後將其註冊到解析上下文中
BeanComponentDefinition componentDefinition =
new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
parserContext.registerComponent(componentDefinition);
}
}
registerAspectJAnnotationAutoProxyCreatorIfNecessary方法中主要做了三件事情:
1-註冊構建器
2-配置屬性
3-元件註冊
針對第2和第3,在原始碼註釋中解釋的很清楚啦,主要看看第一步,繼續進行構建器的註冊:
原始碼4-來自:AopConfigUtils
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//如果構建器已經載入,獲取其BeanDefinition,新增屬性proxyTargetClass,值為true
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//如果構建器已經載入,獲取其BeanDefinition,新增屬性exposeProxy,值為true
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
@Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 1-如果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) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
// 尚未註冊internalAutoProxyCreator的情況下,將給定的構建器包裝成RootBeanDefinition,然後註冊這個BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
// 把元資料儲存到BeanDefinition中
beanDefinition.setSource(source);
// 設定為最高優先值
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
// 設定為基礎角色
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 2-以internalAutoProxyCreator為beanName註冊當前BeanDefinition(AnnotationAwareAspectJAutoProxyCreator類)
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
原始碼4中前兩個方法時原始碼3中第二步裡面配置屬性時呼叫的方法,在此給出。
原始碼4的1-中是在已存在一個自動代理構建器的情況下,將其與新的給定的AnnotationAwareAspectJAutoProxyCreator構建器的優先順序進行比對,取優先極高的。
最後的registerBeanDefinition方法用於註冊BeanDefinition。至此,自動代理構建器載入完畢。
1.2 入口二:@EnableAspectJAutoProxy
原始碼5-來自:EnableAspectJAutoProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
*/
boolean proxyTargetClass() default false;
/**
* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
* Off by default, i.e. no guarantees that {@code AopContext} access will work.
* @since 4.3.1
*/
boolean exposeProxy() default false;
}
原始碼中重點關注@Import(AspectJAutoProxyRegistrar.class),很明顯這個註解匯入了一個新類:AspectJAutoProxyRegistrar。
使用@Import註解匯入的方式可以將一個類註冊到BeanFactory中。
原始碼6-來自AspectJAutoProxyRegistrar
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 1-註冊或升級自動代理構建器
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
// 2-封裝註解屬性,並根據屬性進行配置
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
我們可以從原始碼5中看到註解@EnableAspectJAutoProxy內部有兩個屬性設定proxyTargetClass和exposeProxy,這個之前的入口一中原始碼3裡面的2-中的設定是一樣的,即我們在XML啟動AOP的時候也可以設定這兩個值。
proxyTargetClass屬性表示是否適用基於類的的動態代理CGLIB來建立代理類。true表示使用,false表示不使用,預設是false。
exposeProxy屬性表示是否暴露生成的代理類,暴露就是可手動呼叫,最常見的情況如,在一個類中使用this呼叫帶有@Transactional註解的方法,你會發現事務是不生效的,這時候我們就可以將生成的代理暴露出來手動呼叫代理類來保證事務生效:
如下例子中:
public interface TestService {
void testMethod1();
void testMethod2();
}
public class TestServiceImpl {
@Transactional
public void testMethod1(){
//some transaction operate
}
public void testMethod2(){
this.testMethod1();
}
}
在testMethod2中以this.testMethod1()方式呼叫帶事務註解的testMethod1方法時,其事務是不生效的。修改方式就是將exposeProxy設定為true,然後修改呼叫方式為:
1
((TestService)AopContext.currnetProxy()).testMethod1();
原始碼6中1-呼叫的是AopConfigUtils中的方法:
原始碼7-來自:AopConfigUtils
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
然後它再呼叫的就是原始碼4中的原始碼了,到這裡呼叫的就是公共部分啦。那麼這一部分也就到此為止了,其餘見原始碼4之後的部分。
到此兩個主要的入口都分析完畢,入口的主要作用就是註冊或者升級自動代理構建器,因為之後AOP的操作基本都要依靠這個構建器來完成。
2、建立AOP代理
我們的重點就是構建器AnnotationAwareAspectJAutoProxyCreator,分析下其繼承結構,你會發現它實現了BeanPostProcessor介面。
BeanPostProcessor介面有兩個方法:
postProcessBeforeInitialization:Bean例項初始化前呼叫
postProcessAfterInitialization:Bean例項初始化之後呼叫
然後我們在其繼承體系中尋找實現這兩個方法的類,一番尋找後發現:AbstractAutoProxyCreator
原始碼8-來自:AbstractAutoProxyCreator
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
// 1-生成快取key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 校驗該key是否已存在於earlyProxyReferences快取
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 2-執行建立代理物件
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
我們發現了這個方法的實現,這個方法會在構建器Bean初始化之後被呼叫。我們看看它做了啥?
重點就是wrapIfNecessary方法,這個方法用於代理物件的生成。
原始碼9-來自:AbstractAutoProxyCreator
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)) {
// 檢測目標類是否是AOP的基礎設施類,基礎設施類包括Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否需要跳過代理,如果是則將其設定為無需增強
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 1-獲取針對當前Bean的增強
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
// 如果獲取到增強則執行下面的建立代理
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 2-建立代理
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的增強和建立代理這兩步。
首先來看下如何獲取增強
原始碼10-來自:AbstractAdvisorAutoProxyCreator
//獲取通知(增強)
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
原始碼11-來自:AbstractAdvisorAutoProxyCreator
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 1-先獲取所有的增強器列表
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 2-獲取應用到當前目標類的增強器列表
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
//這個方法是一個鉤子方法,用於子類重寫擴充套件
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);//排序
}
return eligibleAdvisors;
}
上面的原始碼我們重點關注findCandidateAdvisors方法和findAdvisorsThatCanApply方法,分別用於找出BeanFactory中所有的可用的增強器,和從可用增強器中找出作用於目標類的增強器。
原始碼12-來自:AbstractAdvisorAutoProxyCreator
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
原始碼13-來自:BeanFactoryAdvisorRetrievalHelper
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
String[] advisorNames = null;
synchronized (this) {
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!
// 1-獲取當前BeanFactory及其繼承體系中的容器中所有的Advisor型別的Bean的BeanName陣列,排除掉FactoryBeans
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
}
if (advisorNames.length == 0) {
return new LinkedList<>();
}
List<Advisor> advisors = new LinkedList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
// 2-再排除建立中的Bean
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
// 3-把剩下的Bean新增到通知(增強器)列表中
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;
}
這段原始碼看起來挺複雜,我們去掉一些不必要的內容,總結起來,總共三件事:
1-獲取容器中所有的Advisor,排除FactoryBeans
2-再排除建立中的Bean
3-將剩餘的Advisor通過getBean建立Bean例項並新增到列表中返回
至於再下一步的邏輯就不再探尋啦。
然後是原始碼11中的2-findAdvisorsThatCanApply方法
原始碼14-來自:AbstractAdvisorAutoProxyCreator
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
// 將代理的目標Bean的beanName設定到ThreadLocal中
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
// 1-在候選增強器中找出可作用於目標Bean的增強器
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
重點操作就是AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);用於找出能作用於目標Bean的增強器列表
原始碼15-來自:AopUtils
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
// 1-首先新增引介增強
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
//跳過引介增強
continue;
}
//校驗其餘增強器是否能應用到目標Bean
if (canApply(candidate, clazz, hasIntroductions)) {
// 2-新增通知增強
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
原始碼中可以看出來,這裡針對了兩種型別的增強器(引介增強和通知增強)分別進行了處理,首先處理了引介增強,然後是通知增強。
這裡有兩個canApply方法,第一個用於判斷引介增強是否能作用到目標類,第二個用於判斷通知增強是否能作用到目標類。
其實第一個是通過呼叫第二個實現的。
我覺得我們可以簡單看看其實現邏輯:
原始碼16-來源:AopUtils
//用於校驗引介增強
public static boolean canApply(Advisor advisor, Class<?> targetClass) {
return canApply(advisor, targetClass, false);
}
//用於校驗通知增強
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 doesn't 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();// 獲取方法匹配器
if (methodMatcher == MethodMatcher.TRUE) {// 獲取方法匹配器匹配的是所有方法,這裡直接返回true,不必要做校驗
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
// 方法匹配校驗
return true;
}
}
}
return false;
}
上面的原始碼中用於判斷增強是否與給定的類匹配,用了多個matches方法,包括ClassFilter的matches方法,IntroductionAwareMethodMatcher的matches方法和MethodMatcher中的matches方法。
第一個matches匹配的是類,第二、三個matches匹配的是方法。其中引介增強針對的是類,所以其校驗僅僅使用類級匹配進行校驗,但是通知增強針對的是類中的方法,需要進行類和方法的雙重匹配校驗。
然後返回到原始碼9中,我們開始生成代理:
原始碼17-來自:AbstractAutoProxyCreator
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
//暴露目標類,將其儲存到BeanDefinition中
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
//建立一個新的代理工廠,併為其拷貝當前類中的相關配置屬性
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
//校驗proxyTargetClass設定,如果設定不是直接代理目標類,則採用預設的JDK動態代理指定介面
if (shouldProxyTargetClass(beanClass, beanName)) {
//校驗該Bean的BeanDefinition中的preserveTargetClass屬性,是否被代理工廠設定為true,如果設定為true,則表示代理工廠希望代理類可以強轉為目標類
proxyFactory.setProxyTargetClass(true);
}
else {
//否則表示基於介面建立代理
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//將攔截器封裝成通知
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);//加入增強器
proxyFactory.setTargetSource(targetSource);//設定要代理的類
customizeProxyFactory(proxyFactory);//子類定製代理
//用於控制代理工廠被配置之後,是否還允許修改通知,預設為false(表示不允許修改)
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//建立代理
return proxyFactory.getProxy(getProxyClassLoader());
}
原始碼中執行了一大串的工作,都在為最後的建立代理做準備:
原始碼18-來自:ProxyFactory
public Object getProxy(@Nullable ClassLoader classLoader) {
//建立AOP代理,並獲取代理物件
return createAopProxy().getProxy(classLoader);
}
原始碼19-來自:ProxyCreatorSupport
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();//啟用開關
}
// 獲取AOP代理工廠,使用AOP代理工廠建立AOP代理
return getAopProxyFactory().createAopProxy(this);
}
private void activate() {
this.active = true;
// 回撥監聽器的activated方法
for (AdvisedSupportListener listener : this.listeners) {
listener.activated(this);
}
}
public AopProxyFactory getAopProxyFactory() {
return this.aopProxyFactory;
}
重點在createAopProxy方法:
原始碼20-來自:DefaultAopProxyFactory
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
// 如果代理需要執行優化或者proxyTargetClass=true或者不存在代理介面
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)) {
//如果目標類是介面或者是動態生成的代理類,則使用JDK動態代理
return new JdkDynamicAopProxy(config);
}
//建立CGLIB動態AOP代理物件
return new ObjenesisCglibAopProxy(config);
}
else {
//建立JDK動態AOP代理物件
return new JdkDynamicAopProxy(config);
}
}
至此,代理物件生成,至於是使用JDK動態代理,還是Cglib動態代理,機理如下:
如果目標類實現了介面,預設使用JDK動態代理
如果目標類實現了介面,可以強制使用Cglib動態代理
如果目標沒有實現介面,必須採用Cglib動態代理
至於如何強制使用Cglib動態代理:
首先需要新增CGLIB庫,然後設定proxyTargetClass置為true,進行強制使用基於類的CGLIB動態代理。
JDK動態代理和Cglib動態代理的區別:
JDK動態代理只能基於介面生成代理,方式是通過實現JDK提供的InvocationHandler介面中的invoke方法來實現針對目標類指定方法的代理呼叫。
CGLIB可以基於類生成代理,方式是通過對目標類生成一個子類,覆蓋其中的方法。
返回到原始碼18中,建立了AOP代理之後,執行其getProxy方法:(我們看下JDK動態代理的實現)
原始碼21-來自:JdkDynamicAopProxy
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
// 1-獲取用於代理的全部介面的集合(陣列)
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
// 2-查詢介面集合中可能定義的equals方法獲取hashCode方法
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 3-建立指定介面的代理例項
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
上面的代理主要做了三件事情:
1-首先獲取用於代理的全部介面集;
2-然後查詢該介面集中有無定義equals和hashCode方法;
3-最後執行代理例項的建立。
首先看下第一件事情:completeProxiedInterfaces
原始碼22-來自:AopProxyUtils
// 獲取用於代理的介面的集合
static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
// 獲取配置中所有的介面
Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
if (specifiedInterfaces.length == 0) {
// No user-specified interfaces: check whether target class is an interface.
Class<?> targetClass = advised.getTargetClass();
if (targetClass != null) {
if (targetClass.isInterface()) {
advised.setInterfaces(targetClass);
}
else if (Proxy.isProxyClass(targetClass)) {
advised.setInterfaces(targetClass.getInterfaces());
}
specifiedInterfaces = advised.getProxiedInterfaces();
}
}
// 將SpringProxy、Advised、DecoratingProxy三個介面新增到介面集中
boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
int nonUserIfcCount = 0;
if (addSpringProxy) {
nonUserIfcCount++;
}
if (addAdvised) {
nonUserIfcCount++;
}
if (addDecoratingProxy) {
nonUserIfcCount++;
}
Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
int index = specifiedInterfaces.length;
if (addSpringProxy) {
proxiedInterfaces[index] = SpringProxy.class;
index++;
}
if (addAdvised) {
proxiedInterfaces[index] = Advised.class;
index++;
}
if (addDecoratingProxy) {
proxiedInterfaces[index] = DecoratingProxy.class;
}
return proxiedInterfaces;
}
下面我們再看看之前的第二步:findDefinedEqualsAndHashCodeMethods
原始碼23-來自:JdkDynamicAopProxy
private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
for (Class<?> proxiedInterface : proxiedInterfaces) {
Method[] methods = proxiedInterface.getDeclaredMethods();
for (Method method : methods) {
// 遍歷每一個介面,再獲取其中的方法進行遍歷,逐個校驗其是否是equals方法,或者hashCode方法,只有當二者都被定義之後校驗才會結束,否則一直進行下去
if (AopUtils.isEqualsMethod(method)) {
this.equalsDefined = true;
}
if (AopUtils.isHashCodeMethod(method)) {
this.hashCodeDefined = true;
}
if (this.equalsDefined && this.hashCodeDefined) {
return;
}
}
}
}
最後就是我們的重點步驟:Proxy.newProxyInstance(classLoader, proxiedInterfaces, this),這個操作大家都不會陌生,這是JDK動態代理建立代理類的通用方式。這個方法的引數列表中最後一個是一個大家都很熟悉的傢伙:InvocationHandler,我們都知道JDK動態代理的執行邏輯都是在一個實現了InvocationHandler介面中的invoke方法中,這裡傳入this,表示當前例項,代表當前例項所屬類應該實現了InvocationHandler介面,之前已經成功建立了JDK動態代理物件,那麼當我們發生對指定目標方法的呼叫時,就會觸發JdkDynamicAopProxy中的invoke方法:
原始碼24-來自:JdkDynamicAopProxy
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
...
}
這也就是說,這個類中必然實現了invoke方法:
原始碼25-來自:JdkDynamicAopProxy
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
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.
// 目標類體系中未實現equals方法,但是代理的目標方法卻是equals方法
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
// 目標類體系中未實現hashCode方法,但是代理的目標方法卻是hashCode方法
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
// isAssignableFrom方法的意義:呼叫方如果是引數方的同類(介面)或者父類(介面),則返回true
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.
// 如果設定了exposeProxy=true,那麼就代理儲存起來備用
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.
// 1-獲取目標方法的攔截鏈
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...
// 2-建立一個方法呼叫,並執行,ReflectiveMethodInvocation是Spring封裝的
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// 3-執行攔截器鏈,在ReflectiveMethodInvocation中維護了攔截器呼叫的計數器,保證攔截器的逐個呼叫,完成所有攔截器呼叫之後會反射呼叫目標方法。
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);
}
}
}
上面的原始碼並非發現織入的痕跡,讓我們接著看看ReflectiveMethodInvocation類的proceed方法:
原始碼26-來自:ReflectiveMethodInvocation
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 完成所有增強之後執行目標切點方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 直接呼叫目標方法
return invokeJoinpoint();
}
// 獲取下一個要執行的攔截器
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.
// 普通的攔截器,直接呼叫即可
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
攔截器的呼叫執行invoke方法,並將this(當前例項)作為引數,來保證呼叫鏈的順利執行,具體的邏輯那就在每個攔截器的invoke方法之中了,執行完攔截器的邏輯之後,就可以執行目標方法的邏輯了。
這正是織入的實現。
我們從織入的邏輯中並未發現有對攔截器執行順序進行控制的邏輯,那麼那些前置、後置、環繞、異常等的執行位置是怎麼實現的呢?
3、織入實現原理
雖然在ReflectiveMethodInvocation的proceed方法中看到目標方法是最後才被執行,那麼那些後置、環繞、異常的通知是怎麼實現的呢,如果我們開啟各種通知實現的invoke方法中,就會發現一些東西:
我們檢視五種通知後發現
原始碼27-來自:AspectJAfterReturningAdvice、AspectJAfterAdvice、AspectJAroundAdvice、AspectJMethodBeforeAdvice、AspectJAfterThrowingAdvice
// AspectJAfterReturningAdvice:後置通知
@Override
public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
if (shouldInvokeOnReturnValueOf(method, returnValue)) {
invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
}
}
// AspectJAfterAdvice:後置終點通知
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
// AspectJAroundAdvice:環繞通知
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
return invokeAdviceMethod(pjp, jpm, null, null);
}
// AspectJMethodBeforeAdvice:前置通知
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
// AspectJAfterThrowingAdvice:異常通知
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
我們發現這五個通知裡面的invoke方法中都呼叫了invokeAdviceMethod方法,這個方法是在AbstractAspectJAdvice抽象類中定義的。
原始碼28-來自:AbstractAspectJAdvice
protected Object invokeAdviceMethod(
@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
throws Throwable {
// 執行引數繫結,然後使用引數呼叫通知方法
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
此處有三個方法呼叫:
getJoinPoint方法,使用者獲取當前的連線點例項
argBinging方法,用於進行引數繫結操作
invokeAdviceMethodWithGivenArgs方法執行通知方法
首先看看getJointPoint方法:
原始碼29-來自:AbstractAspectJAdvice
protected JoinPoint getJoinPoint() {
// 獲取當前的連線點例項
return currentJoinPoint();
}
public static JoinPoint currentJoinPoint() {
// 首先嚐試從ExposeInvocationInterceptor攔截器中獲取當前的方法呼叫
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
// 然後從方法呼叫之中獲取連線點例項jp
JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
if (jp == null) {
// 如果未獲取到連線點例項,並設定到方法呼叫之中
jp = new MethodInvocationProceedingJoinPoint(pmi);
pmi.setUserAttribute(JOIN_POINT_KEY, jp);
}
return jp;
}
上面邏輯很簡單,就是從方法呼叫上下文中獲取連線點,並返回,需要注意的是,此處一般情況是需要走if(jp == null)中的邏輯的,意思就是這裡一般會是首次為方法呼叫設定連線點的地方,這也正是懶例項化的實現-在真正需要使用的時候才進行建立。
下面看看原始碼28中第二個方法:引數繫結
原始碼30-來自:AbstractAspectJAdvice
protected Object[] argBinding(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
@Nullable Object returnValue, @Nullable Throwable ex) {
// 提前估測引數繫結
// 最後就是將所有通知中候選的引數的名稱和型別儲存到了切點對應屬性之中備用
calculateArgumentBindings();
// AMC start
Object[] adviceInvocationArgs = new Object[this.parameterTypes.length];
int numBound = 0;
if (this.joinPointArgumentIndex != -1) {
adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
numBound++;
}
else if (this.joinPointStaticPartArgumentIndex != -1) {
adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
numBound++;
}
if (!CollectionUtils.isEmpty(this.argumentBindings)) {
// binding from pointcut match
// 1-通過切點匹配進行引數繫結
if (jpMatch != null) {
PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();
for (PointcutParameter parameter : parameterBindings) {
String name = parameter.getName();
Integer index = this.argumentBindings.get(name);
adviceInvocationArgs[index] = parameter.getBinding();
numBound++;
}
}
// binding from returning clause
// 2-通過返回名稱進行引數繫結
if (this.returningName != null) {
Integer index = this.argumentBindings.get(this.returningName);
adviceInvocationArgs[index] = returnValue;
numBound++;
}
// binding from thrown exception
// 3-通過異常返回進行引數繫結
if (this.throwingName != null) {
Integer index = this.argumentBindings.get(this.throwingName);
adviceInvocationArgs[index] = ex;
numBound++;
}
}
if (numBound != this.parameterTypes.length) {
throw new IllegalStateException("Required to bind " + this.parameterTypes.length +
" arguments, but only bound " + numBound + " (JoinPointMatch " +
(jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)");
}
// 這裡會將通知方法中的所有引數進行繫結,因為他們都是候選者,除了一些特殊的不需繫結的之外(只三種切點型別)
return adviceInvocationArgs;
}
引數繫結的重點在於第一步,估測引數繫結,這一步會將所有通知中候選的引數的名稱和型別儲存到了切點對應屬性之中備用,我們來看看:
原始碼31-來自AbstractAspectJAdvice
public final synchronized void calculateArgumentBindings() {
// The simple case... nothing to bind.
if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
// 無參可綁的情況,直接返回
return;
}
// 獲取引數型別的數量numUnboundArgs(未綁引數數量)
int numUnboundArgs = this.parameterTypes.length;
Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
// 排除parameterTypes中JoinPoint型別、ProceedingJoinPoint型別、JoinPoint.StaticPart型別的引數
// JoinPoint型別的引數可作為非環繞通知的首個引數
// ProceedingJoinPoint型別的引數可作為環繞通知的首個引數
// JoinPoint.StaticPart型別的引數也可以作為某些通知的首個引數
// 謹記,以上三種類型的引數只能作為對應通知的首個引數,當然也可以和其他引數共存,但位置必須位於首個,原因也很簡單,因為此處判斷的時候完全就是在拿首個引數型別來完成的。
// 這三個引數是可以直接使用的,無需進行引數繫結操作,所以在這裡排除掉
if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0]) ||
maybeBindJoinPointStaticPart(parameterTypes[0])) {
// 上述三種引數不需要繫結
numUnboundArgs--;
}
if (numUnboundArgs > 0) {
// need to bind arguments by name as returned from the pointcut match
// 排除以上型別之後,如果還有剩餘,則需要根據從切入點匹配返回的名稱繫結引數
// 我們要明白:切點表示式完全就是用來匹配用的,哪怕其中有引數,也是為了匹配指定引數用的,他不帶有任何傳參功能,傳參功能只有通知方法才有
// 所以這裡的剩餘引數個數,其實就是通知方法剩餘引數,這裡是依據引數名稱來進行引數繫結
bindArgumentsByName(numUnboundArgs);
}
this.argumentsIntrospected = true;
}
這個方法中主要就是排除了三大類位於首位的切點引數型別,這三型別引數不需要進行繫結。然後對剩餘的引數進行繫結操作:
原始碼32-來自AbstractAspectJAdvice
// 通過name來繫結引數
private void bindArgumentsByName(int numArgumentsExpectingToBind) {
if (this.argumentNames == null) {
// 建立引數名稱發現器,並獲取指定通知方法的引數名稱
this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod);
}
if (this.argumentNames != null) {
// We have been able to determine the arg names.
// 只要確認通知使用有引數的就行
bindExplicitArguments(numArgumentsExpectingToBind);
}
else {
throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " +
"requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +
"the argument names were not specified and could not be discovered.");
}
}
這一步,我們需要先建立引數名稱發現器,然後發現器來獲取當前通知方法的引數名的陣列argumentNames。
這個陣列的作用僅僅是用來判空,只要其有值,即通知方法有引數,那麼就需要執行繫結操作。
首先來看看建立發現器的原始碼:
原始碼33-來自AbstractAspectJAdvice
protected ParameterNameDiscoverer createParameterNameDiscoverer() {
// We need to discover them, or if that fails, guess,
// and if we can't guess with 100% accuracy, fail.
// DefaultParameterNameDiscoverer是引數名稱發現器的預設實現,他其實是一個
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
AspectJAdviceParameterNameDiscoverer adviceParameterNameDiscoverer =
new AspectJAdviceParameterNameDiscoverer(this.pointcut.getExpression());// 切點的表示式
// 如果返回通知後繫結返回值,則returningName為非null
adviceParameterNameDiscoverer.setReturningName(this.returningName);
// 如果在丟擲通知後綁定了丟擲的值,則throwingName為非null
adviceParameterNameDiscoverer.setThrowingName(this.throwingName);
// Last in chain, so if we're called and we fail, that's bad...
// 設定在未能推匯出通知引數名稱的情況下是否丟擲IllegalArgumentException和AmbiguousBindingException異常
adviceParameterNameDiscoverer.setRaiseExceptions(true);
// 將配置好的發現器新增到DefaultParameterNameDiscoverer中並返回
discoverer.addDiscoverer(adviceParameterNameDiscoverer);
return discoverer;
}
原始碼中AspectJAdviceParameterNameDiscoverer為真正執行發現操作的發現器。
原始碼34-來自AspectJAdviceParameterNameDiscoverer
@Override
@Nullable
public String[] getParameterNames(Method method) {
// 引數型別
this.argumentTypes = method.getParameterTypes();
// 初始化未繫結引數個數
this.numberOfRemainingUnboundArguments = this.argumentTypes.leng