Digression:
Interface & polymorphism
I have a bike and ride it to work every day
package com.zhao.SpringIoc; public class Bike { public void go() { System.out.println("Go to work by bike"); } }
package com.zhao.SpringIoc; /** * Version 1.0: handsome Zhao goes to work by bike */ public class ShuaiQiZhao { private static Bike bike; // private static Bus bus; // private static Car car; public static void main(String[] args) { bike = new Bike(); bike.go(); // Go to work by bike } }
After a while, I didn't want to feel too tired by bike, so I took the bus to work
package com.zhao.SpringIoc; public class Bus { public void go() { System.out.println("Take the bus to work"); } }
package com.zhao.SpringIoc; /** * Version 2.0: handsome Zhao goes to work by bus */ public class ShuaiQiZhao { // private static Bike bike; private static Bus bus; // private static Car car; public static void main(String[] args) { bus = new Bus(); bus.go(); // Take the bus to work } }
Before long, I had money and bought a car, so I drove to work every day
package com.zhao.SpringIoc; public class Car { public void go() { System.out.println("Drive Martha to work"); } }
package com.zhao.SpringIoc; /** * Version 3.0: handsome Zhao drives Martha to work */ public class ShuaiQiZhao { // private static Bike bike; // private static Bus bus; private static Car car; public static void main(String[] args) { car = new Car(); car.go(); // Drive Martha to work } }
When I buy a vehicle, I have to modify the member variable in the Person class and the vehicle in the main method. Some people think it's nothing? That's because you didn't write the code. It's more comfortable to see than to do! And if there were a thousand kinds of transportation, would you still say that?
Use polymorphism to improve the above situation
The housekeeper manages these vehicles
package com.zhao.SpringIoc; public interface Movable { public void go(); // Abstract the common behavior of three vehicles (going to work) }
Three vehicles implement this interface
package com.zhao.SpringIoc.Impl; import com.zhao.SpringIoc.Movable; public class Bike implements Movable { public void go() { System.out.println("Go to work by bike"); } }
package com.zhao.SpringIoc.Impl; import com.zhao.SpringIoc.Movable; public class Bus implements Movable { public void go() { System.out.println("Take the bus to work"); } }
package com.zhao.SpringIoc.Impl; import com.zhao.SpringIoc.Movable; public class Car implements Movable { public void go() { System.out.println("Drive Martha to work"); } }
Handsome Zhao found the housekeeper
package com.zhao.SpringIoc; import com.zhao.SpringIoc.Impl.Car; /** * Version 4.0: handsome Zhao drives Martha to work */ public class ShuaiQiZhao { private static Movable movable; public static void main(String[] args) { movable = new Car(); movable.go(); // Drive Martha to work } }
In contrast, in the past, if you wanted to use a vehicle, you had to declare its member variables and modify the class name and reference name of the main method.
Now just change the class name, isn't it very convenient?
But this is not the most convenient, that is, it has its own new object every time. But this is also helpless. Is there really no other way?
yes
What spring is good at is management. You can manage what objects you need. Traditional programming requires manual creation of various objects and internal control of objects. Spring gives the designed objects to container management (IOC). If you need anything, you can find it.
text
Spring objects are generated from XML documents or annotations. Let's not talk about annotations. It's too far from us. Let's start with XML.
Build environment
Interface: BookService
package com.zhao.Interface; public interface BookService { /** * Book price * @return */ double getBookPrice(); }
Interface: PressService
package com.zhao.Interface; public interface PressService { /** * Return a sentence * @return */ String say(); }
Interface implementation class: BookServiceImpl
package com.zhao.service; import com.zhao.Interface.BookService; public class BookServiceImpl implements BookService { @Override public double getBookPrice() { return 508.8; } }
Interface implementation class: PressServiceImpl
package com.zhao.service; import com.zhao.Interface.BookService; import com.zhao.Interface.PressService; public class PressServiceImpl implements PressService { /** * Dependent BookService */ private BookService bookService; /** * Place of dependency injection * * @param bookService */ public void setBookService(BookService bookService) { this.bookService = bookService; } @Override public String say() { return "The price of this book is:" + bookService.getBookPrice(); } }
Configuration file: applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <!--BookService--> <bean id="bookService" class="com.zhao.service.BookServiceImpl"> </bean> <!--PressService--> <bean id="pressService" class="com.zhao.service.PressServiceImpl"> <!--Set dependencies: pressService rely on pressService--> <property name="bookService" ref="bookService"></property> </bean> </beans>
Startup class: SourceCodeLearning
package com.zhao; import com.zhao.Interface.PressService; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class SourceCodeLearning { public static void main(String[] args) { //Get the bean named user from the container BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); PressService pressService = (PressService) bf.getBean("pressService"); //Method of calling bean String price = pressService.say(); System.out.println(price); } }
All right, here we go!
Let's start with the result analysis process. Now you think of yourself as Spring. What's the first thing to do? Load the configuration file to see which beans need to be created? Everything is difficult at the beginning. After taking the first step, it's easy to do now!
- Load profile
- Parsing configuration files
- Put the parsed Bean information into the Map collection
Load profile
I'm Spring now. I don't know what I'm going to face next. I don't know whether to load Xml files, JSON files, or yaml files. But I don't panic at all. Every configuration file should implement my rules, that is Resource, which is now an Xml file, is used to load the Xml file XmlBeanFactory
package org.springframework.beans.factory.xml; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.core.io.Resource; @Deprecated @SuppressWarnings({"serial", "all"}) public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } }
stay What really works in the XmlBeanFactory class is XmlBeanDefinitionReader (XBDR), which reads the beans in the Xml configuration file to me. At first I was curious about how it read, but I saw its father's friend Lao Wang ResourceLoader, I knew Uncle Wang must have taught him his housekeeping skills. It wasn't long before xbdr He came back with a pile of data, but no one could understand the dense data, so XBDR Invite your good friends DocumentLoader Help translate it into Doc documents (Java documents) and documentloader Invite your good friends again Bean definitionparserdelegate for label resolution.
Since the process of loading and parsing is not important, only the flow chart is shown here. Specific source code, Debgger Down. We are more concerned about where the parsed JavaBeans are stored and how the Ioc container is created!
Parsing of default tags by BeanDefinitionParserDelegate
package org.springframework.beans.factory.xml; public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader { // BDDR's good friend, parsing Tags @Nullable private BeanDefinitionParserDelegate delegate; private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // Parse import tag importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // Parsing alias Tags processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // Parsing bean Tags processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // Parsing beans Tags doRegisterBeanDefinitions(ele); } } // Parsing bean Tags protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // Through this method, the bdHolder instance already contains various attributes configured in the configuration file, such as Class, name and id BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { //If there are custom attributes under the child nodes of the default label, the custom label will be resolved bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { //After parsing, register the parsed bdHolder BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } //Notify the relevant listener that the bean has been loaded getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } } }
The source code of parsing tags should be simplified? Otherwise, the dense code looks like a headache!
public class BeanDefinitionParserDelegate { @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // Resolution ID String id = ele.getAttribute(ID_ATTRIBUTE); // Resolve name String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // Call the parseBeanDefinitionElement method to resolve other properties AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; } @Nullable public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { //Create a GenericBeanDefinition to host attributes, which corresponds to the Bean tag in the xml file AbstractBeanDefinition bd = createBeanDefinition(className, parent); ... return bd; } finally { this.parseState.pop(); } return null; } }
Here we focus on < Constructor Arg > tag, this One is related to dependency injection, so the single actor comes out to explain.
< constructor Arg > tag
// Traverse, extract all constructor args, and then parse them public class BeanDefinitionParserDelegate { @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) { NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) { // Implement the specific parsing process parseConstructorArgElement((Element) node, bd); } } } // Implement the specific parsing process public void parseConstructorArgElement(Element ele, BeanDefinition bd) { // Extract index attribute String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); // Extract type attribute String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); // Extract name attribute String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(indexAttr)) { try { int index = Integer.parseInt(indexAttr); if (index < 0) { error("'index' cannot be lower than 0", ele); } else { try { Object value = parsePropertyValue(ele, bd, null); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder); } finally { this.parseState.pop(); } } } catch (NumberFormatException ex) { error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele); } } else { try { this.parseState.push(new ConstructorArgumentEntry()); Object value = parsePropertyValue(ele, bd, null); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); } finally { this.parseState.pop(); } } } }
The code above roughly means,
If the index is specified in the configuration file, the operation steps are as follows.
- analysis Child element of constructor ARG
- use ConstructorArgumentValues.ValueHolder type to encapsulate the resolved elements.
- Encapsulate the type, name and index in the ConstructorArgumentValues.ValueHolder type and add them to the current Of BeanDefinition Of ConstructorArgumentValues IndexedArgumentValue property.
If the index attribute is not specified, the operation steps are as follows
- analysis Child element of constructor ARG
- use ConstructorArgumentValues.ValueHolder type to encapsulate the resolved elements.
- Encapsulate the type, name and index in the ConstructorArgumentValues.ValueHolder type and add them to the current Of BeanDefinition Of ConstructorArgumentValues In the GenericArgumentValue property.
After the above tags are parsed, all you have to do is register. That is, DefaultBeanDefinitionDocumentReader Method in class BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition oldBeanDefinition; oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (this.logger.isWarnEnabled()) { this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(oldBeanDefinition)) { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (oldBeanDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } } }
Register the bean in the register, with beanName as the key and beanDefinition as the value, and register it in the register(Map) collection.
The above code roughly means
- check
- Handle the case where beanName has been registered. If the override of the bean is not allowed, you need to throw an exception, otherwise you can override it directly
- Add map cache
- Clear the cache of the corresponding beanName left before resolution
Notification listener resolution
Method getReaderContext().fireComponentRegistered(new) BeanComponentDefinition(bdHolder)); Implement notification listener parsing. spring currently does not do any logical processing for this event.