Adolph
Engineer
Engineer
  • UID623
  • Fans2
  • Follows1
  • Posts72
Reads:1047Replies:0

Spring source code profiling - Bean configuration and startup (1)

Created#
More Posted time:Sep 23, 2016 15:54 PM
About IOC
I believe most people have heard about IOC and Bean when they are studying Spring. There must be a well-justified reason for IOC to show up so frequently in Spring learning. To metaphorically refer to bean as a leading role in Spring, IOC is the stage on which the leading role stages performances. Without IOC, bean will find no place to shine in the programming world. As an important member of Spring core components, IOC deserves our efforts to explore its internal implementation so as to facilitate programming and understand the inside story of Spring. Next, I will analyze how IOC is implemented from the source code perspective step by step.
Interface design
First, let’s take a look at the overall design architecture of IOC through an interface design drawing


Source code profiling
BeanFacory
From the above interface design drawing, we can see that the most basic IOC container interface in Spring is BeanFactory. Some most basic operations and functions are defined in this interface for the IOC container. Let’s look at its source code:
package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;

/**
 * BeanFactory, as the most primitive and most important IOC container, mainly serves to support dependency injection (DI). Only a series of interface methods are defined here for BeanFactory and related interfaces. Through the series of BeanFactory interfaces, you can conveniently get the desired bean from the IOC container through different bean retrieval methods, and the specific IOC container implementation is ignored. From this perspective, these retrieval methods represent the most basic container entries.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 13 April 2001
 */
public interface BeanFactory {

    /**
     * Conversion delimiter “&” is used to reference the instance or distinguish the bean generated by the factory with the instance, that is, if a FactoryBean is named “a”, “&a” will help you to get the Factory.
     * FactoryBean and BeanFactory are the most frequently used classes in Spring. They are similar to each other in spelling. One is Factory, that is, the IOC container or object factory; the other is bean. In Spring, all the beans are managed by BeanFactory (that is, the IOC container). But for FactoryBean, the bean is not a simple one, but a factory one that can produce or modify object generation. The factory mode of its implementation and design mode is similar to the modifier mode.
     */
    String FACTORY_BEAN_PREFIX = "&";

    /**
     * There are five different forms of getBean methods to get the instance
     * @param name The bean name used for retrieval
     * @return Object (<T> T) The instance object
     * @throws BeansException If the bean cannot be got
     */
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    /**
     * Let the user judge whether the container has a bean with the designated name.
     * @param name The bean name used for search
     * @return boolean Whether included
     */
    boolean containsBean(String name);

    /**
     * Check whether the bean with the designated name is a Singleton-type bean.
     * You can designate the Singleton attribute in BeanDefinition.
     * @param name The bean name used for search
     * @return boolean Whether it is Singleton
     * @throws NoSuchBeanDefinitionException No such beans found
     */
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    /**
     * Check whether the bean with the designated name is a Prototype-type bean.
     * Like Singleton attribute, you can designate the attribute in BeanDefinition.
     * @param name The bean name used for search
     * @return boolean Whether it is Prototype
     * @throws NoSuchBeanDefinitionException No such beans found
     */
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    /**
     * Query whether the class of the bean with a designated name is of the desired class type.
     * @param name The bean name used for search
     * @param typeToMatch Match type
     * @return boolean Whether it is of the desired type
     * @throws NoSuchBeanDefinitionException No such beans found
     */
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    /**
     * Query the class of the bean with a designated name.
     * @param name The bean name used for search
     * @return The specified bean or null (no matching beans found)
     * @throws NoSuchBeanDefinitionException No such beans found
     */
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    /**
     * Query all the alias of the bean with a designated name, and these are all defined in BeanDefinition.
     * @param name The bean name used for search
     * @return All the alias of the bean with a designated name, or a null array
     */
    String[] getAliases(String name);
}


Through the interface above, we can see that BeanFactory only defines the most basic behavior of IOC container, regardless of how the bean is defined and loaded. If we want to know the specific process of a factory producing objects, we need to check the implementation classes of the interface. In Spring, there are many sub-classes for implementing this interface. We can take a look at the code of a common implementation class XmlBeanFactory.
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;

/**
 * XmlBeanFactory is the most simple implementation class of BeanFactory.
 *
 * XmlBeanFactory functions are based on DefaultListableBeanFactory container, and additional functions such as XML reading are also implemented on the basic container. XmlBeanFactory adopts DefaultListableBeanFactory as the basic class and DefaultListableBeanFactory is a very important IOC implementation which will be detailed in the next chapter.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 15 April 2001
 */
public class XmlBeanFactory extends DefaultListableBeanFactory {

    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

    /**
     * Create a XmlBeanFactory according to the given source.
     * @param resource  The abstract of Spring for external resources. The most common one is for files, especially XML files. In addition, Resource usually stores the bean definition of Spring users, for example when applicationContext.xml is loaded, it is abstracted to a resource for processing.
     * @throws BeansException Errors in loading or parsing.
     */
    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }

    /**
     * Create a XmlBeanFactory according to the given source and BeanFactory.
     * @param resource  The abstract of Spring for external resources. The most common one is for files, especially XML files. In addition, Resource usually stores the bean definition of Spring users, for example when applicationContext.xml is loaded, it is abstracted to a resource for processing.
     * @param parentBeanFactory The BeanFactory of the parent class
     * @throws BeansException Errors in loading or parsing.
     */
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader.loadBeanDefinitions(resource);
    }
}


We can see that to view the object to construct XmlBeanFactory, we need a **Resource** object. What is this Resource?
Simply put, Resource interface aims to provide a more powerful abstracting capability for accessing underlying resources and it is the most basic interface for Spring to access resources.
Through XmlBeanFactory we can also guess that the Resource object that this class passes is an xml file stream. So now we know the usage of the simple implementation class of BeanFactory and the parameters that construct this object. Next, let’s create a most primitive IOC container on our own.
Implement a most primitive IOC container using XmlBeanFactory
Preparation
1. Entity class User.java
public class User{
    private String name;
    private int age;

    getter();
    setter();
}


2. XML resource file beans.xml
<bean id="user1" name="user1" class="com.yanxiao.ioc.User">
        <property name="name" value="yanxiao"></property>
        <property name="age" value="20"></property>
    </bean>


Use of the most primitive IOC container
package com.yanxiao.ioc;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

/**
 * Created by yanxiao on 2016/7/28.
 */
@SuppressWarnings("deprecation")
public class MySimpleBeanFactory {
    public static void main(String[] args) {
        ClassPathResource resource = new ClassPathResource("META-INF/beans.xml");
        BeanFactory beanFactory = new XmlBeanFactory(resource);
        User user = beanFactory.getBean("user1", User.class);
        System.out.println(user.getName()+" : "+user.getAge());
    }
}


Follow the code procedure above
We can use single-step debugging to understand the major internal call procedures of the code above. Here we didn’t include the getBean() method process and the bean parsing designing process after the step. They will be discussed later.


Through the process above, we can summarize the IOC container usage steps as below:
    1. Create the abstract resource for IOC configuration file. The abstract resource should contain BeanDefinition definition information.
    2. Create a BeanFactory. Here we used DefaultListableBeanFactory.
    3. Create a reader for loading BeanDefinition. Here we use XmlBeanDefinitionReader to load BeanDefinition in the XML file form.
    4. Then we configure the located resource to BeanFactory through a callback.
    5. Read the configuration information from the located resource location. The specific parsing process is completed by XmlBeanDefinitionReader.
    6. After the bean definition loading and registering are complete, the desired IOC container is basically set up.
After we understand the internal process, we can apply it in practice. The MySimpleBeanFactory code we wrote previously can be changed to the following form:
public static void main(String[] args) {
        ClassPathResource resource = new ClassPathResource("META-INF/beans.xml");
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions(resource);
        User user = factory.getBean("user", User.class);
        System.out.println(user.getName()+" : "+user.getAge());
    }


The two are of equivalent effects.
Now we have gained a rough idea about IOC. Next we can tap into some key points and details. First, starting with the ClassPathResource object we created in the first step, we can learn more about the resource in Spring.
Resource interface system
In the articles above, we also mentioned the roles of Resource interface. In fact, Spring encapsulates the resources needed in the entire framework in a unified way using its own abstract structure. We can think of the reason for encapsulating this interface. During our programming, there are a wide variety of resource file types, including File, InputStream, UrlResource and ContextResource. For so many resource types, if a set of mature and reasonable resource encapsulation system is absent in the framework, it will undoubtedly add much complexity to framework implementation and developer programming. The encapsulation and inheritance features of object-oriented programming are fully embodied in Spring. If you have time, you can try to feel the benefits of Spring framework for programming of extensible and usable code.
Continuing from the above introduction to Resource interface, the interface, as a primitive encapsulation of resources, inherits from **InputStreamResource** interface. InputStreamResource only has one InputStream getInputStream() method. The inheritance grants Resource interface the feature of InputStream through Resource objects. Next, let’s take a look at the interface source code:
InputStreamResource interface
package org.springframework.core.io;

import java.io.IOException;
import java.io.InputStream;

/**
 * @author Juergen Hoeller
 * @since 20.01.2004
 */
public interface InputStreamSource {

    /**
     * Return the InputStream class, such as the resource and Byte Array under File and Classpath.
     * @return InputStream Return a new InputStream object
     * @throws IOException
     */
    InputStream getInputStream() throws IOException;
}


Resource interface
package org.springframework.core.io;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;

/**
 * Resource interface abstracts all the underlying resources used in Spring: File, URL and Classpath.
 * Meanwhile, for resource files with different sources, Resource has different implementation: files (FileSystemResource), Classpath resources (ClassPathResource), URL resources (UrlResource), InputStream resources (InputStreamResource), Byte arrays (ByteArrayResource) and so on.
 *
 * @author Juergen Hoeller
 * @since 28.12.2003
 */
public interface Resource extends InputStreamSource {

    /**
     * Judge whether the resource exists.
     * @return boolean Whether the resource exists.
     */
    boolean exists();

    /**
     * Judge whether the resource is readable.
     * @return boolean Whether the resource is readable.
     */
    boolean isReadable();

    /**
     * Whether enabled
     * @return boolean Whether enabled.
     */
    boolean isOpen();

    /**
     * Get the URL type resource for resource conversion.
     * @return URL Get the URL type
     * @throws IOException If the resource cannot be opened, an exception will be thrown out.
     */
    URL getURL() throws IOException;

    /**
     * Get the URI type resource for resource conversion.
     * @return URI Get the URI type
     * @throws IOException If the resource cannot be opened, an exception will be thrown out.
     */
    URI getURI() throws IOException;

    /**
     * Get the File type resource for resource conversion.
     * @return File Get the File type
     * @throws IOException If the resource cannot be opened, an exception will be thrown out.
     */
    File getFile() throws IOException;

    /**
     * Get resource length
     * @return long Resource length
     * @throws IOException If the resource cannot be opened, an exception will be thrown out.
     */
    long contentLength() throws IOException;

    /**
     * Get the lastModified attribute
     * @return long Get lastModified
     * @throws IOException If the resource cannot be opened, an exception will be thrown out.
     */
    long lastModified() throws IOException;

    /**
     * Create a relative resource method
     * @param relativePath Relative path
     * @return Resource Return a new resource
     * @throws IOException If the resource cannot be opened, an exception will be thrown out.
     */
    Resource createRelative(String relativePath) throws IOException;

    /**
     * Get file name
     * @return String File name or null
     */
    String getFilename();

    /**
     * Get error processing information, mainly used for error processing information printing
     * @return String Error resource information
     */
    String getDescription();
}


ClassPathResource
Let’s look at some attention-deserving source code we used above of the implementation class ClassPathResource of Resource interface.
package org.springframework.core.io;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 28.12.2003
 * @see ClassLoader#getResourceAsStream(String)
 * @see Class#getResourceAsStream(String)
 */
public class ClassPathResource extends AbstractFileResolvingResource {

    private final String path;
    private ClassLoader classLoader;
    private Class<?> clazz;
    /**
    Create a ClassPathResource object through the resource path
     */
    public ClassPathResource(String path) {
        this(path, (ClassLoader) null);
    }

    /**
     The specific implementation of the above constructor
     */
    public ClassPathResource(String path, ClassLoader classLoader) {
        Assert.notNull(path, "Path must not be null");
        String pathToUse = StringUtils.cleanPath(path);
        //Remove the preceding /
        if (pathToUse.startsWith("/")) {
            pathToUse = pathToUse.substring(1);
        }
        this.path = pathToUse;
        this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
    }

    /**
     * Create a new {@code ClassPathResource} for {@code Class} usage.
     * The path can be relative to the given class, or absolute within the classpath via a leading slash.
     * @param path relative or absolute path within the class path
     * @param clazz the class to load resources with
     * @see java.lang.Class#getResourceAsStream
     */
    public ClassPathResource(String path, Class<?> clazz) {
        Assert.notNull(path, "Path must not be null");
        this.path = StringUtils.cleanPath(path);
        this.clazz = clazz;
    }


    /**
    Get the specific implementation of InputStream
     * This implementation opens an InputStream for the given class path resource.
     * @see java.lang.ClassLoader#getResourceAsStream(String)
     * @see java.lang.Class#getResourceAsStream(String)
     */
    @Override
    public InputStream getInputStream() throws IOException {
        InputStream is;
        if (this.clazz != null) {
            is = this.clazz.getResourceAsStream(this.path);
        }
        else if (this.classLoader != null) {
            is = this.classLoader.getResourceAsStream(this.path);
        }
        else {
            is = ClassLoader.getSystemResourceAsStream(this.path);
        }
        if (is == null) {
            throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
        }
        return is;
    }


    /**
    Get the file name
     * This implementation returns the name of the file that this class path resource refers to.
     * @see org.springframework.util.StringUtils#getFilename(String)
     */
    @Override
    public String getFilename() {
        return StringUtils.getFilename(this.path);
    }
}



DefaultListableBeanFactory class
We have understood the resource-layer interface and implementation. Next we will analyze the DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); statement in the code. Here we introduce a new class DefaultListableBeanFactory. From the IOC class diagram, we can see this is a default implementation class of BeanFactory and its instance object can be used as an IOC container for independent use. We can regard the object as a container to accommodate bean objects. After a well-defined bean object is loaded and processed by Spring, it is delivered to the factory for management. We can get the bean object from the factory as needed.
We call a no-argument constructor to create the factory object to see what the underlying layer has done.
@SuppressWarnings("serial")
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

    /**
     * Create a new DefaultListableBeanFactory.
     */
    public DefaultListableBeanFactory() {
        super();//Call a no-argument constructor of the parent class
    }
}


Next we will track the source code in the parent class **AbstractAutowireCapableBeanFactory**
AbstractAutowireCapableBeanFactory class
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {

    /**
     * Create a new AbstractAutowireCapableBeanFactory.
     */
    public AbstractAutowireCapableBeanFactory() {
        super();//The code of the no-argument constructor of the parent class AbstractBeanFactory can be found below↓↓↓
        ignoreDependencyInterface(BeanNameAware.class);
        ignoreDependencyInterface(BeanFactoryAware.class);
        ignoreDependencyInterface(BeanClassLoaderAware.class);
    }
}


AbstractBeanFactory class
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    /**
     * Create a new AbstractBeanFactory.
     */
    public AbstractBeanFactory() {
        //Nothing is done here
    }
}
Guest