There are a lot of spring source code analysis tutorials and class diagrams on the Internet, but the source code of spring is very complex, one layer is nested, and it's hard to see at the beginning. The more you look, the more muddled you become. So I bought the book "spring source code deep analysis". Follow the book to learn.
First, spring reads the configuration file. Spring has a top-level interface, resource, that reads the configuration. Suppose we use ClassPathResource to instantiate resource.
//Get an instance of resource ClassPathResource resource=new ClassPathResource("beans.xml");
Then, since you have a resource instance, you need to analyze the contents. When we often use spring, we use the form of xml (regardless of springBoot), so the corresponding factory should be xmlFactory.
ClassPathResource resource=new ClassPathResource("beans.xml"); //Here XmlBeanFactory is out of date in 4.3.4 XmlBeanFactory factory=new XmlBeanFactory(resource); //The above two lines of code can be replaced by the following, which roughly means to synthesize two steps into one step /* *BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml"); */ //Since the book starts with xmlBeanFactory, we use xmlBeanFactory to start the analysis //Go to the xmlBeanFactory constructor. public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { //It doesn't matter here super(parentBeanFactory); //The name of the method is used to load the bean this.reader.loadBeanDefinitions(resource); } //Then go to the loadbean definitions (resource) method @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource));//(1) } //Again, key code //getResource is to get the resource in (1) again. It may be that the resource has coding requirements InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //Continue to enter return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } //--------------------------------------------------------------------------------------- protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //Read method, return type, roughly guess, it should be to parse xml. Return the parsed doc Document doc = doLoadDocument(inputSource, resource); //After getting the correct doc, you should register and load the bean (play) return registerBeanDefinitions(doc, resource); } //Omit many catch statements: throw errors after parsing failure }
Next let's look at the play: register and load the bean s.
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //Instantiate BeanDefinitionDocumentReader with DefaultBeanDefinitionDocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //Read method name: count the number of previous registrations int countBefore = getRegistry().getBeanDefinitionCount(); //Load and register bean s documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//(1) return getRegistry().getBeanDefinitionCount() - countBefore; } //Continue to (1) public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); //root: get node Element root = doc.getDocumentElement(); //Ha ha, it should be the core, the real load bean doRegisterBeanDefinitions(root); } protected void doRegisterBeanDefinitions(Element root) { BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); //profile processing, determine what environment: this achieves the effect of different environments, different configuration files. if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //Parsing preprocessing: empty implementation preProcessXml(root); //I'll go. I haven't registered bean yet. It's really deep. Then look parseBeanDefinitions(root, this.delegate); //Post parsing: empty implementation postProcessXml(root); this.delegate = parent; } protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 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)) { //Processing bean parseDefaultElement(ele, delegate); } else { //Processing bean delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }