Spring容器:
在基於Spring的應用中,你的應用對象生存於Spring容器(container)中,Spring容器負責創建對象,裝配它們,配置它們並管理它們的整個生命周期,從生存到死亡。(在這里,可能就是從new()到finalize())。
容器是Spring框架的核心。Spring容器使用DI(依賴注入)管理構成應用的組件,它會創建相互協作的組件之間的關聯。毫無疑問,這些對象更簡單干凈,更易於理解,更易於重用並且更易於進行單元測試。
1、使用應用上下文
Spring自帶了多種類型的應用上下文:
AnnotationConfigApplicationContext: 從一個或多個基於Java的配置類中加載Spring應用上下文。
AnnotationConfigWebApplicationContext: 從一個或多個基於Java的配置類中加載Spring Web應用上下文。
ClassPathXmlApplicationContext: 從類路徑下的一個或多個XML配置文件中加載上下文定義, 把應用上下文的定義文件作為類資源。
FileSystemXmlapplicationcontext: 從文件系統下的一個或多個XML配置文件中加載上下文定義。
XmlWebApplicationContext: 從Web應用下的一個或多個XML配置文件中加載上下文定義。
無論是從哪里裝載應用上下文,將bean加載到bean工廠的過程都是類似的。
如果你想從Java配置中加載應用上下文, 那么可以使用AnnotationConfig-ApplicationContext:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContextConfig.class);
在這里沒有指定加載Spring應用上下文所需的XML文件, AnnotationConfig-ApplicationContext通過一個配置類加載bean。
應用上下文准備就緒之后, 我們就可以調用上下文的getBean()方法從Spring容器中獲取bean。
Spring注解驅動開發
使用Java配置類代替XML配置文件來裝配bean:
package com.yang.config; import com.yang.domain.Person; import com.yang.factory.StudentFactoryBean; import com.yang.service.TestService; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Scope; /** * Spring 基於Java類注解形式的配置 * * @Configuration 使Spring容器知道這是一個配置類 * @ComponentScan 啟用組件掃描,默認掃描本文件所在的包及其子包下聲明了@Component注解的類,並在Spring容器中為其創建一個bean * value屬性指定要掃描的包 * includeFilters 指定掃描的時候只需要包含哪些組件 * excludeFilters 指定掃描的時候按照什么規則排除那些組件 * 如:excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)排除Controller注解的類
* @PropertySource 使用@PropertySource讀取外部配置文件中的k/v保存到運行的環境變量Environment中;加載完外部的配置文件以后使用${key}取出配
* 置文件的值。也可以用Spring容器獲取Environment變量,然后getProperty獲取到配置的value
* ConfigurableEnvironment environment = context.getEnvironment();
* String name = environment.getProperty("name"); * */ @Import(TestService.class) @Configuration @ComponentScan(value = "com.yang")
@PropertySource(value = {"classpath:/applicationContext.properties"}) public class ApplicationContextConfig { /** * 向Spring容器中注冊一個bean,默認作用域是單例的 * 類型為方法的返回值 * bean的id為方法名,也可以使用@Bean的name屬性指定bean的名稱。
* JavaConfig如何實現注入依賴?
* 1、直接引用創建依賴bean的方法,使用的是帶參構造器。但是這種引用並不會創建多個依賴bean的對象,因為Spring將會攔截對它的調用,並確保直接返回
* 該方法所創建的bean,而不是每次都對其進行實際的調用。 * 2、將依賴的bean類型當成@Bean所在方法的一個參數。當Spring調用@Bean注解所在方法創建bean的時候,會自動裝配一個參數類型bean到方法之中,然后
* 方法體就可以按照合適的方式來使用它。這種方式不會像方式1那樣要求將依賴bean的聲明和當前bean在同一個配置類之中,甚至不要求依賴bean必須在JavaConfig
* 類中聲明。
* 疑問,若有多個同一類型的依賴bean,Spring怎么選擇裝配?
* 答案:先參數類型,后參數名稱。源碼在DefaultListableBeanFactory#doResolveDependency:1213
* * @Scope bean的作用域,取值范圍: * ConfigurableBeanFactory#SCOPE_PROTOTYPE singleton 單例,默認,Spring容器啟動就會創建對象並放在容器中,以后每次獲取都去map中取 * ConfigurableBeanFactory#SCOPE_SINGLETON prototype 原型/多例,Spring容器啟動時並不會去創建對象,每次獲取的時候都回去創建一個新的對象 * org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request 同一個請求創建一個對象 * org.springframework.web.context.WebApplicationContext#SCOPE_SESSION session 同一個session創建一個對象
問題?Spring為啥把bean默認設計成單例?
優勢:為了提高性能
1、減少創建新實例的消耗
2、減少JVM垃圾回收
3、可以快速獲取到bean
劣勢:不能做到線程安全,bean如果是有狀態的話在並發環境下線程不安全 * * @Lazy 即懶加載,針對單實例作用域的bean,默認值為true,即Spring容器啟動的時候不創建,調用的時候才去創建 * * @Conditional 按照一定的條件進行判斷,滿足條件給容器中注冊bean,若用在配置類上,則此配置類上的所有的bean都滿足條件在會去Spring容器中 * 去注冊。使用場景如只在linux系統中才創建bean * Class<? extends Condition>[] value(); 接收一個實現Condition接口的類的class對象,需要自定義類實現Condition接口在matches方法中實 * 現具體判斷邏輯,返回true代表符合條件 * */ @Lazy(value = false) @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) @Bean public Person person() { return new Person("yangyongjie"); } /** public class StudentFactoryBean implements FactoryBean<Student> { public Student getObject() throws Exception { return new Student(); } public Class<?> getObjectType() { return Student.class; } public boolean isSingleton() { return false;//false 多例 true 單例 } } 實際是將Student對象注冊到Spring容器中 */ @Bean public StudentFactoryBean studentFactoryBean(){ return new StudentFactoryBean(); }
}
向Spring容器中注冊組件的四種方式: 1、包掃描+組件標注注解(@Component、@Controller、@Service、@Repository) 2、@Bean,適合導入第三方包里面的組件 3、@Import,快速給容器中導入一個組件 1、@Import(要導入的組件的class對象),容器中就會自動注冊這個bean,名稱是全類名(com.yang.service.TestService) 2、@Import(ImportSelector.class),在ImportSelector中返回需要導入的組件的全類名數組 3、@Import(ImportBeanDefinitionRegistrar.class)手動注冊bean到容器中(在ImportBeanDefinitionRegistrar中使用BeanDefinitionRegistry的registerBeanDefinition方法手動注冊)
技巧:BeanDefinitionRegistry:接口,提供往Spring容器中注冊bean,移除bean,獲取容器中所有注冊了的bean和判斷Spring容器中是否注冊了某個bean
4、使用Spring提供的 FactoryBean(工廠Bean),與普通bean不同的是,普通bean是調用其無參構造器去創建對象然后注冊到容器中,FactoryBean是調用其 T getObject()方法,將返回的對象注冊到Spring容器中 1、默認獲取的工廠bean調用getObject創建的對象 2、注冊到容器中工廠bean的id為 “&+bean名稱”,如&studentFactoryBean
問題:BeanFactory和FactoryBean的區別
BeanFactory是IOC容器的核心接口,Spring使用BeanFactory來實例化、配置和管理bean,采用延遲加載的方式來注入bean,只有在getBean的時候才去加載創建bean
FactoryBean是創建bean的工廠,通過自定義類實現FactoryBean<T>接口,實現其getObject()方法創建bean對象,在配置文件中使用FactoryBean創建bean對象
BeanFactory和ApplicationContext的區別
ApplicationContext是完整的IOC容器,其由BeanFactory派生而來,具有BeanFactory的所有功能,可以通過配置實現,且額外提供了messageSource,資源訪問,aop和web應用
,加載多種應用上下文。並可在啟動的時候一次性創建所有需要的單實例bean
Spring容器中Bean的生命周期
bean創建--初始化--銷毀
容器管理bean的生命周期,
若是單例的bean,在Spring容器的啟動后先創建對象,再初始化,容器關閉的時候銷毀bean。
若是多實例的bean,Spring容器在每次獲取的時候才去創建對象,然后初始化,但是Spring容器關閉時並不會去銷毀bean,需要手動去銷毀。
bean初始化和銷毀四種方式:
1、指定在bean中自定義初始化和銷毀方法,通過@Bean注解指定initMethod和destroyMethod
@Bean(initMethod = "customInit",destroyMethod = "customDestory")
2、指定在bean中自定義初始化和銷毀方法,然后使用javax提供的注解
@PostConstruct標注在bean的初始化方法上,將在bean創建並屬性賦值完成,調用此方法
@PreDestroy標注在bean的銷毀方法上,將在容器銷毀對象之前調用
3、通過使Bean實現InitializingBean和DisposableBean接口,在重寫的afterPropertiesSet和destroy方法實現初始化和銷毀邏輯
4、bean的后置處理器,BeanPostProcessor接口,針對全局的bean,自定義bean實現了BeanPostProcessor接口,那么容器中其他所有bean(只針對單例的bean)在初始化方法之前之前
都會調用postProcessBeforeInitialization,在初始化方法執行之后都會調用postProcessAfterInitialization
postProcessBeforeInitialization 在初始化之前調用
postProcessAfterInitialization 在初始化之后調用
如果四種都寫了,那么執行順序為:
constructor 創建bean實例
postProcessBeforeInitialization BeanPostProcessor的初始化前置方法
PostConstruct javax注解定義的初始化方法
afterPropertiesSet 實現InitializingBean接口的初始化方法
customInit @Bean注解上自定義的初始化方法
postProcessAfterInitialization BeanPostProcessor的初始化后方法
PreDestroy javax注解定義的銷毀方法
destroy 實現DisposableBean接口的銷毀方法
customDestory @Bean注解上自定義的銷毀方法
初始化和銷毀的執行順序都是 BeanPostProcessor-->javax提供的注解-->InitializingBean/DisposableBean-->@Bean custom

BeanPostProcessor原理
populateBean(beanName, mbd, instanceWrapper);給bean進行屬性賦值
initializeBean
{
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd);執行自定義初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
遍歷得到容器中所有的BeanPostProcessor;挨個執行beforeInitialization,一但返回null,跳出for循環,不會執行后面的BeanPostProcessor.postProcessorsBeforeInitialization
}
Spring底層對BeanPostProcessor的使用:
1、其實現類ApplicationContextAwareProcessor可對實現了ApplicationContextAware接口的bean(對bean類型作了判斷,只針對部分bean)注入容器ApplicationContext依賴對象,代碼如下:
if (System.getSecurityManager() != null && (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); }
2、其實現類InitDestroyAnnotationBeanPostProcessor,對bean中標注了@PostContruct和@PreDestory注解的方法進行調用執行
3、其實現類AutowiredAnnotationBeanPostProcessor,在對象創建完成以后,對所有標注了@Autowired注解的屬性進行注入值
@Value
使用@Value注解為屬性賦值
1、可以直接寫基本數值
2、可以寫SPEL表達式#{}
3、可以寫${},取出在配置文件中配置的,容器啟動放到環境變量Environment中的值
自動裝配
創建容器中對象協作關系的行為叫做裝配,是DI的核心,即為容器中各個組件的依賴關系賦值
1、@AutoWired,自動注入
1)默認按照類型byType去容器中找對應的組件,然后注入;applicationContext.getBean(xxx.class)
2)如果找到多個相同類型的組件,再將屬性的名稱作為組件的id去容器中查找
3)@Qualifier("xxx"):執行需要裝配的組件的id,而不是使用屬性名
4)自動裝配默認一定要將屬性賦值好,沒有就會報錯,可以使用@AutoWired(required=false),允許不注入
5)@Primary:用在依賴的類上,讓Spring進行自動裝配的時候,默認首選裝配它 2、@Resource
和@AutoWired一樣實現自動裝配功能,默認是按照屬性名稱ByName進行裝配的,按名稱沒有找到則再按類型進行裝配,如果指定了name或者type則按照名稱和類型進行裝配,沒有找到則報錯
3、Inject
需要導入javax.inject的包,和Autowired的功能一樣。沒有required=false的功能
4、自定義組件想要使用Spring容器底層的一些組件(ApplicationContext,BeanFactory,xxx);
自定義組件實現xxxAware;在創建對象的時候,會調用接口規定的方法注入相關組件;Aware;
把Spring底層一些組件注入到自定義的Bean中;
xxxAware:功能使用xxxProcessor,是通過后置處理器來實現的。
ApplicationContextAware==》ApplicationContextAwareProcessor;
技巧:自定義Bean實現xxxAware接口,重寫其中的方法可為我們的自定義Bean注入Spring底層的組件xxx,原理是通過xxxProcessor,一個實現了后置處理器
BeanPostProcessor的類通過在Bean初始化前后做的事情來實現的。如下面所示的實現ApplicationContextAware 接口,為我們的Bean注入了ApplicationContext 對象
@Component public class Child implements ApplicationContextAware { private ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
@Autowired:Spring定義的; @Resource、@Inject都是java規范
AutowiredAnnotationBeanPostProcessor:解析完成自動裝配功能;
@Autowired:構造器,參數,方法,屬性;都是從容器中獲取參數組件的值
1)、[標注在方法位置]:@Bean+方法參數;參數從容器中獲取;默認不寫@Autowired效果是一樣的;都能自動裝配
2)、[標在構造器上]:如果組件只有一個有參構造器,這個有參構造器的@Autowired可以省略,參數位置的組件還是可以自動從容器中獲取
3)、放在參數位置:
高級裝配
@Profile(value = "env"):指定組件在哪個環境的情況下才能被注冊到容器中。
1、加了環境標識的bean,只有在這個環境被激活的時候才能注冊到容器中,默認激活環境是default
2、若標注在配置類上,則只有在執行的環境激活的時候,整個配置類里面的所有配置才生效
3、沒有標注環境標識@Profile注解的bean在任何環境下都會注冊的
如何激活Profile環境:
1、使用命令行動態參數:在虛擬機參數位置加載-Dspring.profiles.active=test
2、代碼的方式激活某個環境變量,需要在啟動Spring容器的時候使用無參構造不傳配置文件的參數
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 設置需要激活的環境 context.getEnvironment().setActiveProfiles("pre"); // 注冊主配置類 context.register(ApplicationContextConfig.class); // 啟動刷新容器 context.refresh();
AOP
實現一個AOP的步驟:
1、將業務邏輯組件和切面類都注入到Spring容器中,標注使Spring知道哪個是界面類,@Aspect(曾犯錯,在切面類上沒有標注@Component注解)
2、在切面類上的每一個通知方法上都標注通知注解(如@Around),並在注解中指明切入點表達式
3、開啟基於注解的aop模式,在配置類上加上注解@EnableAspectJAutoProxy
@AfterReturning注解的returning可將方法的返回值映射到參數上
@AfterReturning(value = "pointCut()", returning = "result") public void afterReturning(Object result) throws Throwable {}
@AfterThrowing(value="pointCut()",throwing="exception")的throwing將異常信息映射到參數上
獲取切入點方法名:joinPoint.getSignature().getName()
獲取方法入參:joinPoint.getArgs()
@Before("pointCut()")
// 方法若有多個參數,JoinPoint一定要放在第一位
public void before(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
}
@EnableAspectJAutoProxy注解做了什么?
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy
往容器中導入了AspectJAutoProxyRegistrar,利用AspectJAutoProxyRegistrar自定義的往容器中注冊了name為internalAutoProxyCreator,class
為AnnotationAwareAspectJAutoProxyCreator的bean。
技巧:如果看到了Enable開頭的注解,就看這個注解有沒有往容器中注冊一些組件,如果注冊組件了,這些組件的功能是什么,如果具體功能搞明白了,那么這個注解的原理就清楚了。
那么既然@EnableAspectJAutoProxy往Spring容器中注入了AnnotationAwareAspectJAutoProxyCreator這個bean(從名字看是:注解裝配切面代理創造器),
那就看一下這個bean的功能是什么,源碼走起:
class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor
其中:class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanClassLoaderAware, AopInfrastructureBean
看到其祖先類實現了InstantiationAwareBeanPostProcessor 接口,而此接口又實現了BeanPostProcessor接口,而這個接口我們知道叫做后置處理器,后置處理器
的功能就是在bean初始化的前后做一些工作。
抽象父類AbstractAutoProxyCreator 又實現了BeanFactoryAware,可以往代碼中注入BeanFactory。
那么先重點看與setBeanFactory以及與后置處理器相關的邏輯,通過源碼發現AbstractAutoProxyCreator有setBeanFactory()以及后置處理器相關的邏輯。而在
AbstractAutoProxyCreator的子抽象類AbstractAdvisorAutoProxyCreator又重寫了setBeanFactory()方法
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException("AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
} else {
this.initBeanFactory((ConfigurableListableBeanFactory)beanFactory);
}
}
由於其子類AnnotationAwareAspectJAutoProxyCreator 重寫了initBeanFactory方法,所以最終調用的是AnnotationAwareAspectJAutoProxyCreator 中的initBeanFactory方法中的邏輯:
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.initBeanFactory(beanFactory); if (this.aspectJAdvisorFactory == null) { this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory); } this.aspectJAdvisorsBuilder = new AnnotationAwareAspectJAutoProxyCreator.BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory); }

斷點看AOP的整個執行流程:
1、new AnnotationConfigApplicationContext(AopConfig.class); 創建Spring容器,接下來看其構造器邏輯
2、先注冊配置類,然后調用refresh()方法刷新容器,接下來看refresh()的邏輯
3、invokeBeanFactoryPostProcessors(beanFactory);實例化並調用所有注冊的BeanFactoryPostProcessor
4、registerBeanPostProcessors(beanFactory); 注冊bean的后置處理器來攔截bean的創建
1):先獲取ioc容器中已經定義了的需要創建對象的所有BeanPostProcessor {String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);}
2):給容器中添加了額外的BeanPostProcessor{beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));}
3):遍歷BeanPostProcessor按照是否實現PriorityOrdered接口進行分組,若實現了,根據其name獲取其對象(beanFactory.getBean(ppName, BeanPostProcessor.class);)
將其放進priorityOrderedPostProcessors集合,若其又實現了MergedBeanDefinitionPostProcessor,將其再放進internalPostProcessors集合;若其實現了Ordered接口,
將其bean name放入orderedPostProcessorNames集合;否則將其name放進nonOrderedPostProcessorNames集合
4):優先注冊實現了PriorityOrdered接口的BeanPostProcessor,再注冊實現了Ordered接口的BeanPostProcessor,最后注冊其他的BeanPostProcessor
5):注冊其實就是步驟3)中的根據其name獲取BeanPostProcessor對象{BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);},實際上就是
創建BeanPostProcessor對象然后將其保存在Spring容器中。以創建name為internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】為例,看
一下創建過程:
(1):創建bean的實例(createBeanInstance)
(2):給bean的各種屬性賦值(populateBean)
(3):初始化bean(initializeBean)
(1):invokeAwareMethods()判斷bean是不是實現了xxxAware接口,如果實現了就調用相關的setXxx方法給bean的屬性賦值
(2):wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);應用bean的后置處理器的初始化前置方法,獲取所有的后置處理器,
遍歷之后順序應用。for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {result = beanProcessor.postProcessBeforeInitialization(result, beanName);...
(3):invokeInitMethods(beanName, wrappedBean, mbd); 執行初始化方法,如果實現了InitializingBean,執行afterPropertiesSet,再執行自定義的初始化方法(如
果在@Bean注解指定了initMethod的話)
(4):wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);應用bean的后置處理器的初始化后置方法,獲取所有的后置處理器,
遍歷之后順序應用。for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {result = beanProcessor.postProcessAfterInitialization(result, beanName);
6):由於AnnotationAwareAspectJAutoProxyCreator實現了 BeanFactoryAware接口,所以在invokeAwareMethods()方法中,回到了上面說的調用其抽象父類
AbstractAdvisorAutoProxyCreator重寫的setBeanFactory()方法。然后走到了AnnotationAwareAspectJAutoProxyCreator 的initBeanFactory()方法,至此,
BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)創建成功
7):將所有的BeanPostProcessor注冊到BeanFactory中 (registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);-->beanFactory.addBeanPostProcessor(postProcessor);)
以上就是創建和注冊AnnotationAwareAspectJAutoProxyCreator的過程。
接下來看其作為一個BeanPostProcessor如何影響bean的創建過程的:
5、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作,創建剩下的單實例bean,為什么說是剩下的呢?因為BeanPostProcessors類型的bean在步驟4已經創建
完成了
1):preInstantiateSingletons()-->遍歷獲取容器中所有的beanDefinitionNames,遍歷依此創建對象getBean(beanName); 在DefaultListableBeanFactory類中
2):getBean(beanName)-->doGetBean()-->getSingleton(beanName) ,在創建bean之前先嘗試獲取單實例Bean的實例,為什么呢?因為只要創建好的bean就會被緩存起來
(1)先檢查JVM緩存中是否有對應的實例,因為在創建單實例bean的時候會存在依賴注入的情況,為了避免循環依賴,Spring在創建bean
的過程中,若發現有依賴bean,則嘗試去創建依賴的bean,因此Spring將每一個正在創建的bean的beanName放在一個“當前創建
bean池”中,bean在創建過程中,BeanName將一直存在這個池中。當前創建bean池:
private final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
如果當前線程創建bean池有,則嘗試從緩存中加載bean
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
(2)如果緩存中加載不成功,則再次嘗試從singletonFactories中獲取提前曝光的ObjectFactory,如果根據beanName找到了ObjectFactory,則從ObjectFactory中獲取bean
實例,然后將其放在earlySingletonObjects 緩存中,再將其ObjectFactory從singletonFactories中移除。
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
因為Spring為了避免循環依賴,在Spring中創建bean的原則是不等bean創建完就會將創建bean的ObjectFactory提早曝光加入到緩存中,一旦下一個bean創建時候需要依賴
上一個bean則直接使用ObjectFactory去獲取依賴bean的實例。
3):createBean(); 創建bean
(1):Object bean=resolveBeforeInstantiation(beanName, mbdToUse); 參數:RootBeanDefinition mbdToUse,bean的定義信息。方法的作用是:給BeanPostProcessor
一個機會返回一個代理對象就不用創建其對象了。如果可以,直接返回代理對象,如果不能,則繼續執行doCreateBean(beanName, mbdToUse, args);,其實現為:
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); 這個方法的邏輯是:先拿到所有的后置處理器然后遍歷,如果其中一個后置處理器實現了
InstantiationAwareBeanPostProcessor接口,則執行這個后置處理器的postProcessBeforeInstantiation方法,如果返回了bean對象,則執行applyBeanPostProcessorsAfterInitialization
並返回bean實例,結束bean的創建,不走下面的doCreateBean。
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
注意:InstantiationAwareBeanPostProcessor雖繼承了BeanPostProcessor,但其定義了postProcessBeforeInstantiation和postProcessAfterInstantiation方法,與BeanPostProcessor
的postProcessBeforeInitialization和postProcessAfterInitialization方法不同,BeanPostProcessor的兩個方法是在bean實例創建完成之后的初始化前后調用的,而
InstantiationAwareBeanPostProcessor的兩個方法是在創建bean實例之前嘗試返回代理對象的。
這也是AnnotationAwareAspectJAutoProxyCreator的功能,在所有的bean創建之前會有一個攔截,嘗試返回其代理對象。
(2):doCreateBean,真正的創建bean的實例,即4.5的流程
AnnotationAwareAspectJAutoProxyCreator做了什么事情?
1、每一個bean創建之前,都會調用postProcessBeforeInstantiation();其處理邏輯是:
1):判斷當前bean是否在advisedBean(保存了所有增強過的bean)中
2): 判斷當前bean是否是 基礎類型的Advice、Pointcut、Advisor、AopInfrastructureBean或者是否是切面(是否有@Aspect注解)
3):或者是否需要跳過
(1):獲取候選的增強器(切面里面的增強方法)[List<Advisor> candidateAdvisors = findCandidateAdvisors();]
(斷點來看我們的每一個封裝的通知方法的增強器類型是InstantiationModelAwarePointcutAdvisor);遍歷然后判斷每一個增強器是否是AspectJPointcutAdvisor類型的,
如果是,返回true,否則返回flase
2、postProcessAfterInstantiation和postProcessBeforeInitialization一個返回true,一個返回bean對象本身,什么都沒做
3、postProcessAfterInitialization方法:創建完對象並初始化后,調用它。其邏輯是:
判斷當前bean是否已經代理過,若沒有則:return wrapIfNecessary(bean, beanName, cacheKey);這個方法的作用是,封裝bean如果必要的話。
1):Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);找到所有能應用到當前bean的增強方法
(1):獲取配置的所有的切面增強方法
(2):找到所有的增強方法中能應用到當前bean方法中的
(3):獲取到能在當前bean使用的增強方法的集合,並按照一定規則排序
(4):如果能在當前bean使用的增強方法的集合不為空,則轉為數組返回,否則返回null
2):如果返回的數組不為空,則需要代理
(1):將bean的name作為key,值為true,放在advisedBeans(所有增強過的bean的ConcurrentHashMap)中
(2):創建代理對象
step1:如果能在當前bean使用的增強方法
step2:遍歷將其添加到proxyFactory中
step3: DefaultAopProxyFactory中創建代理對象,Spring自動決定
JdkDynamicAopProxy(AdvisedSupport config)
ObjenesisCglibAopProxy(AdvisedSupport config)
(3):將bean作為key,代理對象的class對象作為value,保存到private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<Object, Class<?>>(16);中
(4):返回代理對象
(5):以后在容器中獲取到的就是這個組件的代理對象,執行目標方法的時候,代理對象就會執行增強方法的流程
3)否則不需要代理,將bean的name作為key,值為false,放在advisedBeans中
執行目標對象Target中的切入點方法,即執行被增強的方法的流程:
從容器中獲取的對象是代理對象,即容器中保存了組件的代理對象(cglib增強后的對象),這個對象里面保存了詳細的信息(比如要應用的切面中的增強方法,也叫增強器,目標對象等)
1、CglibAopProxy.intercept();攔截目標方法的執行
2、根據ProxyFactory對象獲取將要執行的目標方法攔截器鏈
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
1):List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length); 集合長度為一個默認的 ExposeInvocationInterceptor.ADVISOR和其他被執行方法的增強器
2):遍歷所有的增強器,將其轉為MethodInterceptor 【MethodInterceptor[] interceptors = registry.getInterceptors(advisor);】
如果是MethodInterceptor,直接放入List<MethodInterceptor>集合中
如果不是,使用AdvisorAdapter將增強器轉為MethodInterceptor,然后再放入List<MethodInterceptor>集合中
最后將集合轉為數組返回
3、如果沒有攔截器鏈,則直接執行目標方法(攔截器鏈,每一個通知方法又被包裝為方法攔截器,利用MethodInterceptor機制)
4、如果有攔截器鏈,把需要執行的目標對象,目標方法,攔截器等信息作為參數創建一個CglibMethodInvocation對象並調用其proceed()方法
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
CglibMethodInvocation對象:

攔截器列表:

5、攔截器的觸發過程
1):如果沒有攔截器執行目標方法,或者攔截器的索引和攔截器數組-1大小一樣(即執行到了最后一個攔截器),則執行目標方法
2):鏈式獲取每一個攔截器,攔截器執行invoke方法,每一個攔截器等待下一個攔截器執行完成返回后再去執行(類似於遞歸調用),
如上面攔截器的調用順序是 Expose-->AfterReturning-->Around-->Before,然后前面的等待后面的返回再執行,最后實際執行順
序是Before-->Around-->AfterReturning-->Expose
攔截器鏈的機制,保證通知方法與目標方法的執行順序。
AOP總結
1、@EnableAspectJAutoProxy 開啟AOP功能
2、@EnableAspectJAutoProxy 會給容器中注冊一個組件 AnnotationAwareApectJAutoProxyCreator
3、AnnotationAwareApectJAutoProxyCreator是一個后置處理器
4、容器的創建流程
1):registerBeanPostProcessors(beanFactory); 注冊后置處理器,創建AnnotationAwareApectJAutoProxyCreator對象
2):finishBeanFactoryInitialization(beanFactory); 初始化剩下的單實例bean
(1):創建業務邏輯自檢和切面組件
(2):AnnotationAwareApectJAutoProxyCreator 攔截組件的創建過程
(3): 組件創建完成之后,作為后置處理器的AnnotationAwareApectJAutoProxyCreator在postProcessAfterInitialization
方法中判斷組件是否需要增強,如果需要則 將需要應用到目標對象的切面中的通知方法,包裝成增強器Advisor,然后
給業務邏輯組件創建一個代理對象。
5、執行目標方法
1):代理對象來執行目標方法
2):CglibAopProxy.intercept();攔截目標方法的執行
(1):得到目標方法執行的攔截器鏈
(2):利用攔截器的鏈式機制,依次進入每一個攔截器進行執行
(3):實際執行順序:
正常執行:前置通知-->目標方法-->后置通知-->返回通知
出現異常:前置通知-->目標方法-->后置通知-->異常通知
