摘要:本篇文章是源碼解析上的續(xù)集,上一篇文章介紹了使用的方式啟動,然后追蹤了容器的創(chuàng)建配置文件的解析的注冊等。前方超長篇幅預警。。。記錄依賴關系通過類型裝配。這也是作者第一次閱讀開源框架的源碼,如文章有錯誤之處還請您費心指出。
注意,看完這篇文章需要很長很長很長時間。。。
本篇文章是SpringIOC源碼解析(上)的續(xù)集,上一篇文章介紹了使用XML的方式啟動Spring,然后追蹤了BeanFactory容器的創(chuàng)建、配置文件的解析、Bean的注冊等。
前方超長篇幅預警。。。
剛才我們提到了bean還沒有初始化。這個方法就是負責初始化所有的沒有設置懶加載的singleton bean
開始擼了
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); } //先初始化 LoadTimeWeaverAware 類型的 Bean String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } //停止使用用于類型匹配的臨時類加載器 beanFactory.setTempClassLoader(null); //凍結所有的bean定義,即已注冊的bean定義將不會被修改或后處理 beanFactory.freezeConfiguration(); //初始化 beanFactory.preInstantiateSingletons(); }
上方?jīng)]有解釋的代碼意義往下看吧
這種類型的bean最實用的場景就是用來將前端傳過來的參數(shù)和后端的controller方法上的參數(shù)格式轉換的時候使用
例如:前端要傳一個String,后端使用Date接受的時候就可以這樣操作
public class StringToDateConverter implements Converter{ @Override public Date convert(String date) { try { return dateFormat.parse(date); } catch (Exception e) { e.printStackTrace(); System.out.println("日期轉換失敗!"); return null; } } }
再搞個bean
利用EmbeddedValueResolver可以很方便的實現(xiàn)讀取配置文件的屬性
@Component public class PropertiesUtil implements EmbeddedValueResolverAware { private StringValueResolver resolver; @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.resolver = resolver; } /** * 獲取屬性時直接傳入屬性名稱即可 */ public String getPropertiesValue(String key) { StringBuilder name = new StringBuilder("${").append(key).append("}"); return resolver.resolveStringValue(name.toString()); } }
敲黑板了,重點來了。。。
這里分析beanFactory.preInstantiateSingletons()方法
public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("Pre-instantiating singletons in " + this); } // this.beanDefinitionNames 保存了所有的 beanNames ListbeanNames = new ArrayList (this.beanDefinitionNames); for (String beanName : beanNames) { // 合并父 Bean 中的配置,主意 中的 parent屬性 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 不是抽象類、是單例的且不是懶加載的 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 處理 FactoryBean if (isFactoryBean(beanName)) { //在 beanName 前面加上“&” 符號 final FactoryBean> factory = (FactoryBean>) getBean(FACTORY_BEAN_PREFIX + beanName); // 判斷當前 FactoryBean 是否是 SmartFactoryBean 的實現(xiàn) boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged(new PrivilegedAction () { @Override public Boolean run() { return ((SmartFactoryBean>) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } else { // 不是FactoryBean的直接使用此方法進行初始化 getBean(beanName); } } } // 如果bean實現(xiàn)了 SmartInitializingSingleton 接口的,那么在這里得到回調(diào) for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction
可以看到,不管是不是FactoryBean,最后都調(diào)用了getBean(beanName),繼續(xù)看這個方法吧
@Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } protectedT doGetBean( final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { // 獲取beanName,處理兩種情況,一個是前面說的 FactoryBean(前面帶 ‘&’),再一個這個方法是可以根據(jù)別名來獲取Bean的,所以在這里是要轉換成最正統(tǒng)的BeanName //主要邏輯就是如果是FactoryBean就把&去掉如果是別名就把根據(jù)別名獲取真實名稱后面就不貼代碼了 final String beanName = transformedBeanName(name); //最后的返回值 Object bean; // 檢查是否已初始化 Object sharedInstance = getSingleton(beanName); //如果已經(jīng)初始化過了,且沒有傳args參數(shù)就代表是get,直接取出返回 if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("..."); } else { logger.debug("Returning cached instance of singleton bean "" + beanName + """); } } // 這里如果是普通Bean 的話,直接返回,如果是 FactoryBean 的話,返回它創(chuàng)建的那個實例對象 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // 如果存在prototype類型的這個bean if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 如果當前BeanDefinition不存在這個bean且具有父BeanFactory BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { String nameToLookup = originalBeanName(name); // 返回父容器的查詢結果 if (args != null) { return (T) parentBeanFactory.getBean(nameToLookup, args); } else { return parentBeanFactory.getBean(nameToLookup, requiredType); } } if (!typeCheckOnly) { // typeCheckOnly 為 false,將當前 beanName 放入一個 alreadyCreated 的 Set 集合中。 markBeanAsCreated(beanName); } /* * 到這就要創(chuàng)建bean了 */ try { final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // 先初始化依賴的所有 Bean, depends-on 中定義的依賴 String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { // 檢查是不是有循環(huán)依賴 if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between "" + beanName + "" and "" + dep + """); } // 注冊一下依賴關系 registerDependentBean(dep, beanName); // 先初始化被依賴項 getBean(dep); } } // 如果是單例的 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory
看了上方方法我們知道了原來Spring本身只定義了兩種Scope,也知道了SpringMVC的幾種Scope是如何實現(xiàn)的了。
然后發(fā)現(xiàn)一開始會先判斷bean存不存在,如果存在就直接返回了。如果不存在那就要接著往下看createBean方法了
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean "" + beanName + """); } RootBeanDefinition mbdToUse = mbd; // 確保 BeanDefinition 中的 Class 被加載 Class> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } // 準備方法覆寫,如果bean中定義了和 try { mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // 如果有代理的話直接返回 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); } // 創(chuàng)建 bean Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean "" + beanName + """); } return beanInstance; } protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { //如果是.factoryBean則從緩存刪除 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 實例化 Bean,這個方法里面才是終點,下面說 instanceWrapper = createBeanInstance(beanName, mbd, args); } //bean實例 final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); //bean類型 Class> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); mbd.resolvedTargetType = beanType; synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { // 循環(huán)調(diào)用實現(xiàn)了MergedBeanDefinitionPostProcessor接口的postProcessMergedBeanDefinition方法 // Spring對這個接口有幾個默認的實現(xiàn),其中大家最熟悉的一個是操作@Autowired注解的 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // 解決循環(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"); } //當正在創(chuàng)建A時,A依賴B,此時通過(8將A作為ObjectFactory放入單例工廠中進行early expose,此處B需要引用A,但A正在創(chuàng)建,從單例工廠拿到ObjectFactory,從而允許循環(huán)依賴 addSingletonFactory(beanName, new ObjectFactory () { @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } Object exposedObject = bean; try { // 負責屬性裝配,很重要,下面說 populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { // 這里是處理bean初始化完成后的各種回調(diào),例如init-method、InitializingBean 接口、BeanPostProcessor 接口 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); } } //同樣的,如果存在循環(huán)依賴 if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set actualDependentBeans = new LinkedHashSet (dependentBeans.length); for (String dependentBean : dependentBeans) { 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."); } } } } // 把bean注冊到相應的Scope中 try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
到這里第一次初始化的bean也返回了,你以為就這樣結束了么。不,還有幾個很重要的點
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { // 確保已經(jīng)加載了此 class 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()); } if (mbd.getFactoryMethodName() != null) { // 采用工廠方法實例化 return instantiateUsingFactoryMethod(beanName, mbd, args); } //是否第一次 boolean resolved = false; //是否采用構造函數(shù)注入 boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { return autowireConstructor(beanName, mbd, null, null); } else { // 無參構造函數(shù) return instantiateBean(beanName, mbd); } } // 判斷是否采用有參構造函數(shù) Constructor>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { // 構造函數(shù)依賴注入 return autowireConstructor(beanName, mbd, ctors, args); } // 調(diào)用無參構造函數(shù) return instantiateBean(beanName, mbd); }
選一個無參的構造看一下吧
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { try { Object beanInstance; final BeanFactory parent = this; if (System.getSecurityManager() != null) { beanInstance = AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { return getInstantiationStrategy().instantiate(mbd, beanName, parent); } }, getAccessControlContext()); } else { // 具體實例化的實現(xiàn),往下看 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } } public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) { // 如果不存在方法覆寫,那就使用 java 反射進行實例化,否則使用 CGLIB, if (bd.getMethodOverrides().isEmpty()) { Constructor> constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction >() { @Override public Constructor> run() throws Exception { return clazz.getDeclaredConstructor((Class[]) null); } }); } else { constructorToUse = clazz.getDeclaredConstructor((Class[]) null); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } // 利用構造方法進行實例化 return BeanUtils.instantiateClass(constructorToUse); } else { // 存在方法覆寫,利用 CGLIB 來完成實例化,需要依賴于 CGLIB 生成子類,這里就不展開了 return instantiateWithMethodInjection(bd, beanName, owner); } }
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { // bean的所有屬性 PropertyValues pvs = mbd.getPropertyValues(); if (bw == null) { if (!pvs.isEmpty()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { return; } } boolean continueWithPropertyPopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // 如果返回 false,代表不需要進行后續(xù)的屬性設值,也不需要再經(jīng)過其他的 BeanPostProcessor 的處理 if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } } } if (!continueWithPropertyPopulation) { return; } if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // 通過名字找到所有屬性值,如果是 bean 依賴,先初始化依賴的 bean。記錄依賴關系 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // 通過類型裝配。復雜一些 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE); if (hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // 這里就是上方曾經(jīng)提到過得對@Autowired處理的一個BeanPostProcessor了 // 它會對所有標記@Autowired、@Value 注解的屬性進行設值 pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); } } // 設置 bean 實例的屬性值 applyPropertyValues(beanName, mbd, bw, pvs); }
getBean這一塊就搞完了
protected void finishRefresh() { //看名字就知道了,清理剛才一系列操作使用到的資源緩存 clearResourceCaches(); // 初始化LifecycleProcessor initLifecycleProcessor(); // 這個方法的內(nèi)部實現(xiàn)是啟動所有實現(xiàn)了Lifecycle接口的bean getLifecycleProcessor().onRefresh(); //發(fā)布ContextRefreshedEvent事件 publishEvent(new ContextRefreshedEvent(this)); // 檢查spring.liveBeansView.mbeanDomain是否存在,有就會創(chuàng)建一個MBeanServer LiveBeansView.registerApplicationContext(this); }
最后還是一步還是清除緩存
上方用了這么長的篇幅把整個refresh()方法的細節(jié)給梳理清楚,這里再把剛開始看的懵懵的refresh() 方法貼一下
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 記錄容器的啟動時間、標記“已啟動”狀態(tài)、檢查環(huán)境變量 prepareRefresh(); // 初始化BeanFactory容器、注冊BeanDefinition ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 設置 BeanFactory 的類加載器,添加幾個 BeanPostProcessor,手動注冊幾個特殊的 bean prepareBeanFactory(beanFactory); try { // 擴展點 postProcessBeanFactory(beanFactory); // 調(diào)用 BeanFactoryPostProcessor 各個實現(xiàn)類的 postProcessBeanFactory(factory) 方法 invokeBeanFactoryPostProcessors(beanFactory); // 注冊 BeanPostProcessor 的實現(xiàn)類 registerBeanPostProcessors(beanFactory); // 初始化MessageSource initMessageSource(); // 初始化事件廣播器 initApplicationEventMulticaster(); // 擴展點 onRefresh(); // 注冊事件監(jiān)聽器 registerListeners(); // 初始化所有的 singleton beans finishBeanFactoryInitialization(beanFactory); // 廣播事件 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 銷毀已經(jīng)初始化的的Bean destroyBeans(); // 設置 "active" 狀態(tài) cancelRefresh(ex); throw ex; } finally { // 清除緩存 resetCommonCaches(); } } }總結
至此,本文到此結束。鑒于XML方式比較簡單、通俗易懂,所以本文基于XML的方式大致介紹了SpringIOC的啟動流程、稍微深入的講解了Bean容器的創(chuàng)建以及Bean的初始化過程。這也是作者第一次閱讀開源框架的源碼,如文章有錯誤之處還請您費心指出。
鑒于現(xiàn)在比較流行SpringBoot和SpringCloud,下篇文章將會從基于注解的方向分析SpringIOC
SpringCloud學習系列匯總
為什么一線大廠面試必問redis,有啥好問的?
多線程面試必備基礎知識匯總
Java集合源碼分析匯總-JDK1.8
Linux常用命令速查-匯總篇
JVM系列文章匯總
MySQL系列文章匯總
RabbitMQ系列文章匯總
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76206.html
摘要:重要的會在后面拎出來單獨詳解加鎖,防止在過程中,重啟或銷毀造成不必要的問題準備此上下文以進行刷新,設置其啟動日期和,活動標志以及執(zhí)行屬性源的任何初始化,校驗配置文件。以后所有的相關的操作其實是委托給這個實例來處理的。 1、先上測試代碼 public static void main(String[] args){ //配置文件來啟動一個 ApplicationContext App...
摘要:不同與其它中間件框架,中有大量的業(yè)務代碼,它向我們展示了大神是如何寫業(yè)務代碼的依賴的層次結構,如何進行基礎包配置,以及工具類編寫,可以稱之為之最佳實踐。代碼參考視圖解析器,這里的配置指的是不檢查頭,而且默認請求為格式。 不同與其它中間件框架,Apollo中有大量的業(yè)務代碼,它向我們展示了大神是如何寫業(yè)務代碼的:maven依賴的層次結構,如何進行基礎包配置,以及工具類編寫,可以稱之為sp...
摘要:對于開發(fā)者來說,無疑是最常用也是最基礎的框架之一。概念上的東西還是要提一嘴的用容器來管理。和是容器的兩種表現(xiàn)形式。定義了簡單容器的基本功能。抽象出一個資源類來表示資源調(diào)用了忽略指定接口的自動裝配功能委托解析資源。 對于Java開發(fā)者來說,Spring無疑是最常用也是最基礎的框架之一。(此處省略1w字吹Spring)。相信很多同行跟我一樣,只是停留在會用的階段,比如用@Component...
摘要:代碼如下所示自定義業(yè)務實現(xiàn)恒宇少年碼云消息內(nèi)容是否顯示消息內(nèi)容,我們內(nèi)的代碼比較簡單,根據(jù)屬性參數(shù)進行返回格式化后的字符串。 在我們學習SpringBoot時都已經(jīng)了解到starter是SpringBoot的核心組成部分,SpringBoot為我們提供了盡可能完善的封裝,提供了一系列的自動化配置的starter插件,我們在使用spring-boot-starter-web時只需要在po...
閱讀 689·2023-04-25 19:53
閱讀 4262·2021-09-22 15:13
閱讀 2565·2019-08-30 10:56
閱讀 1320·2019-08-29 16:27
閱讀 2932·2019-08-29 14:00
閱讀 2407·2019-08-26 13:56
閱讀 426·2019-08-26 13:29
閱讀 1611·2019-08-26 11:31