Spring 源碼解析

文章目錄

  • 前言
  • 相關Spring的定義接口
  • 整體代碼
  • StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh")
  • prepareRefresh()
  • obtainFreshBeanFactory()
  • registerBeanPostProcessors(beanFactory)
  • SpringAOP原碼流程
    • @EnableAspectJAutoProxy
    • AnnotationAwareAspectJAutoProxyCreator的創建時機
    • AnnotationAwareAspectJAutoProxyCreator的執行時機
    • 被代理方法的執行流程

前言

Spring 由17個方法構成,本文一以GenericApplicationContext為例

相關Spring的定義接口

  • ImportBeanDefinitionRegistrar
    可以向容器注冊自定義的RootBeanDefinition
  • BeanDefinitionRegistryPostProcessor
  • BeanFactoryPostProcessor
    在BeanDefinitionRegistryPostProcessor方法執行后執行
  • BeanPostProcessor
  • InstantiationAwareBeanPostProcessor
  • SmartInstantiationAwareBeanPostProcessor

整體代碼

public void refresh() throws BeansException, IllegalStateException {synchronized(this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");this.prepareRefresh();ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);try {this.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);beanPostProcess.end();this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();} catch (BeansException var10) {if (this.logger.isWarnEnabled()) {this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);}this.destroyBeans();this.cancelRefresh(var10);throw var10;} finally {this.resetCommonCaches();contextRefresh.end();}}}

StartupStep contextRefresh = this.applicationStartup.start(“spring.context.refresh”)

this.applicationStartup初始化 :

private ApplicationStartup applicationStartup;

ApplicationStartup.DEFAULT :

ApplicationStartup DEFAULT = new DefaultApplicationStartup();

在這里插入圖片描述

prepareRefresh()

protected void prepareRefresh() {this.startupDate = System.currentTimeMillis();this.closed.set(false);//設置關閉狀態為真this.active.set(true);//設置存活狀態為真if (this.logger.isDebugEnabled()) {if (this.logger.isTraceEnabled()) {this.logger.trace("Refreshing " + this);} else {this.logger.debug("Refreshing " + this.getDisplayName());}}this.initPropertySources();  //自身空實現,留給子類重寫this.getEnvironment().validateRequiredProperties();if (this.earlyApplicationListeners == null) {this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners);//創建早期容器監聽者} else {this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);//創建容器監聽者}this.earlyApplicationEvents = new LinkedHashSet();//創建早期事件存放容器
}

this.getEnvironment().validateRequiredProperties() 整體解析:

獲取環境變量及系統變量

this.getEnvironment().validateRequiredProperties() 中 getEnvironment() :

  • getEnvironment()
public ConfigurableEnvironment getEnvironment() {if (this.environment == null) {this.environment = this.createEnvironment();}return this.environment;
}
  • createEnvironment() :
return new StandardEnvironment();
public class StandardEnvironment extends AbstractEnvironment {public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";public StandardEnvironment() {   //空構造調用父類構造,由父類調用customizePropertySources方法}protected StandardEnvironment(MutablePropertySources propertySources) {super(propertySources);}protected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new PropertiesPropertySource("systemProperties", this.getSystemProperties()));//獲取系統的參數變量propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));//獲取系統環境變量}
}
  • AbstractEnvironment 構造器 :
    public AbstractEnvironment() {this(new MutablePropertySources()); //調用下面構造方法}protected AbstractEnvironment(MutablePropertySources propertySources) {this.logger = LogFactory.getLog(this.getClass());this.activeProfiles = new LinkedHashSet();this.defaultProfiles = new LinkedHashSet(this.getReservedDefaultProfiles());this.propertySources = propertySources; //將propertySources賦值給本地propertySources變量this.propertyResolver = this.createPropertyResolver(propertySources);this.customizePropertySources(propertySources);  //調用子類StandardEnvironment的customizePropertySources}

總之是new 一個 StandardEnvironment 類然后調用其的customizePropertySources方法返回該 StandardEnvironment

this.getEnvironment().validateRequiredProperties() 中的validateRequiredProperties():

調用的是之前返回的 StandardEnvironment的父類AbstractEnvironment propertyResolver屬性的validateRequiredProperties的方法 :

propertyResolver.validateRequiredProperties();

propertyResolver屬性是在上面創建StandardEnvironment時候調用其父類AbstractEnvironment(MutablePropertySources propertySources)構造中創建賦值的 :

this.propertyResolver = this.createPropertyResolver(propertySources);

createPropertyResolver(propertySources)則只是調用一個new PropertySourcesPropertyResolver(propertySources);

validateRequiredProperties()方法最終調用的是PropertySourcesPropertyResolver的validateRequiredProperties方法

而PropertySourcesPropertyResolver 的validateRequiredProperties方法本身沒有實現是繼承父類的 AbstractPropertyResolver實現

最終this.getEnvironment().validateRequiredProperties()實現方法是AbstractPropertyResolver.validateRequiredProperties()

public void validateRequiredProperties() {MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();Iterator var2 = this.requiredProperties.iterator();while(var2.hasNext()) {String key = (String)var2.next();if (this.getProperty(key) == null) {ex.addMissingRequiredProperty(key);}}if (!ex.getMissingRequiredProperties().isEmpty()) {throw ex;}
}

requiredProperties中遍歷值并且判斷是否在系統或環境變量存在該值

requiredProperties:
AbstractPropertyResolver的一個屬性

private final Set<String> requiredProperties = new LinkedHashSet();

總結作用
Spring容器初始化的時候,會從集合requiredProperties中取出所有key,然后獲取這些key的環境變量(包括系統環境變量和進程環境變量),如果有一個key對應的環境變量為空,就會拋出異常,導致spring容器初始化失敗;

實戰中使用 :

看了AbstractPropertyResolver類的validateRequiredProperties方法源碼后,可以確定該方法能強制要求一些環境變量必須存在,否則停止spring啟動,我們只要把我們認為必要的環境變量的key存入集合requiredProperties中即可,達到此目標需要解決下面兩個問題:

  1. 如何將環境變量的key存入集合requiredProperties?
    調用AbstractPropertyResolver類的setRequiredProperties方法,注意該方法是向集合requiredProperties中添加數據,并不會將已有數據清除;
  2. 在什么時候執行AbstractPropertyResolver類的setRequiredProperties方法設置key?
    創建一個AbstractApplicationContext的子類,重寫initPropertySources方法,在此方法中執行AbstractPropertyResolver類的setRequiredProperties;

創建一個自定義ApplicationContext 并重寫initPropertySources方法:

    public void main() {CustomApplicationContext customApplicationContext = new CustomApplicationContext();customApplicationContext.refresh();}public  class CustomApplicationContext extends GenericApplicationContext {@Overrideprotected void initPropertySources() {super.initPropertySources();//把"MYSQL_HOST"作為啟動的時候必須驗證的環境變量getEnvironment().setRequiredProperties("MYSQL_HOST");}}

上面寫過initPropertySources實在 prepareRefresh() 時候調用 :

obtainFreshBeanFactory()

獲取BeanFactory并刷新

 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {this.refreshBeanFactory();return this.getBeanFactory();
}
  • refreshBeanFactory()內容 :
 if (!this.refreshed.compareAndSet(false, true)) {throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");} else {this.beanFactory.setSerializationId(this.getId());}
  • getBeanFactory()

返回 GenericApplicationContext 中的 DefaultListableBeanFactory beanFactory 屬性

beanFactory 在 GenericApplicationContext 創建的時候創建的 :

public GenericApplicationContext() {this.customClassLoader = false;this.refreshed = new AtomicBoolean();this.beanFactory = new DefaultListableBeanFactory();
}

registerBeanPostProcessors(beanFactory)

SpringAOP原碼流程

@EnableAspectJAutoProxy

首先需了解@EnableAspectJAutoProxy

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {boolean proxyTargetClass() default false;}

它通過@Import(AspectJAutoProxyRegistrar.class)導入了一個AspectJAutoProxyRegistrar組件:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);AnnotationAttributes enableAJAutoProxy \=attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}}}

它實現了ImportBeanDefinitionRegistrar接口,這個接口可以向IOC容器中注冊bean。 由此可以推測aop利用AspectJAutoProxyRegistrar自定義給容器中注冊bean;BeanDefinetion

進入registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,發現它給容器中注冊了一個名為org.springframework.aop.config.internalAutoProxyCreator,實例為AnnotationAwareAspectJAutoProxyCreator的類。

AnnotationAwareAspectJAutoProxyCreator的創建時機

查看AnnotationAwareAspectJAutoProxyCreator的繼承關系圖

在這里插入圖片描述
在此需要關注兩點內容:

  • 關注后置處理器SmartInstantiationAwareBeanPostProcessor(在bean初始化完成前后做事情)
  • 關注自動裝配BeanFactory。

注意 :SmartInstantiationAwareBeanPostProcessorBeanPostProcessor的一個子接口

因此AnnotationAwareAspectJAutoProxyCreator的創建時機:

1)、傳入配置類,創建ioc容器2)、注冊配置類,調用refresh()刷新容器;3)、registerBeanPostProcessors(beanFactory);注冊bean的后置處理器來方便攔截bean的創建;1)、先獲取ioc容器已經定義了的需要創建對象的所有BeanPostProcessor2)、給容器中加別的BeanPostProcessor3)、優先注冊實現了PriorityOrdered接口的BeanPostProcessor4)、再給容器中注冊實現了Ordered接口的BeanPostProcessor5)、注冊沒實現優先級接口的BeanPostProcessor6)、注冊BeanPostProcessor,實際上就是創建BeanPostProcessor對象,保存在容器中;創建internalAutoProxyCreator的BeanPostProcessorAnnotationAwareAspectJAutoProxyCreator1)、創建Bean的實例2)、populateBean;給bean的各種屬性賦值3)、initializeBean:初始化bean;1)、invokeAwareMethods():處理Aware接口的方法回調2)、applyBeanPostProcessorsBeforeInitialization():應用后置處理器的postProcessBeforeInitialization()3)、invokeInitMethods();執行自定義的初始化方法4)、applyBeanPostProcessorsAfterInitialization();執行后置處理器的postProcessAfterInitialization();4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)創建成功;--》aspectJAdvisorsBuilder7)、把BeanPostProcessor注冊到BeanFactory中;beanFactory.addBeanPostProcessor(postProcessor);  

在上面步驟獲取ioc容器已經定義了的需要創建對象的所有BeanPostProcessor中,因為AnnotationAwareAspectJAutoProxyCreator實現了SmartInstantiationAwareBeanPostProcessor接口,而SmartInstantiationAwareBeanPostProcessor是BeanPostProcessor的子接口,并且@EnableAspectJAutoProxy注冊了AnnotationAwareAspectJAutoProxyCreator的BeanDefinetion進容器,所以此步驟能獲取到AnnotationAwareAspectJAutoProxyCreator的BeanDefinetion

AnnotationAwareAspectJAutoProxyCreator的執行時機

AnnotationAwareAspectJAutoProxyCreator是一個后置處理器,可以猜測它在其他bean的初始化前后進行了特殊處理。我在它父類的postProcessBeforeInstantiation方法進行了斷點調試,其方法調用棧如下:

在這里插入圖片描述

finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;創建剩下的單實例bean1)、遍歷獲取容器中所有的Bean,依次創建對象getBean(beanName);getBean->doGetBean()->getSingleton()->createBean()2)、createBean()創建bean【AnnotationAwareAspectJAutoProxyCreator在所有bean創建之前會有一個攔截,InstantiationAwareBeanPostProcessor,  會調用postProcessBeforeInstantiation()1)、先從緩存中獲取當前bean,如果能獲取到,說明bean是之前被創建過的,直接使用,否則再創建;只要創建好的Bean都會被緩存起來2)、createBean();創建bean;AnnotationAwareAspectJAutoProxyCreator 會在任何bean創建之前先嘗試返回bean的實例【BeanPostProcessor是在Bean對象創建完成初始化前后調用的】【InstantiationAwareBeanPostProcessor是在創建Bean實例之前先嘗試用后置處理器返回對象的】1)resolveBeanClass()加載bean的class文件2)、resolveBeforeInstantiation();解析BeforeInstantiation希望后置處理器在此能返回一個代理對象;如果能返回代理對象就使用,如果不能就繼續創建1)、后置處理器先嘗試返回對象;InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation如果返回對象就再繼續執行BeanPostProcessor#postProcessAfterInitialization,返回bean3)、doCreateBean(beanName, mbdToUse, args);真正的去創建一個bean實例1)、createBeanInstance();實例化beanpopulateBean();屬性注入initializeBean();初始化,執行各種生命周期方法和創建代理等

在步驟resolveBeforeInstantiation(),一般自定義的bean在此都不會獲取到實例,此步驟有印象即可。

再深入解析initializeBean()方法 :

initializeBean()
如果bean實現了相關的接口,就調用執行相關接口1)、invokeAwareMethods;執行BeanNameAware BeanClassLoaderAware BeanFactoryAware接口2)、applyBeanPostProcessorsBeforeInitialization;執行BeanPostProcessor#postProcessBeforeInitialization3)、invokeInitMethods; 執行初始化方法4)、applyBeanPostProcessorsAfterInitialization;BeanPostProcessor#postProcessAfterInitialization;創建代理

這里重點關注AnnotationAwareAspectJAutoProxyCreator類,其postProcessAfterInitialization方法在其父類AbstractAutoProxyCreator實現。
直接進入AbstractAutoProxyCreator#postProcessAfterInitialization,代理創建入口就在此:

	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);// earlyProxyReferences是一個ConcurrentHashMap,里面存放的是已經生成的代理// 里面存放的是已經生成的代理(三級緩存可能會提前生成代理),如果有不會重復生成。if (this.earlyProxyReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}

進入wrapIfNecessary,這里是創建代理的關鍵方法:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}// advicedBeans表示已經判斷過的bean,false表示此Bean不需要進行AOP,直接返回if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}// 當前正在創建的Bean不用進行AOP,比如切面Bean/*如果是基礎設施類(Pointcut、Advice、Advisor 等接口的實現類),或是應該跳過的類,則不應該生成代理,此時直接返回 bean,也就是代理不能給自己再加代理 就是不套娃AspectJAwareAdvisorAutoProxyCreator#shouldSkip*/if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.// 查詢當前bean匹配的Advice,找切面的過程(判斷當前實例化的bean是否有切面,如果有則將切面返回)返回類型為 Advisor// postProcessBeforeInstantiation方法也會調用 ;AbstractAdvisorAutoProxyCreator實現Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);// 如果有切面,則生成bean的代理if (specificInterceptors != DO_NOT_PROXY) {// advisedBeans記錄了某個Bean已經進行過AOP了this.advisedBeans.put(cacheKey, Boolean.TRUE);// 創建代理,把被代理對象bean的實例封裝到SingletonTargetSource中,生成當前實例化bean的代理對象// 傳入的bean是被代理實例,SingletonTargetSource持有了被代理實例的引用(一級緩存單例池中存的就是代理對象)Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());// 將代理對象返回return proxy;}// 沒有代理的情況,就是falsethis.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;
}

第一次進入shouldSkip方法會查詢Spring容器內的所有@Aspecj相關的類,并將其相關方法注冊成Advisor,并緩存起來,以便下次直接獲取。

總結 :

通過@EnableAspectJAutoProxy注入AnnotationAwareAspectJAutoProxyCreator,其實現了BeanFactoryAwareSmartInstantiationAwareBeanPostProcessor接口,間接實現了InstantiationAwareBeanPostProcessorBeanPostProcessor

在Spring容器刷新時候調用registerBeanPostProcessors方法注冊進容器;

在Spring創建完成Bean,執行生命周期后置處理器BeanPostProcessor#postProcessAfterInitialization時候,會調進入AnnotationAwareAspectJAutoProxyCreator執行代理創建邏輯,并返回需要的代理。


繼續深入研究wrapIfNecessary

首先判斷當前類是否需要代理,或者已經被代理過,返回原類。

重點關注getAdvicesAndAdvisorsForBean方法,實現在AbstractAdvisorAutoProxyCreator類:

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {// 找到候選的切面,就是尋找帶有@Aspect注解的過程,把帶有@Aspect的類封裝成Advisor返回List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {return DO_NOT_PROXY;}return advisors.toArray();
}

findEligibleAdvisors方法尋找當前類匹配的Advisor,進入findEligibleAdvisors方法 :

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 找到候選的切面,就是尋找帶有@Aspect注解的過程,把帶有@Aspect的類封裝成Advisor返回List<Advisor> candidateAdvisors = findCandidateAdvisors(); //如果是通過@Aspect注解開啟切面,子類AnnotationAwareAspectJAutoProxyCreator方法// 判斷候選的切面是否作用在當前beanClass上面,就是一個匹配的過程List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);// 針對有@Aspect注解的類添加一個默認的切面--->DefaultPointcutAdvisor,解決參數傳遞問題// 進入AspectJAwareAdvisorAutoProxyCreator類的extendAdvisors()方法extendAdvisors(eligibleAdvisors); // AspectJAwareAdvisorAutoProxyCreatorif (!eligibleAdvisors.isEmpty()) {// 對有@Order、@Priority注解的類進行排序eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;
}

進入AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors:

	protected List<Advisor> findCandidateAdvisors() {// Add all the Spring advisors found according to superclass rules.// 先找到所有Advisor類型的Bean,調用父類resolveBeforeInstantiation方法進入的話實現類是AbstractAdvisorAutoProxyCreator#findCandidateAdvisorsList<Advisor> advisors = super.findCandidateAdvisors();// Build Advisors for all AspectJ aspects in the bean factory.// 再從所有@Aspect中解析得到Advisor對象if (this.aspectJAdvisorsBuilder != null) {// 創建候選的切面,對有@Aspect注解的類進行處理(包裝Advice和Pointcut,還有切面的排序)****advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());// 核心方法-構建Advisor}return advisors;}

注意尋找的方法是this.aspectJAdvisorsBuilder.buildAspectJAdvisors(),此方法就是從Spring容器中查詢所有被@Aspect標注的方法,并轉換成Advisor

//GSCM  2024/1/23  解析容器中@Aspect注解標注的類構建成Advisor對象public List<Advisor> buildAspectJAdvisors() {List<String> aspectNames = this.aspectBeanNames;if (aspectNames == null) {synchronized (this) {aspectNames = this.aspectBeanNames;if (aspectNames == null) {List<Advisor> advisors = new ArrayList<>();aspectNames = new ArrayList<>();// 獲取Spring容器中所有實例的beanNameString[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);for (String beanName : beanNames) {if (!isEligibleBean(beanName)) {continue;}// We must be careful not to instantiate beans eagerly as in this case they// would be cached by the Spring container but would not have been weaved.// 首先拿到實例的類型Class<?> beanType = this.beanFactory.getType(beanName, false);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) {// 創建獲取@Aspect注解類的實例工廠,MetadataAwareAspectInstanceFactory負責獲取有@Aspect注解的實例MetadataAwareAspectInstanceFactory factory =new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);// 創建切面Advisor對象List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);// 創建切面Advisor對象// 放到緩存中if (this.beanFactory.isSingleton(beanName)) {this.advisorsCache.put(beanName, classAdvisors);}else {this.aspectFactoryCache.put(beanName, factory);}advisors.addAll(classAdvisors);}else {// Per target or per this.if (this.beanFactory.isSingleton(beanName)) {throw new IllegalArgumentException("Bean with name '" + beanName +"' is a singleton, but aspect instantiation model is not singleton");}MetadataAwareAspectInstanceFactory factory =new PrototypeAspectInstanceFactory(this.beanFactory, beanName);this.aspectFactoryCache.put(beanName, factory);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}}this.aspectBeanNames = aspectNames;return advisors;}}}if (aspectNames.isEmpty()) {return Collections.emptyList();}List<Advisor> advisors = new ArrayList<>();for (String aspectName : aspectNames) {List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);// 解析過一次后就從緩存中取if (cachedAdvisors != null) {advisors.addAll(cachedAdvisors);}else {MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}return advisors;}

在此行List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);代碼中,最終將@Aspect注解的類解析成為Advisor,返回的Advisor類型同一都為InstantiationModelAwarePointcutAdvisorImpl

進入this.advisorFactory.getAdvisors(factory)方法:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {// 從工廠中獲取有@Aspect注解的類的反射對象ClassClass<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();//GXXX  2023/11/21// 從工廠中獲取有@Aspect注解的類的名稱String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();validate(aspectClass);// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator// so that it will only instantiate once.// 創建工廠的裝飾類,獲取實例(只會獲取一次)MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);List<Advisor> advisors = new ArrayList<>();// 這里循環沒有@Pointcut注解的方法,掃描有@Before、@Around、@After注解的方法,并進行排序,getAdvisorMethods()->for (Method method : getAdvisorMethods(aspectClass)) { // 進入本類的getAdvisorMethods()方法// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).// 獲取Advisor*****(只要是有@Before、@After等注解的方法,它們分別和帶有@Pointcut注解的方法組合,生成各自Advisor類)// 封裝Advisor,返回的實現類為InstantiationModelAwarePointcutAdvisorImplAdvisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);// 再進入本類的getAdvisor(),根據方法生成Advisorif (advisor != null) {advisors.add(advisor);}}// If it's 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.for (Field field : aspectClass.getDeclaredFields()) {Advisor advisor = getDeclareParentsAdvisor(field);if (advisor != null) {advisors.add(advisor);}}return advisors;}

這里遍歷當前class的每個方法,為每個通知方法創建Advisor實例。

進入getAdvisor方法 :

	public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());// 獲取AspectJExpressionPointcut對象,從注解中獲取表達式// candidateAdviceMethod就是有@Before、@After注解的方法AspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());if (expressionPointcut == null) {return null;}// 創建真正的Advisor切面類,里面有pointcut和advice***進入InstantiationModelAwarePointcutAdvisorImpl的構造方法// expressionPointcut是pointcut,candidateAdviceMethod是advicereturn new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);//  創建真正的切面類}

在此針對每個目標方法創建對應的Advisor實例,具體創建邏輯在InstantiationModelAwarePointcutAdvisorImpl的構造方法中:

this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);//GSCM  2024/2/28 創建當前方法對應advice實例的具體方法

此方法繼續調用this.aspectJAdvisorFactory.getAdvice(),直接看aspectJAdvisorFactory.getAdvice():

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {// 獲取有@Aspect注解的類,就是當前被@Aspect標注的classClass<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();validate(candidateAspectClass);// 找到class上面的相關切面注解,并且包裝成AspectJAnnotation對象AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {return null;}.........AbstractAspectJAdvice springAdvice;// 根據方法上切面不同注解,創建相對應的的advice實例switch (aspectJAnnotation.getAnnotationType()) {case AtPointcut:if (logger.isDebugEnabled()) {logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");}return null;case AtAround:// 環繞通知,實現了MethodInterceptor接口springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtBefore:// 前置通知,實現了MethodBeforeAdvice接口,沒有實現MethodInterceptor接口springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfter:// 后置通知,實現了MethodInterceptor接口springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfterReturning:// 結果通知,實現了AfterReturningAdvice接口,沒有實現MethodInterceptor接口springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterReturningAnnotation.returning())) {springAdvice.setReturningName(afterReturningAnnotation.returning());}break;case AtAfterThrowing:// 異常通知,實現了MethodInterceptor接口springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {springAdvice.setThrowingName(afterThrowingAnnotation.throwing());}break;default:throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);}// Now to configure the advice...springAdvice.setAspectName(aspectName);springAdvice.setDeclarationOrder(declarationOrder);String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);if (argNames != null) {springAdvice.setArgumentNamesFromStringArray(argNames);}springAdvice.calculateArgumentBindings();return springAdvice;}

這里就是創建了目標方法對應具體的advice并賦值給InstantiationModelAwarePointcutAdvisorImpl的instantiatedAdvice屬性

被代理方法的執行流程

被Spring的Cglib代理的所有方法都會進入CglibAopProxy.DynamicAdvisedInterceptor#intercept方法;

//GSCM  2024/1/24 被cglib代理的對象調用首先進入此方法
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Object target = null;TargetSource targetSource = this.advised.getTargetSource();try {if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);// ※獲取調用執行鏈※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.if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {// 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 = invokeMethod(target, method, argsToUse, methodProxy);}else {// We need to create a method invocation...// 執行代理方法調用鏈條retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}
}

DynamicAdvisedInterceptor有一個重要的屬性private final AdvisedSupport advised,在創建代理的時候被賦值,里面包含被代理的原生類引用及執行鏈;

在這里插入圖片描述

轉換前

在這里插入圖片描述
轉換后
在這里插入圖片描述

CglibMethodInvocation
ExposeInvocationInterceptor
AspectJAroundAdvice#invoke->調用切面層aspectJAdviceMethod.invoke
MethodBeforeAdviceInterceptor#invoke
AspectJAfterAdvice#invoke
AspectJAfterThrowingAdvice#invoke


AspectJAroundAdvice
MethodBeforeAdviceInterceptor
AfterReturningAdviceAdapter
ThrowsAdviceAdapter
AspectJAfterAdvice

AspectJMethodBeforeAdvice -> MethodBeforeAdviceInterceptor

advisor、advice、adviced各自的含義

  • advisor 通知器包含advice和pointcut

  • advice 具體的某一個消息通知

  • adviced 用來配置代理(proxyFactory)

執行流程整理 :

postProcessAfterInitialization創建代理,第一次進來會遍歷Spring容器內所有的帶有@Aspect注解的類,遍歷其類中的每個帶有@Before、@Around、@After的方法,并將切點對象解析出來與當前方法的Method對象,封裝成一個InstantiationModelAwarePointcutAdvisorImpl實例,在InstantiationModelAwarePointcutAdvisorImpl中構造函數中會根據Method的信息創建一個Advice對象并引用。遍歷完成將這些類緩存起來,并且生成一個bean名稱對應Advice的緩存,下次直接從緩存中。拿到Advice后默認添加一個ExposeInvocationInterceptor在最前面。再創建一個代理工廠ProxyFactory區創建代理,代理工廠選擇Cglib還是Jdk去創建代理類,代理類保存著目標類的引用及Advisor調用鏈。

Cglib代理進入DynamicAdvisedInterceptor#intercept,再根據Advisor獲取調用鏈進行包裝(如果需要的話),包裝成MethodInterceptor。創建一個ReflectiveMethodInvocation類調用proceed,里面保存著一個調用次數currentInterceptorIndex,按照順序一個個調用Advisor調用鏈,并且將當前類作為參數一直傳遞下去,每次調用一個Advisor會將currentInterceptorIndex減一,直到與Advisor調用鏈數量一致,說明調用鏈已經執行完了。
再調用真實的目標對象方法,調用完成依次彈出棧。

調用棧示例,org.gabriel.springwork.boot是自定義包 :

 at org.gabriel.springwork.imported.ImportController.isSuccess(ImportController.java:16)at org.gabriel.springwork.imported.ImportController$$FastClassBySpringCGLIB$$7fcf3b6d.invoke(<generated>:-1)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)at org.gabriel.springwork.boot.config.DictAspect2.doAround(DictAspect2.java:35)at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:566)at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)at org.gabriel.springwork.imported.ImportController$$EnhancerBySpringCGLIB$$20b0886d.isSuccess(<generated>:-1)at org.gabriel.springwork.boot.GabrielSpringBoot.lambda$main$0(GabrielSpringBoot.java:31)at org.gabriel.springwork.boot.GabrielSpringBoot$$Lambda$606.1849602253.accept(Unknown Source:-1)at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)at org.gabriel.springwork.boot.GabrielSpringBoot.main(GabrielSpringBoot.java:30)

Advisor相關實現類解析

AOP原文

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/712377.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/712377.shtml
英文地址,請注明出處:http://en.pswp.cn/news/712377.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux調試器-gdb使用與馮諾依曼體系結構

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言 Linux調試器-gdb使用 1. 背景 2. 開始使用 馮諾依曼體系結構 總結 前言 世上有兩種耀眼的光芒&#xff0c;一種是正在升起的太陽&#xff0c;一種是正在努力學…

計算機網絡-網絡互連和互聯網(五)

1.路由器技術NAT&#xff1a; 網絡地址翻譯&#xff0c;解決IP短缺&#xff0c;路由器內部和外部地址進行轉換。靜態地址轉換&#xff1a;靜態NAT&#xff08;一對一&#xff09; 靜態NAT&#xff0c;內外一對一轉換&#xff0c;用于web服務器&#xff0c;ftp服務器等固定IP的…

(定時器/計數器)中斷系統(詳解與使用)

講解 簡介 定時器/計數器 定時器實際上也是計數器,只是計數的是固定周期的脈沖 定時和計數只是觸發來源不同(時鐘信號和外部脈沖)其他方面是一樣的。 定時器在單片機內部就像一個小鬧鐘一樣,根據時鐘的輸出信號,每隔“一秒”,計數單元的數值就增加一,當計數單元數值…

C++:String類的使用

創作不易&#xff0c;感謝三連&#xff01;&#xff01; 在C語言中&#xff0c;我們想要存儲字符串的話必須要用字符數組 char str[]"hello world"這其實是將在常量區的常量字符串拷貝到數組中&#xff0c;我們會在數組的結尾多開一個空間存儲\0&#xff0c;這樣我…

前端構建之CERT_HAS_EXPIRED和certificate has expired解決方案

問題 2024年 1 月 22 日&#xff0c;淘寶原鏡像域名&#xff08;registry.npm.taobao.org&#xff09;的 HTTPS 證書正式到期。如果想要繼續使用&#xff0c;需要將 npm 源切換到新的源&#xff08;registry.npmmirror.com&#xff09;&#xff0c;否則會報錯。 報錯信息為&a…

Consul服務注冊與發現 Consul配置步驟

Consul服務注冊與發現 Consul配置步驟 consul下載地址 Install | Consul | HashiCorp Developer 啟動需要在 下載好的文件夾里 用cmd 運行consul agent -dev啟動consul Consul配置 配置pom <!--SpringCloud consul config--> <dependency><groupId>org…

【leetcode】回文子串 動態規劃

/*** param {string} s* return {number}*/ var countSubstrings function(s) {let dpnew Array(s.length).fill().map(()>new Array(s.length).fill(false));let num0;for(let i0;i<s.length;i){for(let j0;j<i;j){//在首尾相等時&#xff0c;如果長度時1或者2&…

C++筆記(三)--- 函數重載

目錄 子類繼承父類重載 類成員函數重載 繼承和組合的三種方式請看我上一篇文章 C筆記&#xff08;二&#xff09;--- 繼承和組合-CSDN博客 子類繼承父類重載 當子類繼承父類之后&#xff0c;子類重新定義了一個和父類完全相同函數名稱的函數時&#xff0c;會將父類所有相同…

Pegasus智能家居套件樣例開發--軟定時器

樣例簡介 此樣例將演示如何在Pegasus Wi-Fi IoT智能家居套件上使用cmsis 2.0 接口進行定時器開發。 工程版本 系統版本/API版本&#xff1a;OpenHarmony 3.0 releaseIDE版本&#xff1a;DevEco Device Tool Release 3.0.0.401 快速上手 準備硬件環境 預裝windows系統的PC…

『大模型筆記』RAG應用的12種調優策略指南

RAG應用的12種調優策略指南 文章目錄 一. 概要二. 數據索引2.1. 數據清洗2.2. 分塊2.3. 嵌入模型2.4. 元數據(或未向量化的數據)2.5. 多索引2.6. 索引算法三. 推理階段(檢索和生成)3.1. 檢索參數3.2. 高級檢索策略3.3. 重新排序模型3.5. 大語言模型(LLM)

26、Qt調用.py文件中的函數

一、開發環境 Qt5.12.0 Python3.7.8 64bit 二、使用 新建一個Qt項目&#xff0c;右擊項目名稱&#xff0c;選擇“添加庫” 選擇“外部庫”&#xff0c;點擊“下一步” 點擊“瀏覽”&#xff0c;選擇Python安裝目錄下的libs文件夾中的“python37.lib”文件&#xff0c;點擊“下…

退休開便利店真的靠譜嗎?2024比較賺錢的創業項目排行

近日多個退休后開便利店賺錢的新聞登上熱搜&#xff0c;但是&#xff0c;小編對此有疑問&#xff0c;退休的老年人開便利店真的是一個好選擇嗎&#xff1f; 第一、便利店最基本的轉讓費&#xff0c;裝修費&#xff0c;進貨等等&#xff0c;這筆開支非常大&#xff0c;足以掏空老…

H5下拉刷新分頁

對于分頁需求&#xff0c;分頁數據的請求觸發十分重要&#xff0c;監聽滑動到底的觸發也有很多種。 1.IntersectionObserver監聽 IntersectionObserver 接口&#xff08;從屬于 Intersection Observer API&#xff09;提供了一種異步觀察目標元素與其祖先元素或頂級文檔視口&a…

終結數據混亂!開發者必學的GraphQL秘籍,高效API只需一步

在數字世界中&#xff0c;API就如同城市中的道路&#xff0c;連接著各種服務和數據。然而&#xff0c;傳統的API&#xff08;如RESTful&#xff09;雖然功不可沒&#xff0c;但隨著技術復雜性和需求多樣性不斷攀升&#xff0c;它們顯露出的局限性也呼喚著新的可能出現。此時&am…

Unity中,activeInHierarchy 和 activeSelf

activeInHierarchy&#xff1a; activeInHierarchy 屬性表示游戲對象是否在場景中處于激活狀態&#xff0c;并且是否在層次結構中的激活狀態。它考慮了游戲對象以及其所有父對象的激活狀態。如果 activeInHierarchy 為 true&#xff0c;表示該對象在場景中處于激活狀態且其所有…

LaMa Image Inpainting 圖像修復 Onnx Demo

目錄 介紹 效果 模型信息 項目 代碼 下載 LaMa Image Inpainting 圖像修復 Onnx Demo 介紹 gihub地址&#xff1a;https://github.com/advimman/lama &#x1f999; LaMa Image Inpainting, Resolution-robust Large Mask Inpainting with Fourier Convolutions, WAC…

《PyTorch深度學習實踐》第十三講RNN進階

一、 雙向循環神經網絡&#xff08;Bidirectional Recurrent Neural Network&#xff0c;BiRNN&#xff09;是一種常見的循環神經網絡結構。與傳統的循環神經網絡只考慮歷史時刻的信息不同&#xff0c;雙向循環神經網絡不僅考慮歷史時刻的信息&#xff0c;還考慮未來時刻的信息…

wireshark過濾和tcpdump抓包指令

Wireshark 過濾器的表達式&#xff0c;用于過濾源 IP 地址為 10.184.148.247 并且目標 TCP 端口為 1883 的數據包。啟用抓包后過濾 ip.addr 10.184.148.247 && tcp.port 1883 主機位10.184.148.19和目標端口為 8080 的操作目標 抓包前過濾 host 10.184.148.19 &…

軟件說明書怎么寫?終于有人一次性說清楚了!

每次寫軟件說明書&#xff0c;你是不是總是毫無頭緒&#xff0c;不知道從何下手&#xff1f;到各網站找資料&#xff0c;不僅格式不規范&#xff0c;甚至可能遺漏關鍵內容&#xff01;挨一頓批不說&#xff0c;還浪費大把時間。別著急&#xff0c;編寫軟件說明書&#xff0c;關…

PostgreSQL開發與實戰(2)常用命令

作者&#xff1a;太陽 1、連庫相關 #連庫 $ psql -h <hostname or ip> -p <端口> [數據庫名稱] [用戶名稱] #連庫并執行命令 $ psql -h <hostname or ip> -p <端口> -d [數據庫名稱] -U <用戶名> -c "運行一個命令;"備注&#xff1…