12
头图

Author: little brother Fu
Blog: https://bugstack.cn

Precipitate, share, and grow, so that you and others can gain something! 😄

I. Introduction

If you ask a question, you must give a solution!

Recently, there have been feedback from small fans, that communication with their superiors always encounters obstacles, and they feel that they are not understood. Most of the time the things he put forward may be said by the leader: "I didn't get your point", "The project you want to do has no business value", "If you raise a problem, you must give a solution." And so on and so on.

In view of the specific circumstances that require specific analysis, we may not necessarily be able to determine whose problem is, which leads to disagreements in each conversation. It may be that the leader has the difficulties and perspectives of the leader, or it may be that the employees have the understanding and ideas of the employees, so there is no agreement in the end.

But in terms of leading the team, effective communication is very important. It's like: if you're all right, then why am I arguing with you? Instead of suppressing the contradictions encountered, it is better to spread out the conversation. Whoever has a bigger perspective and mind will have more empathy.


If the sharp criticism disappears completely, the mild criticism will become harsh.

If mild criticism is not allowed, silence will be considered unpredictable.

If silence is no longer allowed, it would be a crime to praise not working hard enough.

If only one voice is allowed, then the only voice that exists is a lie.

2. Interview questions

Thanks for the plane, a small note! , I always feel that Spring has nothing to look at, how can the interviewer ask for flowers?

Interviewer : What is the role of transformedBeanName in Spring's getBean?

Xie : I don’t know, the meaning of the word seems to be to change the Bean name.

Interviewer : So, if your Bean has an alias, what should Spring do when it gets the Bean?

Thank you aircraft : This!

Interviewer : What if depends-on is used?

Thanks for the plane : Ah, I have never used depends-on, I don’t know!

Interviewer : When you debug the code, have you seen the & before BeanName? Why does it appear?

Thanks for the plane : I don't deserve to know! Goodbye!

Third, the acquisition process of Bean

For those who are new to the Spring source code, they may be wondering how to get a Bean with so many processes?

  • There may be Beans that may have aliases, may have dependencies, or may be packaged by BeanFactory, so there will be transformedBeanName to handle these differentiated behaviors.
  • Whether there is a circular dependency, whether there is a parent factory, whether it is a singleton or a prototype, whether it is lazy loading or preloading, whether it is in a buffer, so there are various combinations of judgments to make different processes.
  • Early exposure of objects, three-level cache, and clear post-marking. All optimization processing is to make the acquisition of the entire Bean more efficient.

So , it has become more and more complex in order to meet various needs. And the in-depth learning of this part of knowledge is definitely not just to deal with the eight-legged essay, but also to consider whether there is a general understanding of the process when encountering complex problems in daily Spring use, so that the problem can be quickly located, as well as the needs of such requirements. Whether the technical realization scheme can play a certain guiding role in the future application development, because it is a concrete realization of a design scheme.

1. getBean core flow chart

小傅哥,getBean 核心流程图

  • The whole picture is the classes involved in the getBean process and the methods and operations used in the core process. If you can understand the whole picture, then you basically understand the whole process of getBean.
  • This may be because the network Photo compression becomes clear, you can public concern number : bugstack wormhole stack , replies: artwork obtain.

Next , we will list the key codes about obtaining Bean instances for analysis in turn. Readers and partners can also look at the flowchart together, which will be more convenient to understand.

2. Where to read the source code of getBean

@Test
public void test_getBean() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");
    UserDao userDao = beanFactory.getBean("userDao", UserDao.class);
    logger.info("获取 Bean:{}", userDao);
}
  • The daily application to Spring development is basically based on annotations, and it is almost impossible to use beanFactory.getBean to obtain a Bean instance by yourself.
  • So if you can't find the entry to view the source code of getBean when you are studying, and it is not convenient to debug and familiar with the source code, you can write such a unit test class and click on getBean to read the source code.

3. GetBean source code global preview

source code location : AbstractBeanFactory -> getBean() -> doGetBean()

@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException { 
    // getBean 就像你的领导其实没做啥,都在 doGetBean 里
    return doGetBean(name, requiredType, null, false);
}
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
        throws BeansException {     
    
    // 处理别名BeanName、处理带&符的工厂BeanName
    final String beanName = transformedBeanName(name);
    Object bean;  

    // 先尝试从缓存中获取Bean实例,这个位置就是三级缓存解决循环依赖的方法
    Object sharedInstance = getSingleton(beanName);   

    if (sharedInstance != null && args == null) {
        if (logger.isDebugEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }        
        
        // 1. 如果 sharedInstance 是普通的 Bean 实例,则下面的方法会直接返回
        // 2. 如果 sharedInstance 是工厂Bean类型,则需要获取 getObject 方法,可以参考关于 FactoryBean 的实现类 
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    else {
        
        // 循环依赖有三种,setter注入、多实例和构造函数,Spring 只能解决 setter 注入,所以这里是 Prototype 则会抛出异常
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }    

        // 1. 父 bean 工厂存在
        // 2. 当前 bean 不存在于当前bean工厂,则到父工厂查找 bean 实例
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // 获取 name 对应的 beanName,如果 name 是以 & 开头,则返回 & + beanName
            String nameToLookup = originalBeanName(name);         
            
            // 根据 args 参数是否为空,调用不同的父容器方法获取 bean 实例
            if (args != null) {
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
        }       

        // 1. typeCheckOnly,用于判断调用 getBean 方法时,是否仅是做类型检查
        // 2. 如果不是只做类型检查,就会调用 markBeanAsCreated 进行记录
        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }
        try {    
    
            // 从容器 getMergedLocalBeanDefinition 获取 beanName 对应的 GenericBeanDefinition,转换为 RootBeanDefinition
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); 
            // 检查当前创建的 bean 定义是否为抽象 bean 定义
            checkMergedBeanDefinition(mbd, beanName, args);
            
            // 处理使用了 depends-on 注解的依赖创建 bean 实例
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {   
                    // 监测是否存在 depends-on 循环依赖,若存在则会抛出异常
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }       
                    
                    // 注册依赖记录
                    registerDependentBean(dep, beanName);
                    try {    
                        // 加载 depends-on 依赖(dep 是 depends-on 缩写)
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }  

            // 创建单例 bean 实例
            if (mbd.isSingleton()) {    

                // 把 beanName 和 new ObjectFactory 匿名内部类传入回调
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        try {    
                            // 创建 bean
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // 创建失败则销毁
                            destroySingleton(beanName);
                            throw ex;
                        }
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }      
            // 创建其他类型的 bean 实例
            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }
    // 如果需要类型转换,这里会进行操作
    if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
        try {
            return getTypeConverter().convertIfNecessary(bean, requiredType);
        }
        catch (TypeMismatchException ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to convert bean '" + name + "' to required type '" +
                        ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }    

    // 返回 Bean
    return (T) bean;
}

In summary, it is basically the core processing method involved in the getBean process, which basically includes;

  • TransformedBeanName, processing alias BeanName, processing factory BeanName with ampersand.
  • getSingleton, first try to get the Bean instance from the cache. This location is the way the three-level cache solves the circular dependency.
  • getObjectForBeanInstance, if sharedInstance is an ordinary Bean instance, the following method will return directly. In addition, sharedInstance is a factory bean type, you need to get the getObject method, you can refer to the implementation class of FactoryBean.
  • isPrototypeCurrentlyInCreation, there are three types of circular dependencies, setter injection, multi-instance and constructor. Spring can only solve setter injection, so if it is Prototype, it will throw an exception.
  • getParentBeanFactory, the parent bean factory exists, and the current bean does not exist in the current bean factory, then look for the bean instance in the parent factory.
  • originalBeanName, get the beanName corresponding to name, if name starts with &, return & + beanName
  • args != null, according to whether the args parameter is empty, call different parent container methods to obtain bean instances
  • !typeCheckOnly, typeCheckOnly, used to determine whether the getBean method is only used for type checking, if not only for type checking, markBeanAsCreated will be called to record
  • mbd.getDependsOn, which handles the dependency creation of a bean instance using the depends-on annotation
  • isDependent, to monitor whether there is a depends-on cyclic dependency, if it exists, an exception will be thrown
  • registerDependentBean, register dependency record
  • getBean(dep), load depends-on dependencies (dep is the abbreviation of depends-on)
  • mbd.isSingleton(), create a singleton bean instance
  • mbd.isPrototype(), create other types of bean instances
  • return (T) bean, return Bean instance

4. beanName conversion operation

processing ampersand: transformedBeanName() -> BeanFactoryUtils.transformedBeanName(name)

public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    String beanName = name;
    while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
    }
    return beanName;
}
  • Objects created with FactoryBean will use getBean(FACTORY_BEAN_PREFIX + beanName) to add & (String FACTORY_BEAN_PREFIX = "&")
  • Here is to use the while loop to gradually remove the &, as long as the first character to be intercepted is the ampersand, the interception continues in the loop. &&&userService -> &&userService -> &userService -> userService

alias conversion: transformedBeanName() -> canonicalName

public String canonicalName(String name) {
    String canonicalName = name;
    // Handle aliasing...
    String resolvedName;
    do {
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}
<bean id="userService" class="org.itstack.interview.UserService"/>
<alias name="userService" alias="userService-alias01"/>
<alias name="userService-alias01" alias="userService-alias02"/>
  • First of all, Spring does not use aliases as the keys in the Map for the storage of Beans, so when you encounter all aliases, you need to find the corresponding original names. If you know about this, if you don’t encounter such problems, you will know where to start and check
  • The do...while loop will continuously look for the name corresponding to the alias like a chain, until the current name has no alias, it will return the corresponding BeanName

5. depends-on depends on Bean

AbstractBeanFactory -> isDependent(beanName, dep) -> DefaultSingletonBeanRegistry

protected boolean isDependent(String beanName, String dependentBeanName) {
    synchronized (this.dependentBeanMap) {
        return isDependent(beanName, dependentBeanName, null);
    }
<bean id="userService" class="org.itstack.interview.UserService" depends-on="userDao"/>

<bean id="userDao" class="org.itstack.interview.UserDao"/>
  • isDependent deals with Bean definitions that use depends-on configuration.
private boolean isDependent(String beanName, String dependentBeanName, Set<String> alread
    if (alreadySeen != null && alreadySeen.contains(beanName)) {
        return false;
    }
    String canonicalName = canonicalName(beanName);
    Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
    if (dependentBeans == null) {
        return false;
    }
    if (dependentBeans.contains(dependentBeanName)) {
        return true;
    }
    for (String transitiveDependency : dependentBeans) {
        if (alreadySeen == null) {
            alreadySeen = new HashSet<String>();
        }
        alreadySeen.add(beanName);
        if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
            return true;
        }
    }
    return false;
}
  • alreadySeen != null, monitor already dependent Bean
  • canonicalName, processing alias configuration, find the original BeanName
  • Set<String> dependentBeans, get dependent Bean collection
  • The for loop recursively detects dependent beans and adds them to alreadySeen

AbstractBeanFactory -> registerDependentBean(dep, beanName) -> DefaultSingletonBeanRegistry

public void registerDependentBean(String beanName, String dependentBeanName) {
    String canonicalName = canonicalName(beanName);  

    synchronized (this.dependentBeanMap) {
        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        if (dependentBeans == null) {
            dependentBeans = new LinkedHashSet<String>(8);
            this.dependentBeanMap.put(canonicalName, dependentBeans);
        }
        dependentBeans.add(dependentBeanName);
    }   

    synchronized (this.dependenciesForBeanMap) {
        Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName
        if (dependenciesForBean == null) {
            dependenciesForBean = new LinkedHashSet<String>(8);
            this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
        }
        dependenciesForBean.add(canonicalName);
    }
}
  • canonicalName(beanName), get the original beanName
  • synchronized (this.dependentBeanMap), add <canonicalName, dependentBeanName> to dependentBeanMap
  • synchronized (this.dependenciesForBeanMap), add <dependentBeanName, canonicalName> to dependenciesForBeanMap

Finally: getBean(dep), you can get the beans that depends-on depends on

6. Dealing with single-instance beans

AbstractBeanFactory -> mbd.isSingleton()

if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
        @Override
        public Object getObject() throws BeansException {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                destroySingleton(beanName);
                throw ex;
            }
        }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
  • This part is to use beanName and singletonFactory anonymous inner class to pass in and wait for the callback to create a single instance Bean instance
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "'beanName' must not be null");
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName,
                        "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                        "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<Exception>();
            }
            try {
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            }
            catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            }
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
}
  • this.singletonObjects.get(beanName), first try to get the object from the buffer pool, if not, continue to execute
  • beforeSingletonCreation(beanName), mark that the current bean is created, if there is a circular dependency injected by the constructor, an error will be reported
  • singletonObject = singletonFactory.getObject(), the process of creating a bean is to call the createBean() method
  • afterSingletonCreation(beanName), finally remove the mark from the collection
  • addSingleton(beanName, singletonObject), the newly created one will be added to the cache collection

7. Get the bean instance from the cache

doCreateBean -> if (earlySingletonExposure) -> getSingleton(beanName, false)

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从 singletonObjects 获取实例,singletonObjects 中缓存的实例都是完全实例化好的 bean,可以直接使用
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果 singletonObject 为空,则没有创建或创建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 加锁
        synchronized (this.singletonObjects) {
            // 单例缓存池中,没有当前beanName
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 加入到三级缓存,暴漏早期对象用于解决三级缓存
                    singletonObject = singletonFactory.getObject();  
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
  • In fact, this piece of code mainly uses the three-level cache to solve the set injection circular dependency. A separate chapter will be listed later to verify the circular dependency.
  • singletonObjects, used to store the initialized bean instance.
  • earlySingletonObjects, used to store the bean in initialization, to solve the circular dependency.
  • singletonFactories, used to store the bean factory, the bean generated by the bean factory has not yet completed the bean initialization.

8. Get bean instance from FactoryBean

AbstractBeanFactory -> getObjectForBeanInstance(sharedInstance, name, beanName, null)

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
    
    // 如果 beanName 以 & 开头,但 beanInstance 却不是 FactoryBean,则会抛出异常
    if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
        throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
    }

    // 这里判断就是这个 bean 是不是 FactoryBean,不是就直接返回了
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }   

    Object object = null;
    if (mbd == null) {    
        // 如果 mbd 为空,则从缓存加载 bean(FactoryBean 生成的单例 bean 实例会缓存到 factoryBeanObjectCache 集合中,方便使用)
        object = getCachedObjectForFactoryBean(beanName);
    }   

    if (object == null) {
        // 到这,beanInstance 是 FactoryBean 类型,所以就强转了
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // mbd 为空且判断 containsBeanDefinition 是否包含 beanName
        if (mbd == null && containsBeanDefinition(beanName)) {    
            // 合并 BeanDefinition
            mbd = getMergedLocalBeanDefinition(beanName);
        }    
        boolean synthetic = (mbd != null && mbd.isSynthetic());   
        // 调用 getObjectFromFactoryBean 获取实例 
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}
  • (!(beanInstance instanceof FactoryBean), the judgment here is whether the bean is a FactoryBean or not, it returns directly
  • If mbd is empty, load the bean from the cache (the singleton bean instance generated by FactoryBean will be cached in the factoryBeanObjectCache collection for easy use)
  • Call getObjectFromFactoryBean to get the instance, here will include part of the processing of singleton and non-singleton, and finally return factory.getObject(); the corresponding Bean instance

Four, test case

1. Alias

<bean id="userService" class="org.itstack.interview.UserService"/>
<!-- 起个别名 -->
<alias name="userService" alias="userService-alias01"/>
<!-- 别名的别名 -->
<alias name="userService-alias01" alias="userService-alias02"/>
@Test
public void test_alias() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-alias.xml");
    UserService userService = beanFactory.getBean("userService-alias02", UserService.class);
    logger.info("获取 Bean 通过别名:{}", userService);
}

  • When you unit test getBean, you will see that it will gradually process the alias, and finally get the original BeanName

2. Dependence

<bean id="userService" class="org.itstack.interview.UserService" depends-on="userDao"/>
<bean id="userDao" class="org.itstack.interview.UserDao"/>
@Test
public void test_depends_on() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-depends-on.xml");
    UserService userService = beanFactory.getBean(UserService.class, "userService");
    logger.info("获取 Bean:{}", userService.getUserDao());
}

  • When it comes to dependencies, it will go to dependsOn != null and get the dependent Bean instance.

3. BeanFactory

<bean id="userDao" class="org.itstack.interview.MyFactoryBean"/>
@Test
public void test_factory_bean() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-factory-bean.xml");
    UserDao userDao = beanFactory.getBean("userDao", UserDao.class);
    logger.info("获取 Bean:{}", userDao);
}

  • The class that implements FactoryBean will need to implement the getObject method, and all such beans ultimately get getObject

Five, summary

  • So far, the core process of obtaining Beans in Spring IOC has basically been introduced. The whole chapter shows us that the process of obtaining a Bean is also very complicated, involving a lot of branch processes. There will be so many processes, which we introduced earlier, because Spring's bean acquisition needs to meet many situations.
  • In the learning process, you can first sort it out according to the GetBean flow chart, and then analyze it step by step against the source code. This process will almost consume you 1~2 days, but after the whole process of learning, you will basically be no stranger to GetBean.
  • Learning is almost a gradual process, just like walking through a maze. Although sometimes the wrong path is taken, those wrong paths are also part of knowledge learning. In the learning of programming, it is not only the result that is looked at, the process is more important, and the way of learning to learn is more meaningful.

Sixth, series recommendation


小傅哥
4.7k 声望28.4k 粉丝

CodeGuide | 程序员编码指南 - 原创文章、案例源码、资料书籍、简历模版等下载。