摘要:本文是針對的來進行解析并將解析后的信息使用作為載體進行注冊已經在中被標記為不建議使用,但是我們分析源碼不影響,因為源碼并未改變,并依舊使用和進行的解析和注冊工作,本篇博客是跟源碼一步步看怎么實現的注冊,源碼為源碼已經在每一行上加了注釋,方便
本文是針對Srping的XMLBeanFactory來進行解析xml并將解析后的信息使用GenericBeanDefinition作為載體進行注冊,xmlBeanFactory已經在Spring 3.1中被標記為不建議使用,但是我們分析源碼不影響,因為源碼并未改變,并ApplicationContext依舊使用XmlBeanDefinitionReader和DefaultListableBeanFactory進行xml的解析和注冊工作,本篇博客是跟源碼一步步看spring怎么實現bean的注冊,源碼為spring5.X,源碼已經在每一行上加了注釋,方便讀者學習。
GitHub: https://github.com/lantaoGitH...
首先我們從XMLBeanFactory入手,直接上代碼:
package org.springframework.lantao; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class XmlBeanFactoryTest { public static void main(String[] args) { // 資源加載 ClassPathResource classPathResource = new ClassPathResource("spring-bean.xml"); // XmlBeanFactory 加載資源并解析注冊bean BeanFactory beanFactory = new XmlBeanFactory(classPathResource); // BeanFactory.getBean(); UserBean userBean = (UserBean) beanFactory.getBean("userBean"); System.out.println(userBean.getName()); } }
XmlBeanFactory解析Xml是使用了XmlBeanDefinitionReader.loadBeanDefinition()方法,源碼如下:
@Deprecated @SuppressWarnings({"serial", "all"}) public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); /** * Create a new XmlBeanFactory with the given resource, * which must be parsable using DOM. * @param resource the XML resource to load bean definitions from * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource) throws BeansException { //調用構造方法 79行 this(resource, null); } /** * Create a new XmlBeanFactory with the given input stream, * which must be parsable using DOM. * @param resource the XML resource to load bean definitions from * @param parentBeanFactory parent bean factory * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { //ignoreDependencyInterface 忽略自動裝配 //主要功能就是當有忽略的接口類,自動裝配會忽略這部分類的初始化裝配,因為某種情況下,此時的接口實現類不能初始化,列如BeanNameAware,要想裝配這個接口的實現對象,可以實現這個接口。 super(parentBeanFactory); //這段代碼是真正的資源加載 this.reader.loadBeanDefinitions(resource); } }
我們直接看loadBeanDefinition方法,源碼:
/** * Load bean definitions from the specified XML file. * @param resource the resource descriptor for the XML file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { // 對EncodedResource進行封裝,設置String encoding, Charset charset return loadBeanDefinitions(new EncodedResource(resource)); } /** * Load bean definitions from the specified XML file. * @param encodedResource the resource descriptor for the XML file, * allowing to specify an encoding to use for parsing the file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { //encodedResource 不可以為空 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 { // 從encodedResource已經封裝的Resource獲取InputStream InputStream inputStream = encodedResource.getResource().getInputStream(); try { //InputSource 并不是spring的,而是 org.xml.sax InputSource inputSource = new InputSource(inputStream); //如果encodedResource 中的 Encoding 不是 null 則同步設置 InputSource的 Encoding if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //加載bean的Definitions 將xml中的信息加載到Definition中,并且在內存中注冊的也是key+definitions 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(); } } }
上述源碼可能看著比較長,但實際上這里并不是真正解析的地方,在這里做了如下:1:從encodedResource已經封裝的Resource獲取InputStream;
2:如果encodedResource 中的 Encoding 不是 null 則同步設置 InputSource的 Encoding;
3:將解析動作委托給doLoadBeanDefinitions實現;
接下來我們繼續看doLoadBeanDefinitions方法內容:
/** * Actually load bean definitions from the specified XML file. * @param inputSource the SAX InputSource to read from * @param resource the resource descriptor for the XML file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #doLoadDocument * @see #registerBeanDefinitions */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //加載 Document Document doc = doLoadDocument(inputSource, resource); //注冊 bean int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
當我們看著這個方法的時候,依舊不是真正的解析或注冊的方法,在這里只是做了Document的加載,并將后續工作委托給了registerBeanDefinitions,registerBeanDefinitions方法的返回時注冊Bean的個數;
我們繼續看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 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //獲取之前的beanDefinition加載個數 int countBefore = getRegistry().getBeanDefinitionCount(); //加載xml及注冊bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //記錄本次加載個數 return getRegistry().getBeanDefinitionCount() - countBefore; }
在registerBeanDefinitions方法具體實現:1:通過BeanUtils.instantiateClass(this.documentReaderClass)的方法實例化BeanDefinitionDocumentReader;
2:通過DefaultListAbleBeanFactory中的beanDefinitionMap.size()獲取之前注冊bean的個數,(beanDefinitionMap是存儲最終的xml解析后信息的載體,xml解析后信息是由GenericBeanDefinition進行存儲,beanDefinitionMap的存儲格式是key:String value:GenericBeanDefinition)
3:將解析xml和注冊的工作委托給BeanDefinitionDocumentReader的registerBeanDefinitions方法;
4:記錄本次加載個數并返回;
繼續看BeanDefinitionDocumentReader的registerBeanDefinitions方法:
/** * This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). *Opens a DOM Document; then initializes the default settings * specified at the {@code
} level; then parses the contained bean definitions. */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { //實例化 ReaderContext this.readerContext = readerContext; //注冊 doRegisterBeanDefinitions(doc.getDocumentElement()); }
registerBeanDefinitions并沒有做什么,我們繼續看doRegisterBeanDefinitions方法:
/** * Register each bean definition within the given root {@code} element. */ @SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...) protected void doRegisterBeanDefinitions(Element root) { // Any nested elements will cause recursion in this method. In // order to propagate and preserve default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); //驗證xml namespace, BeanDefinitionParserDelegate.BEANS_NAMESPACE_URI if (this.delegate.isDefaultNamespace(root)) { //獲取Attribute String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //解析前處理, 內容null 留個子類實現 preProcessXml(root); //解析 parseBeanDefinitions(root, this.delegate); //解析后處理, 內容null 留個子類實現 postProcessXml(root); this.delegate = parent; }
在doRegisterBeanDefinitions煩那個發中驗證xml的namespace,最重要的方法是parseBeanDefinitions,parseBeanDefinitions方法進行了解析操作;
parseBeanDefinitions方法的源碼:
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //驗證xml namespace, BeanDefinitionParserDelegate.BEANS_NAMESPACE_URI if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { //對默認標簽處理 parseDefaultElement(ele, delegate); } else { //對自定義標簽處理 delegate.parseCustomElement(ele); } } } } else { //對自定義標簽處理 delegate.parseCustomElement(root); } }
parseBeanDefinitions方法中已經開始對標簽進行解析,區分默認標簽和自定義標簽,我們本次只對默認標簽的源碼進行解析,自定義標簽自行DeBug,
parseDefaultElement方法的源碼:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //解析import標簽 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //解析alias標簽并注冊 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //解析bean標簽并注冊 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } //解析beans標簽 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
到這里我們可以看到,spring對import/bean/alias/beans的解析過程,對于beans的解析無法就是解析beans中的bean標簽,spring直接又重新調用了doRegisterBeanDefinitions方法,我們接下來進行對bean標簽的解析;
processBeanDefinition方法:
/** * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //委托BeanDefinitionParserDelegate的parseBeanDefinitionElement方法進行元素解析并返回 //BeanDefinitionHolder實例,BeanDefinitionHolder已經包含了配置文件中的各種屬性 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); //當BeanDefinitionHolder返回不null的情況,弱存在默認標簽下的子標簽再有自定義的屬性,還需要再次解析 if (bdHolder != null) { //解析默認標簽中的自定義標簽 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. // 進行實例注冊注冊操作是BeanDefinitionReaderUtisl.registerBeanDefinition進行處理 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name "" + bdHolder.getBeanName() + """, ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
在processBeanDefinition方法中,spring做了兩件事情:1:委托BeanDefinitionParserDelegate的parseBeanDefinitionElement方法進行元素解析并返回BeanDefinitionHolder實例,BeanDefinitionHolder已經包含了配置文件中的各種屬性
2:通過上獲得的BeanDefinitionHolder進行bean的注冊操作,通BeanDefinitionReaderUtils.registerBeanDefinition方法;
通過delegate.parseBeanDefinitionElement方法進行xml解析:
/** * Parses the supplied {@code} element. May return {@code null} * if there were errors during parse. Errors are reported to the * {@link org.springframework.beans.factory.parsing.ProblemReporter}. */ @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { //解析id屬性 String id = ele.getAttribute(ID_ATTRIBUTE); //解析name屬性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); //分割name屬性 List aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isTraceEnabled()) { logger.trace("No XML "id" specified - using "" + beanName + "" as bean name and " + aliases + " as aliases"); } } if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } //將信息封裝到 beanDefinition中 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { //beanname不存在則使用默認規則創建 if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isTraceEnabled()) { logger.trace("Neither XML "id" nor "name" specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
在parseBeanDefinitionElement方法中做了三件事:1:解析id/name;
2:檢查name的唯一性;
3:將信息封裝到 beanDefinition中,接下來直接看parseBeanDefinitionElement方法;
parseBeanDefinitionElement源碼:
/** * Parse the bean definition itself, without regard to name or aliases. May return * {@code null} if problems occurred during the parsing of the bean definition. */ @Nullable public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; //解析classname屬性 if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; //解析parent屬性 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { //創建用于承載屬性的AbstractBeanDefinition類型的 AbstractBeanDefinition bd = createBeanDefinition(className, parent); //解析bean的各種屬性 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); //提取description bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //解析meta (元數據) parseMetaElements(ele, bd); //解析Lookup-method 書中53頁有使用方法 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //解析replaced-method 書中55頁有使用方法 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //構造函數 參數 //解析constructor-arg 書中replaced-method后邊 parseConstructorArgElements(ele, bd); //解析Property 書中replaced-method后邊 parsePropertyElements(ele, bd); //解析Qualifier 書中Property后邊 parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } return null; }
通過上述代碼我們可以看到這里首先是實例化了一個AbstractBeanDefinition來承載各種xml屬性,接下來通過parseBeanDefinitionAttributes方法解析了xml中的各種你屬性值,然后在解析lookUp-method(方法注入),replaced-method(替換方法或方法返回值),構造函數參數constructor-arg,property屬性,Qualifier屬性等;上述方法的源碼就不一一展示了,無非都是通過Element進行解析;
接下來看真正注冊的代碼 BeanDefinitionReaderUtils.registerBeanDefinition
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //beanName不可為空 Assert.hasText(beanName, "Bean name must not be empty"); //beanDefinition不可為空 Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { //校驗 MethodOverrides,MethodOverrides在解析并組裝beanDefinition時有提到 ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } //beanDefinitionMap 存儲實例的全局Map 使用ConcurrentHashMap 線程安全 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); //如果已經注冊 處理內容 if (existingDefinition != null) { //是否覆蓋 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean "" + beanName + "" with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean "" + beanName + "" with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean "" + beanName + "" with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } this.beanDefinitionMap.put(beanName, beanDefinition); } else { //判斷是否已經至少創建過一次 使用AbstractBeanFactory.alreadyCreated來判斷 if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); ListupdatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // 仍處于啟動注冊階段 // 注冊 beanDefinitionMap 新實例 beanName + beanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); // 增加beanDefinitionNames this.beanDefinitionNames.add(beanName); // 清除緩存 this.manualSingletonNames.remove(beanName); } // 清除緩存 this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
上述代碼中首先驗證了beanName和BeannDefinition不可為空,然后繼續校驗了MethodOverridesMethodOverrides在解析并組裝beanDefinition時lookup-method和recpse-method的源碼中有提到,繼續判斷beanDefinitionMap是否存在該bean,如果bean已經存在,通過allowBeanDefinitionOverriding屬性判斷是否可覆蓋,反之則拋出異常;如果不存在則需要判斷本次是否是第一次注冊bean,如果是則初始化beanDefinitionMap后進行put操作,反之直接put beanDefinitionMap完成注冊;
至此我們已經看完了整個XmlBeanFactory的xml解析和注冊的源碼部分,相信看本篇文章無法真正理解,還需要讀者下載源碼使用debug運行,再結合本篇文章的注釋,相信會很容易理解,碼字不易,轉發請注明出處:https://blog.csdn.net/qq_3025...
博客地址:https://lantaogithub.github.io
博客地址:https://lantaogithub.github.io
簡書:https://www.jianshu.com/u/bfb...
CSDN:https://blog.csdn.net/qq_3025...
開源中國:https://my.oschina.net/u/3948555
掘金:https://juejin.im/user/5c8c6f...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76208.html
摘要:對于開發者來說,無疑是最常用也是最基礎的框架之一。概念上的東西還是要提一嘴的用容器來管理。和是容器的兩種表現形式。定義了簡單容器的基本功能。抽象出一個資源類來表示資源調用了忽略指定接口的自動裝配功能委托解析資源。 對于Java開發者來說,Spring無疑是最常用也是最基礎的框架之一。(此處省略1w字吹Spring)。相信很多同行跟我一樣,只是停留在會用的階段,比如用@Component...
摘要:在上文中,我實現了一個很簡單的和容器。比如,我們所熟悉的就是在這里將切面邏輯織入相關中的。初始化的工作算是結束了,此時處于就緒狀態,等待外部程序的調用。其中動態代理只能代理實現了接口的對象,而動態代理則無此限制。 1. 背景 本文承接上文,來繼續說說 IOC 和 AOP 的仿寫。在上文中,我實現了一個很簡單的 IOC 和 AOP 容器。上文實現的 IOC 和 AOP 功能很單一,且 I...
plantuml code @startuml xmlBeanFactory: XmlBeanFactory -> reader:XmlBeanDefinitionReader : 1: loadBeanDefinitions(resource) activate xmlBeanFactory: XmlBeanFactory activate reader:XmlBeanDefinitionRe...
摘要:動態地代理,可以猜測一下它的含義,在運行時動態地對某些東西代理,代理它做了其他事情。所以動態代理的內容重點就是這個。所以下一篇我們來細致了解下的到底是怎么使用動態代理的。 之前講了《零基礎帶你看Spring源碼——IOC控制反轉》,本來打算下一篇講講Srping的AOP的,但是其中會涉及到Java的動態代理,所以先單獨一篇來了解下Java的動態代理到底是什么,Java是怎么實現它的。 ...
摘要:概述約定大于配置的功力讓我們如沐春風,在我之前寫的文章從到也對比過和這兩個框架,不過最終以超高的代碼信噪比和易上手性讓我們映像頗深。至于,我想在非時代大家應該不陌生吧,作用是配置容器,也即形式的容器的配置類所使用。 showImg(https://segmentfault.com/img/remote/1460000015822144); 概 述 SpringBoot 約定大于配置...
閱讀 2336·2021-11-23 09:51
閱讀 1137·2021-11-22 13:52
閱讀 3611·2021-11-10 11:35
閱讀 1187·2021-10-25 09:47
閱讀 2994·2021-09-07 09:58
閱讀 1059·2019-08-30 15:54
閱讀 2817·2019-08-29 14:21
閱讀 3025·2019-08-29 12:20