前言
在之前的文章中,我們介紹了 Bean 的核心概念、Bean 定義的解析過程以及 Bean 建立的準備工作。在今天的文章中,我們將深入探討 Bean 的建立過程,並主要講解 createBean 方法的實現。在這個過程中,我們將瞭解 Bean 的例項化、屬性注入、初始化和銷燬等步驟,以及各個步驟的具體實現細節。透過本文的學習,讀者將能夠更深入地理解 Spring 框架中 Bean 的建立過程,從而為後續的學習和實踐打下堅實的基礎。好了,我們開始!
createBean
前面我們說過,最開始的bean定義(合併後的),解析類的後設資料時,用到的是ASM技術並不會真正開始解析class檔案,所以也只是提取出來bean的name值作為beanClass屬性,知道這個前提,那麼這一步就好說了,下面是他的原始碼:
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
RootBeanDefinition mbdToUse = mbd;
// 馬上就要例項化Bean了,確保beanClass被載入了
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
mbdToUse.prepareMethodOverrides();
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// 例項化前
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
......
return beanInstance;
}
}
- resolveBeanClass:真正的開始載入bean。
- mbdToUse.prepareMethodOverrides();和@lookUp註解有關係,不看
- resolveBeforeInstantiation:例項化前的BeanPostProcessors,如果初始化了那麼就返回了,不走其他建立邏輯了。
- doCreateBean:正常開始例項化、初始化bean。
resolveBeanClass
如果當前bean被載入了,那麼直接返回了,如果沒載入那麼開始解析當前bean
@Nullable
protected Class<?> resolveBeanClass(RootBeanDefinition mbd, String beanName, Class<?>... typesToMatch)
throws CannotLoadBeanClassException {
try {
// 如果beanClass被載入了
if (mbd.hasBeanClass()) {
return mbd.getBeanClass();
}
// 如果beanClass沒有被載入
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>)
() -> doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
}
else {
return doResolveBeanClass(mbd, typesToMatch);
}
}
}
是否已經載入的判斷依據就是我說的,是否是class,正常下我們的beanClass為字串,也就是beanname,看下原始碼:
public boolean hasBeanClass() {
return (this.beanClass instanceof Class);
}
doResolveBeanClass
真正開始載入class,如果需要載入class那肯定離不開類載入器,看下原始碼:
@Nullable
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
throws ClassNotFoundException {
ClassLoader beanClassLoader = getBeanClassLoader();
ClassLoader dynamicLoader = beanClassLoader;
boolean freshResolve = false;
if (!ObjectUtils.isEmpty(typesToMatch)) {
// When just doing type checks (i.e. not creating an actual instance yet),
// use the specified temporary class loader (e.g. in a weaving scenario).
ClassLoader tempClassLoader = getTempClassLoader();
if (tempClassLoader != null) {
dynamicLoader = tempClassLoader;
freshResolve = true;
if (tempClassLoader instanceof DecoratingClassLoader) {
DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
for (Class<?> typeToMatch : typesToMatch) {
dcl.excludeClass(typeToMatch.getName());
}
}
}
}
String className = mbd.getBeanClassName();
if (className != null) {
// 解析Spring表示式,有可能直接返回了一個Class物件
Object evaluated = evaluateBeanDefinitionString(className, mbd);
if (!className.equals(evaluated)) {
// A dynamically resolved expression, supported as of 4.2...
if (evaluated instanceof Class) {
return (Class<?>) evaluated;
}
else if (evaluated instanceof String) {
className = (String) evaluated;
freshResolve = true;
}
else {
throw new IllegalStateException("Invalid class name expression result: " + evaluated);
}
}
if (freshResolve) {
// When resolving against a temporary class loader, exit early in order
// to avoid storing the resolved Class in the bean definition.
if (dynamicLoader != null) {
try {
return dynamicLoader.loadClass(className);
}
catch (ClassNotFoundException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);
}
}
}
return ClassUtils.forName(className, dynamicLoader);
}
}
// Resolve regularly, caching the result in the BeanDefinition...
return mbd.resolveBeanClass(beanClassLoader);
}
我們自己的bean走不了這麼多邏輯,我們既沒有傳typesToMatch,也沒有寫Spring表示式,所以就是拿了一個類載入器和使用類載入器載入class,如果我們沒有自定義類載入器那麼使用預設的,看下原始碼:
@Nullable
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
// 優先獲取執行緒中的類載入器
try {
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
// 執行緒中類載入器為null的情況下,獲取載入ClassUtils類的類載入器
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
// 加入ClassUtils是被Bootstrap類載入器載入的,則獲取系統類載入器
try {
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
- 優先獲取執行緒中的類載入器
- 執行緒中類載入器為null的情況下,獲取載入ClassUtils類的類載入器,這裡Spring注意到了java的boostrap載入器,所以會有為null的情況
- 如果為null,那麼使用ClassUtils當前工具類使用的是哪個載入器
- 假如ClassUtils是被Bootstrap類載入器載入的,則獲取系統類載入器
public Class<?> resolveBeanClass(@Nullable ClassLoader classLoader) throws ClassNotFoundException {
String className = getBeanClassName();
if (className == null) {
return null;
}
Class<?> resolvedClass = ClassUtils.forName(className, classLoader);
this.beanClass = resolvedClass;
return resolvedClass;
}
public String getBeanClassName() {
Object beanClassObject = this.beanClass;
if (beanClassObject instanceof Class) {
return ((Class<?>) beanClassObject).getName();
}
else {
return (String) beanClassObject;
}
}
透過這一步也可以看出bean定義中最初的beanClass屬性,都是String型別的beanname
resolveBeforeInstantiation
這一步走的是例項化前的工作,當然如果你想在這一步中直接返回實體類也可,而且最離譜的是Spring並沒有校驗你返回的類是否是當前beanname的類,可以看下原始碼:
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
// synthetic表示合成,如果某些Bean式合成的,那麼則不會經過BeanPostProcessor的處理
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
- hasInstantiationAwareBeanPostProcessors:直接從快取list中獲取有關例項化的BeanPostProcessors,這裡是一個最佳化,要不然每次獲取有關例項化的BeanPostProcessors都是遍歷整個BeanPostProcessors再加個校驗
- determineTargetType:獲取類
- applyBeanPostProcessorsBeforeInstantiation:執行InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation的方法,該方法可以返回bean。
- postProcessAfterInstantiation:執行BeanPostProcessor的postProcessAfterInstantiation的方法,正常我們的bean不會走到這裡,因為例項化前根本沒有建立出來bean,所以也就是bean != null一直為false
當然除非你自己寫一個InstantiationAwareBeanPostProcessors,其實真沒看見這麼玩的,主要是沒有啥意義,比如這樣:
@Component
public class MyInstantiationAwareBeanPostProcessors implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("userService")) {
System.out.println("MyInstantiationAwareBeanPostProcessors.postProcessBeforeInstantiation");
return new First();
}
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("userService")) {
System.out.println("MyInstantiationAwareBeanPostProcessors.postProcessAfterInitialization");
return new Second();
}
return bean;
}
}
再堅持一下,讓我把例項化過程先講完!
現在的邏輯已經走完了例項化前的postProcessBeforeInstantiation方法,那麼現在我們的bean要進行例項化了,
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 例項化bean
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 有可能在本Bean建立之前,就有其他Bean把當前Bean給建立出來了(比如依賴注入過程中)
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 建立Bean例項
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 後置處理合併後的BeanDefinition
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// 為了解決迴圈依賴提前快取單例建立工廠
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 迴圈依賴-新增到三級快取
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 屬性填充
populateBean(beanName, mbd, instanceWrapper);
......
return exposedObject;
}
跟這篇無關的內容能刪除的都刪除了,主要有這幾步我們需要注意下:
- createBeanInstance:建立例項,前提是之前沒有建立過
- applyMergedBeanDefinitionPostProcessors:找到注入點,比如AutowiredAnnotationBeanPostProcessor(@Autowired、@Value、@Inject)和CommonAnnotationBeanPostProcessor(@Resource),這在例項化前和例項化後方法中間夾了一個處理合併bean定義的邏輯,注意一下
- addSingletonFactory:新增快取,用來解決迴圈依賴,以後單獨講解
- populateBean:這一方法主要是屬性填充也就是依賴注入的,但是官方把例項化後的PostProcessors方法寫到這裡了,所以也得貼出來,但是我們只看例項化相關的。
createBeanInstance
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
// BeanDefinition中新增了Supplier,則呼叫Supplier來得到物件
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// @Bean對應的BeanDefinition
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
......
return instantiateBean(beanName, mbd);
}
- resolveBeanClass:之前講解過了,不重複講了,就是拿到class
- obtainFromSupplier:透過Supplier函式獲取bean,前提是你得宣告bean定義
- instantiateUsingFactoryMethod:這種是使用@Bean方法例項化物件,
- 後面省略了推斷構造方法進行例項化物件,以後單獨講解推斷構造方法
obtainFromSupplier
這一步其實我們用到的很少,主要是考慮到Spring自動注入的開銷,我們自己可以就行例項化而已,比如我們這樣寫照樣可以獲取bean,但是不會由Spring幫我們注入,得靠自己了:
// 建立一個Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
beanDefinition.setInstanceSupplier(() -> new UserService());
applicationContext.registerBeanDefinition("userService", beanDefinition);
UserService userService = (UserService) applicationContext.getBean(UserService.class);
userService.test();
其實用法和@bean註解相似,除了減少Spring自動注入的開銷,實在沒想到有啥用
instantiateUsingFactoryMethod
該方法內部邏輯很多,為了更加直觀的展現,只貼出關鍵程式碼:
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(factoryMethod);
return null;
});
}
else {
ReflectionUtils.makeAccessible(factoryMethod);
}
Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
try {
currentlyInvokedFactoryMethod.set(factoryMethod);
// factoryBean就是AppConfig的代理物件(如果加了@Configuration)
// factoryMethod就是@Bean修飾的方法
Object result = factoryMethod.invoke(factoryBean, args);
if (result == null) {
result = new NullBean();
}
return result;
}
finally {
if (priorInvokedFactoryMethod != null) {
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
}
else {
currentlyInvokedFactoryMethod.remove();
}
}
}
......
}
比如我們定義的配置類中有很多@Bean形式的方法,最終Spring會直接invoke呼叫被@Bean修飾的方法從而實現例項化物件。
applyMergedBeanDefinitionPostProcessors
這裡關於MergedBeanDefinitionPostProcessors的實現類不全講解了,主要講解下工作常用的註解AutowiredAnnotationBeanPostProcessor,他是用來解析@Autowired、@Value、@Inject,看下他的預設原始碼:
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
}
}
看下他主要做了那些工作,關鍵程式碼附上:
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
// 如果一個Bean的型別是String...,那麼則根本不需要進行依賴注入
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 遍歷targetClass中的所有Field
ReflectionUtils.doWithLocalFields(targetClass, field -> {
// field上是否存在@Autowired、@Value、@Inject中的其中一個
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
// static filed不是注入點,不會進行自動注入
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 構造注入點
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 遍歷targetClass中的所有Method
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
// method上是否存在@Autowired、@Value、@Inject中的其中一個
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// static method不是注入點,不會進行自動注入
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
// set方法最好有入參
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
- 如果一個Bean的型別是String,那麼則根本不需要進行依賴注入
- 遍歷targetClass中的所有Field,是否存在@Autowired、@Value、@Inject中的其中一個,如果是static欄位則不注入否則記錄構造注入點
- 遍歷targetClass中的所有Method,是否存在@Autowired、@Value、@Inject中的其中一個,如果是static欄位則不注入否則記錄構造注入點
populateBean
這個方法主要是屬性填充,也就是所說的依賴注入的過程,我們不講解這一部分,只講解關於例項化最後的階段postProcessAfterInstantiation方法,方法進來第一步就是呼叫postProcessAfterInstantiation方法。但是隻看Spring原始碼的話,其實並沒有太多實現,都是預設實現方法:
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
總結
在本文中,我們深入探討了 Spring 框架中 Bean 的例項化過程,關於某些細節以後我會單獨拿出一篇文章單獨講解,我們來總結下例項化都做了哪些事情:
- 先從bean定義中載入當前類,因為最初Spring使用ASM技術解析後設資料時只獲取了當前類的名稱
- 尋找所有InstantiationAwareBeanPostProcessors實現類,並呼叫例項化前的方法postProcessBeforeInstantiation
- 進行例項化,這裡會使用構造方法進行例項化
- 呼叫applyMergedBeanDefinitionPostProcessors找到所有MergedBeanDefinitionPostProcessors的實現類,比如我們的注入點(@Autowired等)
- 尋找所有InstantiationAwareBeanPostProcessors實現類,並呼叫例項化後的方法postProcessAfterInstantiation
透過本文的學習,讀者將能夠更深入地瞭解 Spring 框架中 Bean 的例項化過程,為後續的學習和實踐打下堅實的基礎。下一篇文章,我們將深入探討 Bean 的初始化過程。