摘要:前言這篇是專題初始化的第二篇,主要對初始化具體過程的源碼分析。上篇博客專題之初始化源碼分析中我們對如何開始初始化以及初始化的總體過程有了大致的了解,接下來就繼續(xù)上篇博客的結(jié)尾處開始來分析初始化的具體過程。
前言
這篇是Spring專題Bean初始化的第二篇,主要對bean初始化具體過程的源碼分析。上篇博客Spring專題之Bean初始化源碼分析(1)中我們對Spring如何開始初始化bean以及bena初始化的總體過程有了大致的了解,接下來就繼續(xù)上篇博客的結(jié)尾處開始來分析初始化bean的具體過程。
Bean初始化上篇博客中我們知道了Spring在通過判斷bean定義是否是單例bean,是否是原型bena之后,最后都是調(diào)用了createBean方法去創(chuàng)建bean的,所以我們進入該方法分析具體初始化過程。進入方法后發(fā)現(xiàn)是該方法是AbstractBeanFactory的一個抽象方法,真正地調(diào)用是在子類AbstractAutowireCapableBeanFactory中重寫了。源碼如下:
//創(chuàng)建Bean實例對象 protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean "" + beanName + """); } RootBeanDefinition mbdToUse = mbd; //判斷需要創(chuàng)建的Bean是否可以實例化,即是否可以通過當(dāng)前的類加載器加載 Class> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } //校驗和準(zhǔn)備Bean中的方法覆蓋 try { mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { //如果Bean配置了初始化前和初始化后的處理器,則試圖返回一個需要創(chuàng)建Bean的代理對象 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } try { //創(chuàng)建Bean的入口 Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean "" + beanName + """); } return beanInstance; } catch (BeanCreationException ex) { throw ex; } catch (ImplicitlyAppearedSingletonException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } }
這里我們有兩個地方需要了解的,第一個是resolveBeforeInstantiation方法,第二個是doCreateBean方法。之后我們重點分析doCreateBean方法,這里的resolveBeforeInstantiation方法主要是針對Bean如果配置了初始化前和初始化后的處理器,會試圖返回一個需要創(chuàng)建Bean的代理對象。那么現(xiàn)在我們開始進入doCreateBean方法,源碼如下:
//真正創(chuàng)建Bean的方法 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { //封裝被創(chuàng)建的Bean對象 BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); //獲取實例化對象的類型 Class> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } //調(diào)用PostProcessor后置處理器 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; } } //向容器中緩存單例模式的Bean對象,以防循環(huán)引用 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean "" + beanName + "" to allow for resolving potential circular references"); } //這里是一個匿名內(nèi)部類,為了防止循環(huán)引用,盡早持有對象的引用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } //Bean對象的初始化,依賴注入在此觸發(fā) //這個exposedObject在初始化完成之后返回作為依賴注入完成后的Bean Object exposedObject = bean; try { //將Bean實例對象封裝,并且Bean定義中配置的屬性值賦值給實例對象 populateBean(beanName, mbd, instanceWrapper); //初始化Bean對象 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { //獲取指定名稱的已注冊的單例模式Bean對象 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { //根據(jù)名稱獲取的已注冊的Bean和正在實例化的Bean是同一個 if (exposedObject == bean) { //當(dāng)前實例化的Bean初始化完成 exposedObject = earlySingletonReference; } //當(dāng)前Bean依賴其他Bean,并且當(dāng)發(fā)生循環(huán)引用時不允許新創(chuàng)建實例對象 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); SetactualDependentBeans = new LinkedHashSet<>(dependentBeans.length); //獲取當(dāng)前Bean所依賴的其他Bean for (String dependentBean : dependentBeans) { //對依賴Bean進行類型檢查 if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name "" + beanName + "" has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + ""getBeanNamesOfType" with the "allowEagerInit" flag turned off, for example."); } } } } // Register bean as disposable. //注冊完成依賴注入的Bean try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
這里有三個方法我們需要注意,第一個createBeanInstance方法,這是bean實例化的第一步,第二個是populateBean方法,這是bean依賴注入的地方,第三個是initializeBean,這是初始化bean的地方。
首先我們先看createBeanInstance方法,源碼如下:
//創(chuàng)建Bean的實例對象 protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. //檢查確認(rèn)Bean是可實例化的 Class> beanClass = resolveBeanClass(mbd, beanName); //使用工廠方法對Bean進行實例化 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()); } Supplier> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } if (mbd.getFactoryMethodName() != null) { //調(diào)用工廠方法實例化 return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... //使用容器的自動裝配方法進行實例化 boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { //配置了自動裝配屬性,使用容器的自動裝配實例化 //容器的自動裝配是根據(jù)參數(shù)類型匹配Bean的構(gòu)造方法 return autowireConstructor(beanName, mbd, null, null); } else { //使用默認(rèn)的無參構(gòu)造方法實例化 return instantiateBean(beanName, mbd); } } // Need to determine the constructor... //使用Bean的構(gòu)造方法進行實例化 Constructor>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { //使用容器的自動裝配特性,調(diào)用匹配的構(gòu)造方法實例化 return autowireConstructor(beanName, mbd, ctors, args); } // No special handling: simply use no-arg constructor. //使用默認(rèn)的無參構(gòu)造方法實例化 return instantiateBean(beanName, mbd); }
通過上述的源碼分析,我們知道Spring這里對bean實例化有幾種方式,分別是工廠方法實例化,容器自動裝配以及使用默認(rèn)的無參構(gòu)造方法實例化。
然后我們在來看populateBean方法是如何進行依賴注入的,源碼如下:
//將Bean屬性設(shè)置到生成的實例對象上 protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { //如果屬性存在但是需要注入的對象為空,則拋出異常。 if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { // Skip property population phase for null instance. return; } } // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the // state of the bean before properties are set. This can be used, for example, // to support styles of field injection. //使得InstantiationAwareBeanPostProcessors有機會在屬性注入之前修改bean的狀態(tài),例如支持一些屬性類型的注入。 boolean continueWithPropertyPopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } } } if (!continueWithPropertyPopulation) { return; } //獲取容器在解析Bean定義資源時為BeanDefiniton中設(shè)置的屬性值 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); //對依賴注入處理,首先處理autowiring自動裝配的依賴注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. //根據(jù)Bean名稱進行autowiring自動裝配處理 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. //根據(jù)Bean類型進行autowiring自動裝配處理 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } //對非autowiring的屬性進行依賴注入處理 boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE); if (hasInstAwareBpps || needsDepCheck) { if (pvs == null) { pvs = mbd.getPropertyValues(); } PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); } } if (pvs != null) { //對屬性進行注入 applyPropertyValues(beanName, mbd, bw, pvs); } }
這里有也有幾個方法需要注意,分別是autowireByName、autowireByType以及applyPropertyValues。
我們先來看下autowireByName方法,源碼如下:
//根據(jù)名稱對屬性進行自動依賴注入 protected void autowireByName( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { //對Bean對象中非簡單屬性(不是簡單繼承的對象,如8中原始類型,字符串,URL等都是簡單屬性)進行處理 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { //如果Spring IOC容器中包含指定名稱的Bean if (containsBean(propertyName)) { //調(diào)用getBean方法向IOC容器索取指定名稱的Bean實例,迭代觸發(fā)屬性的初始化和依賴注入 Object bean = getBean(propertyName); //為指定名稱的屬性賦予屬性值 pvs.add(propertyName, bean); //指定名稱屬性注冊依賴Bean名稱,進行屬性依賴注入 registerDependentBean(propertyName, beanName); if (logger.isDebugEnabled()) { logger.debug("Added autowiring by name from bean name "" + beanName + "" via property "" + propertyName + "" to bean named "" + propertyName + """); } } else { if (logger.isTraceEnabled()) { logger.trace("Not autowiring property "" + propertyName + "" of bean "" + beanName + "" by name: no matching bean found"); } } } }
可以看到這里通過找到Bean中非簡單屬性的屬性,比如CharSequence類型、Number類型、Date類型、URL類型、URI類型、Locale類型、Class類型就會忽略,具體可見BeanUtils的isSimpleProperty方法,遍歷所有被找到的屬性,如果bean定義中包含了屬性名,那么先實例化該屬性名對應(yīng)的bean,注冊一下當(dāng)前bean的依賴bean。
然后我們看下根據(jù)類型自動裝配autowireByType方法的實現(xiàn),源碼如下:
//根據(jù)類型對屬性進行自動依賴注入 protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { //獲取用戶定義的類型轉(zhuǎn)換器 TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } //存放解析的要注入的屬性 SetautowiredBeanNames = new LinkedHashSet<>(4); //對Bean對象中非簡單屬性(不是簡單繼承的對象,如8中原始類型,字符 //URL等都是簡單屬性)進行處理 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { try { //獲取指定屬性名稱的屬性描述器 PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); // Don"t try autowiring by type for type Object: never makes sense, // even if it technically is a unsatisfied, non-simple property. //不對Object類型的屬性進行autowiring自動依賴注入 if (Object.class != pd.getPropertyType()) { //獲取屬性的setter方法 MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); // Do not allow eager init for type matching in case of a prioritized post-processor. //檢查指定類型是否可以被轉(zhuǎn)換為目標(biāo)對象的類型 boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance()); //創(chuàng)建一個要被注入的依賴描述 DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); //根據(jù)容器的Bean定義解析依賴關(guān)系,返回所有要被注入的Bean對象 Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { //為屬性賦值所引用的對象 pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { //指定名稱屬性注冊依賴Bean名稱,進行屬性依賴注入 registerDependentBean(autowiredBeanName, beanName); if (logger.isDebugEnabled()) { logger.debug("Autowiring by type from bean name "" + beanName + "" via property "" + propertyName + "" to bean named "" + autowiredBeanName + """); } } //釋放已自動注入的屬性 autowiredBeanNames.clear(); } } catch (BeansException ex) { throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); } } }
具體的解析過程主要看resolveDependency方法,這里感興趣的同學(xué)可以研究下具體過程。
最后我們來看下applyPropertyValues方法,源碼如下:
//解析并注入依賴屬性的過程 protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { if (pvs.isEmpty()) { return; } //封裝屬性值 MutablePropertyValues mpvs = null; Listoriginal; if (System.getSecurityManager() != null) { if (bw instanceof BeanWrapperImpl) { //設(shè)置安全上下文,JDK安全機制 ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext()); } } if (pvs instanceof MutablePropertyValues) { mpvs = (MutablePropertyValues) pvs; //屬性值已經(jīng)轉(zhuǎn)換 if (mpvs.isConverted()) { // Shortcut: use the pre-converted values as-is. try { //為實例化對象設(shè)置屬性值 bw.setPropertyValues(mpvs); return; } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } } //獲取屬性值對象的原始類型值 original = mpvs.getPropertyValueList(); } else { original = Arrays.asList(pvs.getPropertyValues()); } //獲取用戶自定義的類型轉(zhuǎn)換 TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } //創(chuàng)建一個Bean定義屬性值解析器,將Bean定義中的屬性值解析為Bean實例對象的實際值 BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); // Create a deep copy, resolving any references for values. //為屬性的解析值創(chuàng)建一個拷貝,將拷貝的數(shù)據(jù)注入到實例對象中 List deepCopy = new ArrayList<>(original.size()); boolean resolveNecessary = false; for (PropertyValue pv : original) { //屬性值不需要轉(zhuǎn)換 if (pv.isConverted()) { deepCopy.add(pv); } //屬性值需要轉(zhuǎn)換 else { String propertyName = pv.getName(); //原始的屬性值,即轉(zhuǎn)換之前的屬性值 Object originalValue = pv.getValue(); //轉(zhuǎn)換屬性值,例如將引用轉(zhuǎn)換為IOC容器中實例化對象引用 Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); //轉(zhuǎn)換之后的屬性值 Object convertedValue = resolvedValue; //屬性值是否可以轉(zhuǎn)換 boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); if (convertible) { //使用用戶自定義的類型轉(zhuǎn)換器轉(zhuǎn)換屬性值 convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter); } // Possibly store converted value in merged bean definition, // in order to avoid re-conversion for every created bean instance. //存儲轉(zhuǎn)換后的屬性值,避免每次屬性注入時的轉(zhuǎn)換工作 if (resolvedValue == originalValue) { if (convertible) { //設(shè)置屬性轉(zhuǎn)換之后的值 pv.setConvertedValue(convertedValue); } deepCopy.add(pv); } //屬性是可轉(zhuǎn)換的,且屬性原始值是字符串類型,且屬性的原始類型值不是 //動態(tài)生成的字符串,且屬性的原始值不是集合或者數(shù)組類型 else if (convertible && originalValue instanceof TypedStringValue && !((TypedStringValue) originalValue).isDynamic() && !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) { pv.setConvertedValue(convertedValue); //重新封裝屬性的值 deepCopy.add(pv); } else { resolveNecessary = true; deepCopy.add(new PropertyValue(pv, convertedValue)); } } } if (mpvs != null && !resolveNecessary) { //標(biāo)記屬性值已經(jīng)轉(zhuǎn)換過 mpvs.setConverted(); } // Set our (possibly massaged) deep copy. //進行屬性依賴注入 try { bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } }
這里主要是對需要轉(zhuǎn)換的屬性進行轉(zhuǎn)換然后進行屬性值的依賴注入。
至此屬性的依賴注入分析結(jié)束,最后我們看下initializeBean方法是如何對bean進行初始化操作的,其源碼如下:
//初始容器創(chuàng)建的Bean實例對象,為其添加BeanPostProcessor后置處理器 protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { //JDK的安全機制驗證權(quán)限 if (System.getSecurityManager() != null) { //實現(xiàn)PrivilegedAction接口的匿名內(nèi)部類 AccessController.doPrivileged((PrivilegedAction
至此,bean初始化操作的總體過程分析結(jié)束,當(dāng)然中間存在一些細(xì)節(jié)方面的點沒有分析清楚,還需要各位小伙伴自行研究,然后有什么問題可以留言,大家一起討論一起進步。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/77024.html
摘要:初始化我們知道容器初始化后會對容器中非懶加載的,單例的以及非抽象的定義進行的初始化操作,所以我們分析源碼的入口也就是在容器初始化的入口,分析容器初始化后在什么地方開始第一次的初始化。 前言 Spring IOC容器在初始化之后會對容器中非懶加載的,單例的以及非抽象的bean定義進行bean的初始化操作,同時會也涉及到Bean的后置處理器以及DI(依賴注入)等行為。對于Bean的初始化,...
摘要:前言以下源碼基于版本解析。實現(xiàn)源碼分析對于的實現(xiàn),總結(jié)來說就是定位加載和注冊。定位就是需要定位配置文件的位置,加載就是將配置文件加載進內(nèi)存注冊就是通過解析配置文件注冊。下面我們從其中的一種使用的方式一步一步的分析的實現(xiàn)源碼。 前言 以下源碼基于Spring 5.0.2版本解析。 什么是IOC容器? 容器,顧名思義可以用來容納一切事物。我們平常所說的Spring IOC容器就是一個可以容...
摘要:的在單例被破壞時由進行方法調(diào)用。定義并實現(xiàn)這兩個接口容器創(chuàng)建完成注解是的縮寫,意思是規(guī)范提案。在創(chuàng)建完成并且屬性賦值完成來執(zhí)行初始化方法在容器銷毀之前回調(diào)通知支持自動裝配,類似。 Spring注解應(yīng)用篇--IOC容器Bean生命周期 這是Spring注解專題系類文章,本系類文章適合Spring入門者或者原理入門者,小編會在本系類文章下進行企業(yè)級應(yīng)用實戰(zhàn)講解以及spring源碼跟進。本文...
摘要:入門篇學(xué)習(xí)總結(jié)時間年月日星期三說明本文部分內(nèi)容均來自慕課網(wǎng)。主要的功能是日志記錄,性能統(tǒng)計,安全控制,事務(wù)處理,異常處理等等。 《Spring入門篇》學(xué)習(xí)總結(jié) 時間:2017年1月18日星期三說明:本文部分內(nèi)容均來自慕課網(wǎng)。@慕課網(wǎng):http://www.imooc.com教學(xué)示例源碼:https://github.com/zccodere/s...個人學(xué)習(xí)源碼:https://git...
閱讀 814·2021-11-25 09:43
閱讀 1681·2021-09-29 09:42
閱讀 1897·2019-08-30 15:55
閱讀 3418·2019-08-30 15:54
閱讀 2623·2019-08-30 13:20
閱讀 3506·2019-08-29 13:25
閱讀 916·2019-08-28 18:03
閱讀 1783·2019-08-26 13:44