摘要:下面跟蹤代碼到這個(gè)實(shí)現(xiàn)中看看是怎么做的在實(shí)例化的過(guò)程中,在構(gòu)造函數(shù)中調(diào)用了其超類的構(gòu)造函數(shù),而在超類中對(duì)其所處換環(huán)境進(jìn)行的判斷,所謂的環(huán)境呢,事實(shí)上指得就是是通過(guò),還是通過(guò)加載的上下文,這也就意味著不同方式加載可能存在某些不同。
前言
本文基于《Spring源碼深度解析》學(xué)習(xí), 《Spring源碼深度解析》講解的Spring版本低于Spring3.1,當(dāng)前閱讀的版本為Spring5.x,所以在文章內(nèi)容上會(huì)有所不同。
這篇文章基于有一定Spring 基礎(chǔ)的人進(jìn)行講解,所以有些問(wèn)題并不做詳細(xì)的實(shí)現(xiàn), 如有分析不合理或是錯(cuò)誤的地方請(qǐng)指教指正,不勝感激。
在《Spring源碼深度解析》中有這樣一個(gè)實(shí)例:
public class BeanFactoryTest { @test public void testSimpleLoad() { BeanFactory bf = new XmBeanFactory(new ClassPathResource("beanFactoryTest.xml"); MyTestBean bean = (MyTestBean) bf.getBean("myTestBean"); assertEquals("testStr", bean.getTestStr()); } }
當(dāng)然在這里會(huì)有一個(gè)Spring的配置文件 beanFactoryTest.xml, 當(dāng)使用xml文件的時(shí)候,會(huì)發(fā)現(xiàn)文件頭有一些
這樣的標(biāo)簽, 建議學(xué)習(xí)一下DOM,DOM2, DOM3結(jié)構(gòu), 以便更加清晰的了解xml文件頭中的內(nèi)容的真正意義。
這里的配置文件只寫一個(gè)相關(guān)的bean
這段代碼的作用就是以下幾點(diǎn):
讀取配置文件。
在Spring的配置中找到bean,并實(shí)例化。
使用斷言判斷實(shí)例的屬性。
@deprecated as of Spring 3.1 in favor of {@link DefaultListableBeanFactory} and {@link XmlBeanDefinitionReader}
這是該類在當(dāng)前版本的部分注釋,在Spring3.1以后這個(gè)類被棄用了,Spring官方不建議使用這個(gè)類。建議使用以上這兩個(gè)類。XmlBeanDefinitionReader本就是以前定義在這個(gè)類中的一個(gè)final的實(shí)例,而DefaultListableBeanFactory則是該類的超類。加載配置文件可以這樣使用:
Resource resource = new ClassPathResource("beanFactoryTest.xml"); BeanFactory beanFactory = new DefaultListableBeanFactory(); BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) beanFactory); beanDefinitionReader.loadBeanDefinitions(resource); MyTestBean bean = (MyTestBean) bf.getBean("myTestBean"); assertEquals("testStr", bean.getTestStr());
這個(gè)過(guò)程和上面的過(guò)程實(shí)際上的實(shí)現(xiàn)只用一點(diǎn)不同、前者是在創(chuàng)建時(shí)就直接實(shí)例化了bean, 后者則是在加載的時(shí)候才實(shí)例化bean:
讀取配置文件。
創(chuàng)建BeanFactory。
創(chuàng)建BeanDefinitionReader。
加載resource資源。
獲取bean實(shí)例(實(shí)例化bean)。
使用斷言判斷實(shí)例的屬性。
事實(shí)上在實(shí)際的使用中,絕大多數(shù)時(shí)候都會(huì)通過(guò)以下這種ApplicationContext的方式來(lái)加載Spring的配置文件并進(jìn)行解析, 以后會(huì)寫到這里的實(shí)現(xiàn):
ApplicationContext sc = new ClassPathXmlApplicationContext("applicationContext.xml");二、加載并解析配置文件
Resource resource = new ClassPathResource("beanFactoryTest.xml");
通過(guò)ClassPathResource 加載配置文件,并構(gòu)建該實(shí)例的時(shí)候,是使用Resource接口進(jìn)行定義的, 這也就說(shuō)明了創(chuàng)建的實(shí)際上是Resource的實(shí)例,通過(guò)查看Resource 的源碼不難發(fā)現(xiàn),Resource對(duì)Java中將要使用的資源進(jìn)行了抽象,Spring的設(shè)計(jì)中幾乎所有可以加載資源的
類需要直接或間接的實(shí)現(xiàn)Resource 這個(gè)接口。下面可以看一下這個(gè)接口:
boolean exists(); // 判斷是否資源是否存在 default boolean isReadable() { // 判斷資源是否可讀 return exists(); } default boolean isOpen() { // 判斷文件是否打開 return false; } default boolean isFile() { // 判斷文件是否是文件系統(tǒng)中的文件,Spring5.0后加入的 return false; } URL getURL() throws IOException; // 獲取文件的URL URI getURI() throws IOException; // 獲取文件的URI File getFile() throws IOException; // 獲取文件 default ReadableByteChannel readableChannel() throws IOException { // 返回一個(gè)Channel, 擁有最大效率的讀操作 return Channels.newChannel(getInputStream()); } long contentLength() throws IOException; // 返回資源解析后的長(zhǎng)度 long lastModified() throws IOException; // 最后一次休干時(shí)間 Resource createRelative(String relativePath) throws IOException; // 基于當(dāng)前資源創(chuàng)建一個(gè)相對(duì)資源 @Nullable String getFilename(); // 獲取文件名 for example, "myfile.txt" String getDescription(); // 獲取資源描述, 當(dāng)發(fā)生錯(cuò)誤時(shí)將被打印
通過(guò)查看源碼,還有一點(diǎn)可以發(fā)現(xiàn), Resource接口繼承了InputStreamSource 接口,下面來(lái)看下這個(gè)接口:
public interface InputStreamSource { /** * Return an {@link InputStream} for the content of an underlying resource. *It is expected that each call creates a fresh stream. *
This requirement is particularly important when you consider an API such * as JavaMail, which needs to be able to read the stream multiple times when * creating mail attachments. For such a use case, it is required * that each {@code getInputStream()} call returns a fresh stream. * @return the input stream for the underlying resource (must not be {@code null}) * @throws java.io.FileNotFoundException if the underlying resource doesn"t exist * @throws IOException if the content stream could not be opened */ InputStream getInputStream() throws IOException; }
這個(gè)接口的作用非常簡(jiǎn)單并且是頂層接口,它的作用就是返回一個(gè)InputStream, 最簡(jiǎn)單的作用卻提供了最大的方便, 因?yàn)樗匈Y源加載類幾乎都直接或間接的實(shí)現(xiàn)了Resource, 這也就意味著, 幾乎所有的資源加載類都可以得到一個(gè)InputStream, 這將使得在資源加載之后能輕易地得到一個(gè)InputStream, 這非常重要。通過(guò)InputStream, Spring使所有的資源文件都能進(jìn)行統(tǒng)一的管理了, 優(yōu)點(diǎn)是不言而喻的。至于實(shí)現(xiàn)是非常簡(jiǎn)單的, ClassPathResource 中的實(shí)現(xiàn)方式便是通過(guò)class或者classLoader提供的底層方法進(jìn)行調(diào)用的, 對(duì)于FileSystemResource的實(shí)現(xiàn)其實(shí)更加簡(jiǎn)單, 就是直接使用FileInputStream對(duì)文件進(jìn)行實(shí)例化。
三、DefaultListableBeanFactory、XmlBeanDefinitionReader和BeanDefinitionRegistry配置文件加載完成后進(jìn)行的下一步操作是這樣的,這和3.1之前的版本不太一樣,至于為什么要棄用XmlBeanFactory(我猜是為了對(duì)其他部分進(jìn)行設(shè)計(jì),從而讓這部分代碼更加充分的進(jìn)行解耦):
BeanFactory beanFactory = new DefaultListableBeanFactory(); BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) beanFactory); beanDefinitionReader.loadBeanDefinitions(resource);
配置文件加載完成后,創(chuàng)建了一個(gè)BeanFactory的實(shí)例。DefaultListableBeanFactory: 見名知意,這是一個(gè)默認(rèn)可列的bean工廠類,注釋用說(shuō)道,典型的一個(gè)應(yīng)用就是在第一次定義時(shí)注冊(cè)所有bean。接下來(lái)的操作是利用多態(tài)將上一步創(chuàng)建的BeanFactory的實(shí)例轉(zhuǎn)成BeanDefinitionRegistry, 因?yàn)橄乱徊叫枰x取xml文件中定義的內(nèi)容,這也就是XmlBeanDefinitionReader的作用,而XmlBeanDefinitionReader在實(shí)例化的時(shí)候需要一個(gè)bean的定義注冊(cè)機(jī),所以就進(jìn)行了以上操作, 事實(shí)上:在創(chuàng)建BeanFactory實(shí)例時(shí),同樣可以定義為BeanDefinitionRegistry類型。下面詳細(xì)說(shuō)下一這三個(gè)類的作用:
DefaultListableBeanFactory:在定義時(shí)通過(guò)當(dāng)前類的類加載器(如果不存在就向上級(jí)加載器尋找直到系統(tǒng)加載器)下的所有的bean進(jìn)行注冊(cè),注意這里只是進(jìn)行注冊(cè)而已。
BeanDefinitionRegistry: 為了注冊(cè)Spring持有的bean的一個(gè)接口,是在BeanFactory,AbstractBeanDefinition中間的一層接口。
XmlBeanDefinitionReader:注釋中這樣寫道
/** * Bean definition reader for XML bean definitions. * Delegates the actual XML document reading to an implementation * of the {@link BeanDefinitionDocumentReader} interface. * *Typically applied to a * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory} * or a {@link org.springframework.context.support.GenericApplicationContext}. * *
This class loads a DOM document and applies the BeanDefinitionDocumentReader to it. * The document reader will register each bean definition with the given bean factory, * talking to the latter"s implementation of the * {@link org.springframework.beans.factory.support.BeanDefinitionRegistry} interface. */ 只說(shuō)最重要的一個(gè)部分, 在這里它需要委托一個(gè)真正的xml文檔讀取器來(lái)讀取文檔內(nèi)容,也就是BeanDefinitionDocumentReader,而這個(gè)文檔讀取器將讀取所有的bean注冊(cè)內(nèi)容,而這些資源正是1、2中所得到的。
接下來(lái)就是重要的一步,beanDefinitionReader.loadBeanDefinitions(resource); 在解析了配置文件中的bean后,事實(shí)上配置文件中bean并沒有被真正的加載,并且上面的步驟也只是對(duì)所有的bean進(jìn)行了一次注冊(cè), 所以,這個(gè)時(shí)候load了resoure中的內(nèi)容, 在編碼沒有問(wèn)題以后,并且resource中bean可以在類加載器下找到這些類,這時(shí)就對(duì)這些bean進(jìn)行加載,實(shí)例化。下面跟蹤代碼到這個(gè)實(shí)現(xiàn)中看看Spring 是怎么做的:
/** * Create new XmlBeanDefinitionReader for the given bean factory. * @param registry the BeanFactory to load bean definitions into, * in the form of a BeanDefinitionRegistry */ public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); } protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; // Determine ResourceLoader to use. if (this.registry instanceof ResourceLoader) { this.resourceLoader = (ResourceLoader) this.registry; } else { this.resourceLoader = new PathMatchingResourcePatternResolver(); } // Inherit Environment if possible if (this.registry instanceof EnvironmentCapable) { this.environment = ((EnvironmentCapable) this.registry).getEnvironment(); } else { this.environment = new StandardEnvironment(); } }
在實(shí)例化XmlBeanDefinitionReader 的過(guò)程中,在構(gòu)造函數(shù)中調(diào)用了其超類的構(gòu)造函數(shù),而在超類中對(duì)其所處換環(huán)境進(jìn)行的判斷,所謂的環(huán)境呢,事實(shí)上指得就是是通過(guò)BeanFactory, 還是通過(guò)ApplicationContext加載的上下文,這也就意味著不同方式加載可能存在某些不同。寫這些的目的其實(shí)是為了引出這里的一個(gè)我們十分關(guān)注的東西, 就是自動(dòng)裝配。在AbstractAutowireCapableBeanFactory這個(gè)抽象類的構(gòu)造方法中實(shí)現(xiàn)了相關(guān)的自動(dòng)裝配,在BeanDefinitionRegistry 和DefaultListableBeanFactory中都繼承了這個(gè)抽象類, 并在其構(gòu)造函數(shù)內(nèi)直接調(diào)用了其超類的構(gòu)造函數(shù)也就是:
/** * Create a new AbstractAutowireCapableBeanFactory. */ public AbstractAutowireCapableBeanFactory() { super(); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); }
這里有必要提及一下ignoreDependencyInterface();這個(gè)方法。它的主要功能就是忽略接口中的自動(dòng)裝配, 那么這樣做的目的是什么呢?會(huì)產(chǎn)生什么樣的效果呢?舉例來(lái)說(shuō), 當(dāng)A中有屬性B, 那么Spring在獲取A的時(shí)候就會(huì)去先去獲取B, 然而有些時(shí)候Spring不會(huì)這樣做,就是Spring通過(guò)BeanNameAware、BeanFactoryAware和BeanClassLoaderAware進(jìn)行注入的, 也就是根據(jù)環(huán)境的不同, Spring會(huì)選擇相應(yīng)的自從裝配的方式。在不是當(dāng)前環(huán)境中的注入,Spring并不會(huì)再當(dāng)前環(huán)境對(duì)Bean進(jìn)行自動(dòng)裝配。類似于,BeanFactory通過(guò)BeanFactoryAwar進(jìn)行注入或者ApplicationContext通過(guò)ApplicationContextAware進(jìn)行注入。
經(jīng)過(guò)了這么長(zhǎng)時(shí)間的鋪墊,終于應(yīng)該進(jìn)入正題了, 就是進(jìn)入通過(guò)loadBeanDefinitions(resource)方法加載這個(gè)文件。這個(gè)方法這樣實(shí)現(xiàn):
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } SetcurrentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
這段代碼很容易理解,不過(guò)真正實(shí)現(xiàn)的核心代碼是在return doLoadBeanDefinitions(inputSource, encodedResource.getResource());這里實(shí)現(xiàn)的。下面是這個(gè)方法的核心代碼:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } }
這段處理可以說(shuō)非常容易了,對(duì)resource流進(jìn)行再封裝,封裝為Docment對(duì)象,然后解析并注冊(cè)這個(gè)doc中的bean對(duì)象,返回定義的bean的個(gè)數(shù)。在Spring3.1之前上面這個(gè)方法中還要驗(yàn)證加載Xml是否符合規(guī)范。而Spring5.x之后Spring將驗(yàn)證的工作放到了獲取Document中。
三、獲取Document看一下Document doc = doLoadDocument(inputSource, resource);這個(gè)方法的源碼:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }
在這個(gè)方法中做了三件事:
getEntityResolver();這個(gè)方法將根據(jù)當(dāng)前的resource創(chuàng)建一個(gè)ResourceLoader實(shí)例,然后根據(jù)這個(gè)對(duì)ResourceLoader進(jìn)行封裝,封裝為EntityResolver實(shí)例, 這個(gè)EntityResolver的作用是進(jìn)行處理實(shí)體映射。
getValidationModeForResource(); 這個(gè)方法的作用是獲取資源的驗(yàn)證模式,通過(guò)自動(dòng)或手動(dòng)的方式對(duì)已經(jīng)加載到的資源進(jìn)行檢驗(yàn)。這里是真正對(duì)xml文件進(jìn)行驗(yàn)證的地方。
isNamespaceAware(); 這個(gè)方法用來(lái)判斷加載的xml文件是否支持明明空間。
實(shí)現(xiàn)上面方法的類繼承了這個(gè)接口:DocumentLoader,并且實(shí)現(xiàn)了這個(gè)接口中的唯一的抽象:
/** * Load a {@link Document document} from the supplied {@link InputSource source}. * @param inputSource the source of the document that is to be loaded * @param entityResolver the resolver that is to be used to resolve any entities * @param errorHandler used to report any errors during document loading * @param validationMode the type of validation * {@link org.springframework.util.xml.XmlValidationModeDetector#VALIDATION_DTD DTD} * or {@link org.springframework.util.xml.XmlValidationModeDetector#VALIDATION_XSD XSD}) * @param namespaceAware {@code true} if support for XML namespaces is to be provided * @return the loaded {@link Document document} * @throws Exception if an error occurs */
Document loadDocument(
InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception;
那么詳細(xì)講一下上面提及的EntityResolver, 如果SAX(Simple API for XML:簡(jiǎn)單的來(lái)講,它的作用就是不去構(gòu)建DOM,而是以應(yīng)用程序的方式以最有效率的方式實(shí)現(xiàn)XML與應(yīng)用實(shí)體之間的映射;當(dāng)然還有一種方式是解析DOM,具體兩種方式,我也沒有做過(guò)相應(yīng)深入探究)應(yīng)用驅(qū)動(dòng)程序需要實(shí)現(xiàn)自定義的處理外部實(shí)體,在必須實(shí)現(xiàn)此接口并通過(guò)某種方式向SAX驅(qū)動(dòng)器注冊(cè)一個(gè)實(shí)例。這需要根據(jù)XML頭部的DTD中的網(wǎng)絡(luò)地址下載聲明并認(rèn)證,而EntityResolver實(shí)際上就是在提供一個(gè)尋dtd找聲明的方法,這樣就可以在項(xiàng)目中直接定義好聲明,而通過(guò)本地尋找的方式避免了網(wǎng)絡(luò)尋找的過(guò)程,編譯器也避免了在網(wǎng)絡(luò)延遲高或沒有網(wǎng)絡(luò)的情況下報(bào)錯(cuò)。
四、解析及注冊(cè)BeanDefinitions當(dāng)文件轉(zhuǎn)換為Document后,接下來(lái)提取及注冊(cè)Bean就是我們后頭的重頭戲。事實(shí)上,這一部分內(nèi)容并不會(huì)在我們的使用中出現(xiàn)了。
同樣在XmlBeanDefinitionReader這個(gè)類中,可以發(fā)現(xiàn)隨著Docment的獲取完成后,直接做的是下面的這個(gè)事情registerBeanDefinitions();:
/** * Register the bean definitions contained in the given DOM document. * Called by {@code loadBeanDefinitions}. *Creates a new instance of the parser class and invokes * {@code registerBeanDefinitions} on it. * @param doc the DOM document * @param resource the resource descriptor (for context information) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of parsing errors * @see #loadBeanDefinitions * @see #setDocumentReaderClass * @see BeanDefinitionDocumentReader#registerBeanDefinitions */ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
作用在注釋中寫的很清楚,注冊(cè)DOM文檔(Spring的配置信息中. 也就是解析后的xml)中包含的bean。
首先創(chuàng)建一個(gè)bean定義文檔讀取器,這個(gè)對(duì)象是根據(jù)DefaultBeanDefinitionDocumentReader的class通過(guò)反射的方式來(lái)創(chuàng)建的,
DefaultBeanDefinitionDocumentReader實(shí)現(xiàn)了BeanDefinitionDocumentReader這個(gè)接口,這里唯一的抽象方法就是registerBeanDefinitions(Document doc, XmlReaderContext readerContext);,這也就意味著上面的第三行代碼是一個(gè)自然的應(yīng)用。
提示:這里的doc參數(shù)就是通過(guò)之前的doLoadDocument方法獲得的,而這很好的應(yīng)用了面向?qū)ο蟮膯我宦氊?zé)原則, 將轉(zhuǎn)換為Docment的復(fù)雜過(guò)程交給一個(gè)單一的類處理,而這個(gè)類就是BeanDefinitionDocumentReader, 事實(shí)上這是一個(gè)接口,而具體的實(shí)例化是在createBeanDefinitionDocumentReader這個(gè)方法中完成的。
getRegistry();的作用實(shí)際上是獲得一個(gè)BeanDefinitionRegistry對(duì)象。下面的圖片是程序最開始時(shí),容器開始實(shí)現(xiàn)時(shí)候的代碼,這里可以看到BeanDefinitionRegistry這個(gè)接口。注意這里創(chuàng)建的BeanDefinitionRegistry是final的,也就是這里獲取的是Spring發(fā)現(xiàn)的所有的bean個(gè)數(shù),是不許改變的, 熟悉設(shè)計(jì)模式的同學(xué)肯定知道,這個(gè)BeanDefinitionRegistry是一個(gè)單例的。
而接下來(lái)做的就是就是,記錄所有對(duì)我們的資源文件進(jìn)行加載,這里是真正解析xml文件并加載的地方,而這個(gè)邏輯就是那么簡(jiǎn)單了, 先統(tǒng)計(jì)當(dāng)前的bean defintions個(gè)數(shù)然后加載一些bean定義進(jìn)來(lái),然后在統(tǒng)計(jì)bean 的個(gè)數(shù),然后用后來(lái)的減去開始的就是加載的。沒錯(cuò)了,就是學(xué)前班加減法。
到這里我已經(jīng)不想探究xml文件是如何讀取的了,如果想看的話,可以去看下一篇《Spring源碼一(容器的基本實(shí)現(xiàn)2)》!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72023.html
摘要:本文是容器源碼分析系列文章的第一篇文章,將會(huì)著重介紹的一些使用方法和特性,為后續(xù)的源碼分析文章做鋪墊。我們可以通過(guò)這兩個(gè)別名獲取到這個(gè)實(shí)例,比如下面的測(cè)試代碼測(cè)試結(jié)果如下本小節(jié),我們來(lái)了解一下這個(gè)特性。 1. 簡(jiǎn)介 Spring 是一個(gè)輕量級(jí)的企業(yè)級(jí)應(yīng)用開發(fā)框架,于 2004 年由 Rod Johnson 發(fā)布了 1.0 版本。經(jīng)過(guò)十幾年的迭代,現(xiàn)在的 Spring 框架已經(jīng)非常成熟了...
摘要:進(jìn)一步解析其他所有屬性并統(tǒng)一封裝至類型的實(shí)例中。是一個(gè)接口,在中存在三種實(shí)現(xiàn)以及。通過(guò)將配置文件中配置信息轉(zhuǎn)換為容器的內(nèi)部表示,并將這些注冊(cè)到中。容器的就像是配置信息的內(nèi)存數(shù)據(jù)庫(kù),主要是以的形式保存。而代碼的作用就是實(shí)現(xiàn)此功能。 前言:繼續(xù)前一章。 一、porfile 屬性的使用 如果你使用過(guò)SpringBoot, 你一定會(huì)知道porfile配置所帶來(lái)的方便, 通過(guò)配置開發(fā)環(huán)境還是生產(chǎn)...
摘要:從使用到原理學(xué)習(xí)線程池關(guān)于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實(shí)現(xiàn)在軟件開發(fā)中,分散于應(yīng)用中多出的功能被稱為橫切關(guān)注點(diǎn)如事務(wù)安全緩存等。 Java 程序媛手把手教你設(shè)計(jì)模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經(jīng)風(fēng)雨慢慢變老,回首走過(guò)的點(diǎn)點(diǎn)滴滴,依然清楚的記得當(dāng)初愛情萌芽的模樣…… Java 進(jìn)階面試問(wèn)題列表 -...
摘要:對(duì)于開發(fā)者來(lái)說(shuō),無(wú)疑是最常用也是最基礎(chǔ)的框架之一。概念上的東西還是要提一嘴的用容器來(lái)管理。和是容器的兩種表現(xiàn)形式。定義了簡(jiǎn)單容器的基本功能。抽象出一個(gè)資源類來(lái)表示資源調(diào)用了忽略指定接口的自動(dòng)裝配功能委托解析資源。 對(duì)于Java開發(fā)者來(lái)說(shuō),Spring無(wú)疑是最常用也是最基礎(chǔ)的框架之一。(此處省略1w字吹Spring)。相信很多同行跟我一樣,只是停留在會(huì)用的階段,比如用@Component...
閱讀 906·2021-10-25 09:44
閱讀 1285·2021-09-23 11:56
閱讀 1201·2021-09-10 10:50
閱讀 3144·2019-08-30 15:53
閱讀 2148·2019-08-30 13:17
閱讀 634·2019-08-29 18:43
閱讀 2512·2019-08-29 12:57
閱讀 869·2019-08-26 12:20