Spring源码分析之Bean的加载

作者: 一字马胡
转载标志 【2017-12-29】

更新日志

日期 更新内容 备注
2017-12-29 创建分析文档 Spring源码分析系列文章(二)

前言

在Spring源码分析的第一篇文章Spring源码分析之Bean的解析中,主要梳理了Spring Bean解析的链路,当然只是梳理了主干部分,细节的内容没有涉及太多。本文接着上一篇文章,开始进行Spring Bean加载的源码分析,bean解析完成之后,还不是可用的对象实例,需要进行加载,所谓bean的加载,就是将解析好的BeanDefinition变为可用的Object。在进行源码分析之前,可以猜测一下Spring bean加载到底做了些什么工作。首先,Spring bean解析的工作将我们在xml中配置的bean解析成BeanDefinition并且放在内存中,现在,我们可以拿到每一个bean的完整的信息,其中最重要的应该是Class信息,因为实例化对象需要知道具体需要实例化的Class信息。还需要知道具体bean的属性值等信息,而这些数据目前都是可以拿到的,所以接下来的事情就是根据这些信息首先new一个对于Class类型的Object,然后进行一些初始化,然后返回。

当然,Spring bean加载的实现远远没有这么简单,这中间涉及的内容很多,本文依然不会涉及太多细节的内容,主要梳理Spring bean加载的主干流程,知道一个bean是如何被解析,以及如何加载的,是本文以及第一篇文章内容所想要表达的内容。

Spring Bean加载流程分析

分析Spring bean加载的第一步是找到分析的入口。接着上一篇文章的例子,从下面的代码开始分析:


        String file = "applicationContext.xml";

        ApplicationContext context = new ClassPathXmlApplicationContext(file);

        ModelA modelA = (ModelA) context.getBean("modelA");

跟进context.getBean,可以在AbstractBeanFactory中找到具体的实现:


    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
    }

可以看到实际执行的方法是doGetBean,因为这个方法较为复杂,所以拆开来分析实现的具体细节。首先关注下面的代码:


    final String beanName = transformedBeanName(name);

    protected String transformedBeanName(String name) {
        return canonicalName(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;
    }   

我们传递进去的name就是我们在xml中配置的id,首先会调用transformedBeanName对传递的name做一次处理,在transformedBeanName中又调用了BeanFactoryUtils.transformedBeanName方法来进行做处理,具体的内容就是判断我们传递的name是不是以“&”开头的,要是的需要去掉再返回。需要注意的是,我们传递的name可能是bean的别名,也可能是BeanFactory,所以transformedBeanName的操作的目的就是取到真正的bean的名字,以便开始后续的流程。

获取到bean的name之后,开始走下面的流程,需要注意下面的方法调用:


        // Eagerly check singleton cache for manually registered singletons.
        Object sharedInstance = getSingleton(beanName);

具体的getSingleton的实现细节可以参考下面的代码:

因为我们的bean在配置的时候可能会被配置为单例或者原型模式,单例模式的bean全局只会生成一次,所以getSingleton方法的目的是去Spring的单例bean缓存中寻找是否该bean已经被初始化了,如果已经被初始化了那么直接从缓存取到就可以了,就没有必要走下面的流程了。上面贴出的getSingleton代码实现的就是尝试从缓存map中取bean,当然,这里面还是涉及一些其他的流程的。如果发现需要取的bean实例没有在缓存中,那么就不能走缓存了,就需要走接下来的流程了。方法isSingletonCurrentlyInCreation实现的内容是判断当前bean是否是正在被加载的bean,判断方法是使用一个set来实现,没当在进行加载单例bean之前,Spring会将该bean的name放到set中。earlySingletonObjects是一个map,存储的是还没有加载完成的bean的信息。如果发现从earlySingletonObjects中取到的bean实例为null的话,getSingleton方法就会进行该单例bean的加载。在实现上,首先通过取到用于创建该bean的BeanFactory,然后调用BeanFactory的getObject方法获取到bean实例。

singletonFactories存储着bean name以及创建该bean的BeanFactory。那用于获取bean实例的BeanFactory是什么时候被什么对象放到map中来的呢?可以发现是一个叫做addSingletonFactory的方法在做这件事情,下面是这个方法的实现细节:

那是什么地方调用该方法的呢?可以在AbstractAutowireCapableBeanFactory类中的doCreateBean方法中找到该方法的调用细节,相关的上下文代码如下:

接着回到getSingleton方法,获取到用于创建bean实例的BeanFactory之后,就可以使用singletonFactory.getObject()来获取到实例对象了。下面来分析一下这条链路的具体流程。这里需要注意一下,我们需要知道这个BeanFactory到底是什么样的实现,才能分析具体的内容,所以解铃还得系铃人,回头看看到底放到singletonFactories中的是一个什么样的BeanFactory。最后发现,实际执行的方法是getEarlyBeanReference,见下面的代码:


    /**
     * Obtain a reference for early access to the specified bean,
     * typically for the purpose of resolving a circular reference.
     * @param beanName the name of the bean (for error handling purposes)
     * @param mbd the merged bean definition for the bean
     * @param bean the raw bean instance
     * @return the object to expose as bean reference
     */
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

实际上我们需要关心的是第三个参数bean,这也就是实际的bean实例对象,那这个bean到底是什么呢?是怎么样被创建出来的呢?这就得回头看调用getEarlyBeanReference方法的上下文了,所以回头看doCreateBean方法中的具体内容。主要看下面这段代码:

主要的流程是看缓存里面有没有,如果有则不需要再次加载,否则需要调用方法createBeanInstance来创建bean实例,为了简化问题,主要分析createBeanInstance这个方法的实现细节。首先调用方法resolveBeanClass获取bean的Class,然后判断权限等操作,首先来看instantiateBean方法的具体细节,该方法代表使用默认构造函数来进行bean的实例化,而autowireConstructor方法则代表使用有参数的构造函数来进行bean的实例化。下面是instantiateBean的实现细节:

主要关注:beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent)这一句代码,然后看instantiate这个方法的实现细节:

主要关注:return BeanUtils.instantiateClass(constructorToUse)这行代码以及else分支的return语句,闲来看return BeanUtils.instantiateClass(constructorToUse)这句代码的实现细节:

其实就是根据类的构造函数以及构造函数参数来new一个对象,具体的细节可以再跟进去详细研究。下面来看else分支中的instantiateWithMethodInjection(bd, beanName, owner),下面是具体的实现代码:

具体的内容就是使用CGlib来进行对象的创建,具体的内容可以参考其他的资料,或者再跟下去来详细研究。下面来看使用有参构造函数来进行bean的实例化的具体实现细节,对应的方法是autowireConstructor,该方法相对于使用默认构造函数来进行bean的实例化来说,需要做的事情是,因为bean可能有多个构造函数,并且可能权限不同,所以Spring需要根据参数个数以及类型选取一个合适的构造函数来进行bean的实例化,这部分代码较多,但是大概的流程就是这样,代码就不再贴出来了。

在new出来一个bean实例之后,还没有真正完成Spring的bean加载内容,因为除了new出来一个Object之外,还需要做其他的事情,下面来分析一下Spring都做了一些什么事情。首先是populateBean方法,该方法实现的功能是对bean进行属性注入,下面是该方法的关键代码:


        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

            // Add property values based on autowire by name if applicable.
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }

            // Add property values based on autowire by type if applicable.
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }

            pvs = newPvs;
        }

autowireByName代表根据名字自动注入属性,而autowireByType实现的是根据类型自动注入属性,根据名字注入相对简单一些,而根据类型注入要复杂很多,下面先来分析根据名字自动注入属性的分析。下面是autowireByName的代码:


    protected void autowireByName(
            String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        for (String propertyName : propertyNames) {
            if (containsBean(propertyName)) {
                Object bean = getBean(propertyName);
                pvs.add(propertyName, bean);
                registerDependentBean(propertyName, beanName);
                if (logger.isDebugEnabled()) {
                    logger.debug("Added autowiring by name from bean name '" + beanName +
                            "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
                            "' by name: no matching bean found");
                }
            }
        }
    }

    /**
     * Return an array of non-simple bean properties that are unsatisfied.
     * These are probably unsatisfied references to other beans in the
     * factory. Does not include simple properties like primitives or Strings.
     * @param mbd the merged bean definition the bean was created with
     * @param bw the BeanWrapper the bean was created with
     * @return an array of bean property names
     * @see org.springframework.beans.BeanUtils#isSimpleProperty
     */
    protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
        Set<String> result = new TreeSet<>();
        PropertyValues pvs = mbd.getPropertyValues();
        PropertyDescriptor[] pds = bw.getPropertyDescriptors();
        for (PropertyDescriptor pd : pds) {
            if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
                    !BeanUtils.isSimpleProperty(pd.getPropertyType())) {
                result.add(pd.getName());
            }
        }
        return StringUtils.toStringArray(result);
    }

unsatisfiedNonSimpleProperties方法首先获取所有需要进行依赖注入的属性名称,然后保存起来,特别需要注意的是registerDependentBean这个方法的调用,该方法实现的是维护bean的依赖管理。接着是根据类型注入autowireByType的实现。该方法的实现还是很复杂的,暂时不对该方法进行分析。

当执行完上面两个方法之后,已经获取到了所有需要注入的属性信息,pvs持有这些信息,接着,applyPropertyValues方法会实际将这些依赖注入。下面来分析一下applyPropertyValues方法的具体实现流程。具体的实现细节可以参考下面给出的索引:


AbstractAutowireCapableBeanFactory#applyPropertyValues


主要关注核心代码:bw.setPropertyValues(mpvs),bw是bean实例对象,mpvs是属性信息。如果再追踪下去,就可以看到如下的代码链路:


    public void setPropertyValues(PropertyValues pvs) throws BeansException {
        setPropertyValues(pvs, false, false);
    }

    public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
            throws BeansException {

        List<PropertyAccessException> propertyAccessExceptions = null;
        List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
                ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
        for (PropertyValue pv : propertyValues) {
            setPropertyValue(pv);
        }
    }
    

这里面已经去掉了一些异常处理的代码,只留下最核心的代码,主要关注setPropertyValue这个方法,再继续跟踪下去:


    public void setPropertyValue(PropertyValue pv) throws BeansException {
        setPropertyValue(pv.getName(), pv.getValue());
    }


到这里就应该很清晰了,pv.getName()是属性名称,而pv.getValue()就是实际上注入的依赖。在完成依赖注入之后,应该说bean就可以使用了,但是因为Spring还提供了一些其他的标签让用户进行个性化设置,比如初始化方法、销毁方法等,接着看exposedObject = initializeBean(beanName, exposedObject, mbd)这行代码的实际工作流程。


    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

这其中调用了几个个性化的方法,第一个是invokeAwareMethods,是去访问子类的Aware,BeanClassLoaderAware, BeanFactoryAware等,下面是该方法的实现细节:


    private void invokeAwareMethods(final String beanName, final Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ClassLoader bcl = getBeanClassLoader();
                if (bcl != null) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                }
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }

接着是applyBeanPostProcessorsBeforeInitialization方法,是访问子类的后处理器内容,下面是该方法的详细实现:


    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessBeforeInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

接着是invokeInitMethods方法,是调用用户设定的init方法,下面是该方法的实现细节:


protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
            throws Throwable {

        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }

        if (mbd != null && bean.getClass() != NullBean.class) {
            String initMethodName = mbd.getInitMethodName();
            if (StringUtils.hasLength(initMethodName) &&
                    !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }

这其中需要注意的一点是,在该方法中调用了afterPropertiesSet方法,然后是获取了用户设定的init方法的名字,然后调用invokeCustomInitMethod方法执行了这个init方法。

到此,单例bean的加载好像分析到头了,确实是,当然这只是一个很小的分支,还有其他的分支没有走,现在回到doGetBean方法,继续分析剩下的代码。为了分析简单,直接从下面的代码开始分析,其他没有分析到的代码以后再详细分析,现在主要是希望走通整个流程,所以只会挑选关键的代码进行分析。首先是下面的代码:


                // Guarantee initialization of beans that the current bean depends on.
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        registerDependentBean(dep, beanName);
                        getBean(dep);
                    }
                }

因为我们的bean可能依赖其他的bean,所以需要在加载当前bean之前加载它所依赖的bean。所以这里面又调用了getBean方法来循环加载依赖的bean。接着看下面的代码:

如果bean是一个单例bean,那么会走到该分支里面。会调用getSingleton方法来加载该bean。getSingleton方法的第一个参数是beanName,第二个参数比较关键,是用于创建bean的BeanFactory,所以有必要仔细分析一下该BeanFactory。因为在getSingleton方法中关键的步骤就是调用BeanFactory的getObject方法来获取到一个bean的实例,当然还有很多其他的细节需要判断,但不再本文的叙述范围之内。下面来看这个BeanFactory的具体实现。根据上面贴出的代码,其实内部关键调用了一个方法createBean。下面来看这个方法的实现细节。该方法的关键内容如下:

首先会调用resolveBeforeInstantiation方法,这是一个试探性的方法调用,根据描述,该方法是给BeanPostProcessors一个机会来返回bean实例,具体看看该方法的细节:


    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // Make sure bean class is actually resolved at this point.
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                Class<?> targetType = determineTargetType(beanName, mbd);
                if (targetType != null) {
                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    if (bean != null) {
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
    }

该方法关键会先调applyBeanPostProcessorsBeforeInstantiation方法来调用实现类的postProcessBeforeInstantiation方法,如果返回内容还为null,就会继续调用applyBeanPostProcessorsAfterInitialization方法来调用实现类的postProcessAfterInitialization。所以这里需要注意一下,这也是一个Spring使用技巧点,可以继承BeanPostProcessor来实现个性化的Spring bean定制。

当resolveBeforeInstantiation方法返回null的时候,就会继续调用doCreateBean方法来加载bean的实例。该方法中首先一个比较关键的方法调用是createBeanInstance,下面的流程在上文中已经进行过了,就不再赘述了。只要知道doCreateBean方法返回的就是一个bean的实例就好了。

当然,我们获取到的bean实例可能是一个FactoryBean,那么就需从FactoryBean中获取到Object。实现该流程的是方法getObjectForBeanInstance。主要看该方法中调用的getObjectFromFactoryBean方法。进到getObjectFromFactoryBean方法之后主要注意方法doGetObjectFromFactoryBean。该方法的主要流程代码如下:

主要关注代码:object = factory.getObject(),继续看下面的代码:


    public final T getObject() throws Exception {
        if (isSingleton()) {
            return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
        }
        else {
            return createInstance();
        }
    }

这里区分了单例和原型两种bean,如果bean被配置为单例,则会走第一个分支,原型则会走第二个分支。对于单例bean,首先会判断是否已经初始化过了,如果已经初始化过了,那么就直接取到bean实例,否则会调用getEarlySingletonInstance来获取到bean的实例,下面来具体分析一下getEarlySingletonInstance这个方法的执行流程。跟踪进去最后执行的代码是下面的方法:


    private T getEarlySingletonInstance() throws Exception {
        Class<?>[] ifcs = getEarlySingletonInterfaces();
        if (ifcs == null) {
            throw new FactoryBeanNotInitializedException(
                    getClass().getName() + " does not support circular references");
        }
        if (this.earlySingletonInstance == null) {
            this.earlySingletonInstance = (T) Proxy.newProxyInstance(
                    this.beanClassLoader, ifcs, new EarlySingletonInvocationHandler());
        }
        return this.earlySingletonInstance;
    }

到这里就很清晰了,还是会判断是否已经加载过了,如果加载过了,那么就不会重复实例化bean了,否则就使用java的动态代理技术生成一个bean实例。

对于原型bean来说,会调用createInstance方法进行bean的实例化过程,对不同类型的FactoryBean会使用不同的子类的方法来获取实例,比如AbstractFactoryBean的子类等等。

到这里需要说明一下FactoryBean这个类,FactoryBean是一个Bean,实现了FactoryBean接口的类有能力改变bean,FactoryBean希望你实现了它之后返回一些内容,Spring会按照这些内容
去注册bean,这也给了程序员一个定制bean的机会,继承了FactoryBean的子类的bean的加载会调用FactoryBean的getObject方法获取bean实例,而且,我们使用getBean获取到的是FactoryBean的getObject方法调用的结果,如果想要获取到FactoryBean本身,需要在bean前面加上“&”标志,这个逻辑可以在方法getObjectForBeanInstance中找到:


        // Now we have the bean instance, which may be a normal bean or a FactoryBean.
        // If it's a FactoryBean, we use it to create a bean instance, unless the
        // caller actually wants a reference to the factory.
        if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
        }

如果对BeanFactory和FactoryBean傻傻分不清,可以参考文章Spring的BeanFactory和FactoryBean,主要思路就是看后缀,BeanFactory是一种FactoryBean,是用来生成bean的,而FactoryBean是一个bean,它是用来改变FactoryBean生成bean的路径的。

需要注意的一点是,上文的分析中并没有涉及缓存的内容,这部分内容比较离散,我们在走主流程的时候只要知道它会在何时的时候填充缓存(主要关注单例bean),以及在需要的时候从缓存中获取就可以了。

接着回到doGetBean方法中,看下面的分支:

如果不是一个单例的话,那么可能是一个Prototype类型的bean,所谓Prototype类型的bean,就是每次都会创建一个bean实例,这是和单例区分开来说的,除此之外,和单例bean的加载没有区别,所以也不再赘述了。

doGetBean方法还有后续的流程没有走完,但是到此为止我们已经明白了一个bean是如何被加载的,它现在几乎可以被使用了,后面就是一些收尾工作,具体流程就不再分析了,后续会进行一些策略性的补充。

本文涉及的内容时Spring bean的加载,内容较多,而且较为复杂,很多内容都没有提及,只是大概梳理了一下主干流程,接着上一篇文章来继续分析,在上一篇文章中分析了Spring bean解析的流程,解析完成之后需要加载才能使用,在加载的过程中设计很多内容,其中包括bean的实例化,以及bean的依赖注入等内容。bean的加载大体上分为单例和原型,本文主要分析了单例bean的加载,原型bean的加载与单例bean的加载流程是一样的,只是单例全局只有一个bean实例,所有可以从缓存中取到,而原型bean每次都是新创建一个bean实例。总体来讲,分析Spring的源码还是比较复杂的,本文中欠缺的内容将在未来陆续补充完善,但主要目的还是梳理流程,走一遍Spring容器的方方面面,这样使用起来就会多几分底气了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容