《Spring技术内幕》——2.5节容器其他相关特性的设计与实现

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 本节书摘来自华章社区《Spring技术内幕》一书中的第2章,第2.5节容器其他相关特性的设计与实现,作者:计文柯,更多章节内容可以访问云栖社区“华章社区”公众号查看

2.5 容器其他相关特性的设计与实现
在前面的IoC原理分析中,我们对IoC容器的主要功能进行了分析,比如BeanDefinition的载入和解析,依赖注入的实现,等等。为了更全面地理解IoC容器特性的设计,下面对容器的一些其他相关特性的设计原理也进行简要的分析。这些特性都是在使用IoC容器的时候会经常遇到的。这些特性其实很多,在这里只选择了几个例子供读者参考。在了解了IoC容器的整体运行原理以后,对这些特性的分析已经不再是一件困难的事情。如果读者对其他IoC容器的特性感兴趣,也可以按照同样的思路进行分析。
2.5.1 ApplicationContext和Bean的初始化及销毁
对于BeanFactory,特别是ApplicationContext,容器自身也有一个初始化和销毁关闭的过程。下面详细看看在这两个过程中,应用上下文完成了什么,可以让我们更多地理解应用上下文的工作。
从图中可以看到,对ApplicationContext启动的过程是在AbstractApplicationContext中实现的。在使用应用上下文时需要做一些准备工作,这些准备工作在prepareBeanFactory()方法中实现。在这个方法中,为容器配置了ClassLoader、PropertyEditor和BeanPost-Processor等,从而为容器的启动做好了必要的准备工作。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    beanFactory.setBeanClassLoader(getClassLoader());
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this));
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader
    (beanFactory.getBeanClassLoader()));
    }
    if (!beanFactory.containsBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME,
        System.getProperties());
    }
    if (!beanFactory.containsBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME,
    System.getenv());
    }
}

同样,在容器要关闭时,也需要完成一系列的工作,这些工作在doClose( )方法中完成。在这个方法中,先发出容器关闭的信号,然后将Bean逐个关闭,最后关闭容器自身。

protected void doClose() {
    if (isActive()) {
        if (logger.isInfoEnabled()) {
            logger.info("Closing " + this);
        }
        try {
            publishEvent(new ContextClosedEvent(this));
        }
        catch (Throwable ex) {
            logger.error("Exception thrown from ApplicationListener handling
            ContextClosedEvent", ex);
        }
            Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
        for (String beanName : new LinkedHashSet<String>
        (lifecycleBeans.keySet())) {
            doStop(lifecycleBeans, beanName);
        }
        destroyBeans();
        closeBeanFactory();
        onClose();
        synchronized (this.activeMonitor) {
            this.active = false;
        }
    }
}

以上是容器的初始化和销毁的设计与实现。在这个过程中需要区分Bean的初始化和销毁过程。在应用开发中,常常需要执行一些特定的初始化工作,这些工作都是相对比较固定的,比如建立数据库连接,打开网络连接等,同时,在结束服务时,也有一些相对固定的销毁工作需要执行。为了便于这些工作的设计,Spring IoC容器提供了相关的功能,可以让应用定制Bean的初始化和销毁过程。
容器的实现是通过IoC管理Bean的生命周期来实现的。Spring IoC容器在对Bean的生命周期进行管理时提供了Bean生命周期各个时间点的回调。在分析Bean初始化和销毁过程的设计之前,简要介绍一下IoC容器中的Bean生命周期。

  • Bean实例的创建。
  • 为Bean实例设置属性。
  • 调用Bean的初始化方法。
  • 应用可以通过IoC容器使用Bean。
  • 当容器关闭时,调用Bean的销毁方法。
    Bean的初始化方法调用是在以下的initializeBean方法中实现的:
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
    if (bean instanceof BeanNameAware) {
        ((BeanNameAware) bean).setBeanName(beanName);
    }
    if (bean instanceof BeanClassLoaderAware) {
        ((BeanClassLoaderAware) bean).setBeanClassLoader
        (getBeanClassLoader());
    }
    if (bean instanceof BeanFactoryAware) {
        ((BeanFactoryAware) bean).setBeanFactory(this);
    }
    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;
}
在调用Bean的初始化方法之前,会调用一系列的aware接口实现,把相关的BeanName、BeanClassLoader,以及BeanFactoy注入到Bean中去。接着会看到对invokeInitMethods的调用,这时还会看到启动afterPropertiesSet的过程,当然,这需要Bean实现InitializingBean的接口,对应的初始化处理可以在InitializingBean接口的afterPropertiesSet方法中实现,这里同样是对Bean的一个回调。
protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mbd)
    throws Throwable {
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternally
        ManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isDebugEnabled()) {
            logger.debug("Invoking afterPropertiesSet() on bean with name '" +
        beanName + "'");
        }
        ((InitializingBean) bean).afterPropertiesSet();
    }
    if (mbd != null) {
        String initMethodName = mbd.getInitMethodName();
        if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".
        equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}
最后,还会看到判断Bean是否配置有initMethod,如果有,那么通过invokeCustom-InitMethod方法来直接调用,最终完成Bean的初始化。
protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition
mbd) throws Throwable {
    String initMethodName = mbd.getInitMethodName();
    Method initMethod = (mbd.isNonPublicAccessAllowed() ?
            BeanUtils.findMethod(bean.getClass(), initMethodName) :
            ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
    if (initMethod == null) {
        if (mbd.isEnforceInitMethod()) {
            throw new BeanDefinitionValidationException("Couldn't find an init
            method named '" +
                initMethodName + "' on bean with name '" + beanName + "'");
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("No default init method named '" + initMethodName +
                    "' found on bean with name '" + beanName + "'");
            }
            return;
        }
    }
    if (logger.isDebugEnabled()) {
        logger.debug("Invoking init method  '" + initMethodName + "' on bean with name '"
        + beanName + "'");
    }
    ReflectionUtils.makeAccessible(initMethod);
    try {
        initMethod.invoke(bean, (Object[]) null);
    }
    catch (InvocationTargetException ex) {
        throw ex.getTargetException();
    }
}
在这个对initMethod的调用中,可以看到首先需要得到Bean定义的initMethod,然后通过JDK的反射机制得到Method对象,直接调用在Bean定义中声明的初始化方法。
与Bean初始化类似,当容器关闭时,可以看到对Bean销毁方法的调用。Bean销毁过程是这样的:
protected void doClose() {
    if (isActive()) {
        if (logger.isInfoEnabled()) {
            logger.info("Closing " + this);
        }
        try {
            publishEvent(new ContextClosedEvent(this));
        }
        catch (Throwable ex) {
            logger.error("Exception thrown from ApplicationListener handling
            ContextClosedEvent", ex);
        }
        Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
        for (String beanName : new LinkedHashSet<String>
        (lifecycleBeans.keySet())) {
            doStop(lifecycleBeans, beanName);
        }
        destroyBeans();
        closeBeanFactory();
        onClose();
        synchronized (this.activeMonitor) {
            this.active = false;
        }
    }
}
其中的destroy方法,对Bean进行销毁处理。最终在DisposableBeanAdapter类中可以看到destroy方法的实现。
public void destroy() {
    if (this.beanPostProcessors != null && !this.beanPostProcessors.isEmpty()) {
        for (int i = this.beanPostProcessors.size() - 1; i >= 0; i--) {
            this.beanPostProcessors.get(i).postProcessBeforeDestruction(this.bean,
            this.beanName);
        }
    }
    if (this.invokeDisposableBean) {
        if (logger.isDebugEnabled()) {
            logger.debug("Invoking destroy() on bean with name '" + this.
            beanName + "'");
        }
        try {
            ((DisposableBean) this.bean).destroy();
        }
        catch (Throwable ex) {
            String msg = "Invocation of destroy method failed on bean with name '" +
            this.beanName + "'";
            if (logger.isDebugEnabled()) {
                logger.warn(msg, ex);
            }
            else {
                logger.warn(msg + ": " + ex);
            }
        }
    }
    if (this.destroyMethod != null) {
        invokeCustomDestroyMethod(this.destroyMethod);
    }
    else if (this.destroyMethodName != null) {
        this.destroyMethod = (this.nonPublicAccessAllowed ?
               BeanUtils.findMethodWithMinimalParameters(this.bean.getClass(),
               this.destroyMethodName) :
               BeanUtils.findMethodWithMinimalParameters(this.bean.getClass().
                getMethods(), 
               this.destroyMethodName));
        invokeCustomDestroyMethod(this.destroyMethod);
    }
}

这里可以看到对Bean的销毁过程,首先对postProcessBeforeDestruction进行调用,然后调用Bean的destroy方法,最后是对Bean的自定义销毁方法的调用,整个过程和前面的初始化过程很类似。
2.5.2 lazy-init属性和预实例化
正如前面所述,在IoC容器的初始化过程中,主要的工作是对BeanDefinition的定位、载入、解析和注册。此时依赖注入并没有发生,依赖注入发生在应用第一次向容器索要Bean时。向容器索要Bean是通过getBean的调用来完成的,该getBean是容器提供Bean服务的最基本的接口。在前面的分析中也提到,对于容器的初始化,也有一种例外情况,就是用户可以通过设置Bean的lazy-init属性来控制预实例化的过程。这个预实例化在初始化容器时完成Bean的依赖注入。毫无疑问,这种容器的使用方式会对容器初始化的性能有一些影响,但却能够提高应用第一次取得Bean的性能。因为应用在第一次取得Bean时,依赖注入已经结束了,应用可以取得已有的Bean。
我们回过头头看看在上下文的初始化过程,也就是refresh中的代码实现,可以看到预实例化是整个refresh初始化IoC容器的一个步骤。在AbstractApplicationContext中看一下refresh的实现。这个初始化过程在前面分析IoC容器初始化时已经从载入和注册BeanDefinition的角度分析过。
下面将从lazy-init属性配置实现的角度进行分析。对这个属性的处理也是容器refresh的一部分。在finishBeanFactoryInitialization的方法中,封装了对lazy-init属性的处理,实际的处理是在DefaultListableBeanFactory这个基本容器的preInstantiateSingletons方法中完成的。该方法对单件Bean完成预实例化,这个预实例化的完成巧妙地委托给容器来实现。如果需要预实例化,那么就直接在这里采用getBean去触发依赖注入,与正常依赖注入的触发相比,只有触发的时间和场合不同。在这里,依赖注入发生在容器执行refresh的过程中,也就是发生在IoC容器初始化的过程中,而不像一般的依赖注入一样发生在IoC容器初始化完成以后,第一次向容器执行getBean时。具体的实现脉络清晰而简洁,如代码清单2-31所示。
代码清单2-31 refresh中的预实例化

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        try {
            postProcessBeanFactory(beanFactory);
            invokeBeanFactoryPostProcessors(beanFactory);
            registerBeanPostProcessors(beanFactory);
            initMessageSource();
            initApplicationEventMulticaster();
            onRefresh();
            registerListeners();
            // 这里是对lazy-init属性进行处理的地方
            finishBeanFactoryInitialization(beanFactory);
                finishRefresh();
            }
        catch (BeansException ex) {
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        }
    }
}
//在finishBeanFactoryInitialization中进行具体的处理过程
protected void finishBeanFactoryInitialization(ConfigurableListable
BeanFactory beanFactory) {
    beanFactory.setTempClassLoader(null);
    beanFactory.freezeConfiguration();
    /* 这里调用的是BeanFactory的 preInstantiateSingletons,
    这个方法是由DefaultListableBeanFactory实现的 */
    beanFactory.preInstantiateSingletons();
}
//在DefaultListableBeanFactory中的preInstantiateSingletons是这样的
public void preInstantiateSingletons() throws BeansException {
    if (this.logger.isInfoEnabled()) {
        this.logger.info("Pre-instantiating singletons in " + this);
    }
    //在这里就开始getBean,也就是去触发Bean的依赖注入
    /*这个getBean和前面分析的触发依赖注入的过程是一样的,只是发生的地方不同。如果不设置lazy-init属性,那么这个依赖注入是发生在容器初始化结束以后。第一次向容器发出getBean时,如果设置了lazy-init属性,那么依赖注入发生在容器初始化的过程中,会对 beanDefinitionMap中所有的Bean进行依赖注入,这样在初始化过程结束以后,容器执行getBean得到的就是已经准备好的Bean,不需要进行依赖注入*/
    synchronized (this.beanDefinitionMap) {
        for (String beanName : this.beanDefinitionNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    FactoryBean factory = (FactoryBean) getBean
                    (FACTORY_BEAN_PREFIX + beanName);
                    if (factory instanceof SmartFactoryBean &&
                    ((SmartFactoryBean) factory).isEagerInit()) {
                        getBean(beanName);
                    }
                }
                else {
                    getBean(beanName);
                }
            }
        }
    }
}

根据上面的分析得知,可以通过lazy-init属性来对整个IoC容器的初始化和依赖注入过程进行一些简单的控制。这些控制是可以由容器的使用者来决定的,具体来说,可以通过在BeanDefinition中设置lazy-init属性来进行控制。了解了这些控制原理,可以帮助我们更好地利用这些特性。
2.5.3 FactoryBean的实现
下面来介绍常见的FactoryBean是怎样实现的。这些FactoryBean为应用生成需要的对象,这些对象往往是经过特殊处理的,如ProxyFactoryBean这样的特殊Bean。FactoryBean的生产特性是在getBean中起作用的,看下面的调用:
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
getObjectForBeanInstance做了哪些处理?在getObjectForBeanInstance的实现方法中可以看到在FactoryBean中常见的getObject方法的接口,详细的实现过程如代码清单2-32所示。

代码清单2-32 FactoryBean特性的实现

protected Object getObjectForBeanInstance(
    Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
    //如果这里不是对FactoryBean的调用,那么结束处理
    if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof
    FactoryBean)) {
    throw new BeanIsNotAFactoryException(transformedBeanName(name),
    beanInstance.getClass());
    }
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.
     isFactoryDereference(name)) {
    return beanInstance;
    }
    Object object = null;
    if (mbd == null) {
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        FactoryBean factory = (FactoryBean) beanInstance;
        if (mbd == null && containsBeanDefinition(beanName)) {
        mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        //这里从FactoryBean中得到bean
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}
protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, 
boolean shouldPostProcess) {
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
                this.factoryBeanObjectCache.put(beanName, (object != null ? 
                object : NULL_OBJECT));
            }
            return (object != NULL_OBJECT ? object : null);
        }
    }
    else {
        return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
    }
}
private Object doGetObjectFromFactoryBean(
        final FactoryBean factory, final String beanName, final boolean 
        shouldPostProcess)
        throws BeanCreationException {
        AccessControlContext acc = AccessController.getContext();
        return AccessController.doPrivileged(new PrivilegedAction<Object>() {
        public Object run() {
            Object object;
            //这里调用factory的getObject方法来从FactoryBean中得到Bean
            try {
                object = factory.getObject();
            }
            catch (FactoryBeanNotInitializedException ex) {
                throw new BeanCurrentlyInCreationException(beanName, ex.toString());
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "FactoryBean threw 
                exception on object creation", ex);
            }
            if (object == null && isSingletonCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(
                    beanName, "FactoryBean which is currently in creation returned 
                    null from getObject");
            }
            if (object != null && shouldPostProcess) {
                try {
                    object = postProcessObjectFromFactoryBean(object, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(beanName, "Post-processing of the
                    FactoryBean's object failed", ex);
                }
            }
            return object;
        }
    }, acc);
}

这里返回的已经是作为工厂的FactoryBean生产的产品,而不是FactoryBean本身。这种FactoryBean的机制可以为我们提供一个很好的封装机制,比如封装Proxy、RMI、JNDI等。通过对FactoryBean实现过程的原理进行分析,相信读者会对getObject方法有很深刻的印象。这个方法就是主要的FactoryBean的接口,需要实现特定的工厂的生产过程,至于这个生产过程是怎样和IoC容器整合的,就是在上面分析的内容。
如图2-18所示是一个典型的工厂模式的使用。在这里我们复习一下设计模式中的工厂模式,做一个对比,以加深对这些代码的理解。


3d86bc0e8338356f6e94ad83604219b55e90cccb

对比两者的实现,可以看到FactoryBean类似于AbstractFactory抽象工厂,getObjectForBeanInstance( )方法类似于createProductA()这样的生产接口,而具体的FactoryBean实现,如TransactionProxyFactoryBean,就是具体的工厂实现,其生成出的TransactionProxy就是“抽象工厂”模式中对应的ConcreteProduct。有了抽象工厂设计模式的参考和对比,对FactoryBean的设计和实现就更容易理解一些了。
2.5.4 BeanPostProcessor的实现
BeanPostProcessor是使用IoC容器时经常会遇到的一个特性,这个Bean的后置处理器是一个监听器,它可以监听容器触发的事件。将它向IoC容器注册后,容器中管理的Bean具备了接收IoC容器事件回调的能力。BeanPostProcessor的使用非常简单,只需要通过设计一个具体的后置处理器来实现。同时,这个具体的后置处理器需要实现接口类BeanPostProcessor,然后设置到XML的Bean配置文件中。这个BeanPostProcessor是一个接口类,它有两个接口方法,一个是postProcessBeforeInitialization,在Bean的初始化前提供回调入口;一个是postProcessAfterInitialization,在Bean的初始化后提供回调入口,这两个回调的触发都是和容器管理Bean的生命周期相关的。这两个回调方法的参数都是一样的,分别是Bean的实例化对象和Bean的名字。BeanPostProcessor为具体的处理提供基本的回调输入,如代码清单2-33所示。
代码清单2-33 BeanPostProcessor接口定义

public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's <code>afterPropertiesSet</code>
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding <code>bean instanceof FactoryBean</code> checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* /
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

对于这些接口是在什么地方与IoC结合在一起的,可以查看一下以getBean方法为起始的调用关系,其调用过程如图2-19所示。


4e5ea26885dff481ef1aa53528debab43ae648be

postProcessBeforeInitialization是在populateBean完成之后被调用的。从BeanPostProcessor中的一个回调接口入手,对另一个回调接口postProcessAfterInitialization方法的调用,实际上也是在同一个地方封装完成的,这个地方就是populateBean方法中的initializeBean调用。关于这一点,读者会在接下来的分析中了解得很清楚。在前面对IoC的依赖注入进行分析时,对这个populateBean有过分析,这个方法实际上完成了Bean的依赖注入。在容器中建立Bean的依赖关系,是容器功能实现的一个很重要的部分。节选doCreateBean中的代码就可以看到postProcessBeforeInitialization调用和populateBean调用的关系,如下所示。

Object exposedObject = bean;
try {
    populateBean(beanName, mbd, instanceWrapper);
    /*在完成对Bean的生成和依赖注入以后,开始对Bean进行初始化,这个初始化过程包含了对后置处理器postProcessBeforeInitialization的回调 */
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}

具体的初始化过程也是IoC容器完成依赖注入的一个重要部分。在initializeBean方法中,需要使用Bean的名字,完成依赖注入以后的Bean对象,以及这个Bean对应的BeanDefinition。在这些输入的帮助下,完成Bean的初始化工作,这些工作包括为类型是BeanNameAware的Bean设置Bean的名字,类型是BeanClassLoaderAware的Bean设置类装载器,类型是BeanFactoryAware的Bean设置自身所在的IoC容器以供回调使用,当然,还有对postProcess-BeforeInitialization/postProcessAfterInitialization的回调和初始化属性init-method的处理等。经过这一系列的初始化处理之后,得到的结果就是可以正常使用的由IoC容器托管的Bean了。具体的实现过程如代码清单2-34所示。
代码清单2-34 IoC容器对Bean的初始化

//初始化Bean实例,调用在容器的回调方法和Bean的初始化方法
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
    if (bean instanceof BeanNameAware) {
        ((BeanNameAware) bean).setBeanName(beanName);
    }
    if (bean instanceof BeanClassLoaderAware) {
        ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
    }
    if (bean instanceof BeanFactoryAware) {
        ((BeanFactoryAware) bean).setBeanFactory(this);
    }
    //这里是对后置处理器BeanPostProcessors的postProcessBeforeInitialization的
    //回调方法的调用
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    //调用Bean的初始化方法,这个初始化方法是在BeanDefinition中通过定义init-method属性指定的
    //同时,如果Bean实现了InitializingBean接口,那么这个Bean的afterPropertiesSet
    //实现也会被调用
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                 (mbd != null ? mbd.getResourceDescription() : null),
                 beanName, "Invocation of init method failed", ex);
    }
    //这里是对后置处理器BeanPostProcessors的postProcessAfterInitialization的回调方
    //法的调用
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}
/*这里是对设置好的BeanPostProcessors的postProcessBeforeInitialization回调进行依次调用的地方*/
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean,
String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessBeforeInitialization(result, beanName);
    }
    return result;
}
//这里是对设置好的BeanPostProcessors的postProcessAfterInitialization回调进行
//依次调用的地方
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessAfterInitialization(result, beanName);
    }
    return result;
}

从以上的代码实现中可以看到,这两个Bean后置处理器定义的接口方法,一前一后,围绕着Bean定义的init-method方法调用,与IoC容器对Bean的管理有机结合起来了。对这个特性的理解,离不开对IoC容器基本实现原理的理解。了解了Bean后置处理器的实现原理后,就能更灵活地使用它。IoC容器的附加特性还有很多,它们代表了容器的一些特色和高级的使用技巧,掌握这些特性对应用开发有很大的帮助。下面再分析一下容器的autowiring特性是怎样实现的。
2.5.5 autowiring(自动依赖装配)的实现
在前面对IoC容器实现原理的分析中,一直是通过BeanDefinition的属性值和构造函数以显式的方式对Bean的依赖关系进行管理的。在Spring中,相对这种显式的依赖管理方式,IoC容器还提供了自动依赖装配的方式,为应用使用容器提供更大的方便。在自动装配中,不需要对Bean属性做显式的依赖关系声明,只需要配置好autowiring属性,IoC容器会根据这个属性的配置,使用反射自动查找属性的类型或者名字,然后基于属性的类型或名字来自动匹配IoC容器中的Bean,从而自动地完成依赖注入。
这是一个很有诱惑力的功能特性,使用它可以完成依赖关系管理的自动化,但是使用时一定要注意,计算机只是在自动执行,它是不会思考的。使用这个特性的优点是能够减少用户配置Bean的工作量,但它是一把双刃剑,如果使用不当,也会为应用带来不可预见的后果,所以,使用时需要多一些小心和谨慎。
从autowiring使用上可以知道,这个autowiring属性在对Bean属性进行依赖注入时起作用。对Bean属性依赖注入的实现原理,在前面已经做过分析。回顾那部分内容,不难发现,对autowirng属性进行处理,从而完成对Bean属性的自动依赖装配,是在populateBean中实现的。节选AbstractAutowireCapableBeanFactory的populateBean方法中与autowiring实现相关的部分,可以清楚地看到这个特性在容器中实现的入口。也就是说,对属性autowiring的处理是populateBean处理过程的一个部分。在populateBean的实现中,在处理一般的Bean之前,先对autowiring属性进行处理。如果当前的Bean配置了autowire_by_name和autowire_by_type属性,那么调用相应的autowireByName方法和autowireByType方法。这两个方法很巧妙地应用了IoC容器的特性。例如,对于autowire_by_name,它首先通过反射机制从当前Bean中得到需要注入的属性名,然后使用这个属性名向容器申请与之同名的Bean,这样实际又触发了另一个Bean的生成和依赖注入的过程。实现过程如代码清单2-35所示。
代码清单2-35 populateBean对autowiring属性的处理

//开始进行依赖注入过程,先处理autowiring的注入
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
    mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
    MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
    // 这里是对autowire注入的处理,根据Bean的名字或者type进行autowire的过程
    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
        autowireByName(beanName, mbd, bw, newPvs);
    }
    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
        autowireByType(beanName, mbd, bw, newPvs);
    }
    pvs = newPvs;
}

在对autowiring类型做了一些简单的逻辑判断以后,通过调用autowireByName和autowireByType来完成自动依赖装配。以autowireByName为例子来看看容器的自动依赖装配功能是怎样实现的。对autowireByName来说,它首先需要得到当前Bean的属性名,这些属性名已经在BeanWrapper和BeanDefinition中封装好了,然后是对这一系列属性名进行匹配的过程。在匹配的过程中,因为已经有了属性的名字,所以可以直接使用属性名作为Bean名字向容器索取Bean,这个getBean会触发当前Bean的依赖Bean的依赖注入,从而得到属性对应的依赖Bean。在执行完这个getBean后,把这个依赖Bean注入到当前Bean的属性中去,这样就完成了通过这个依赖属性名自动完成依赖注入的过程。autowireByType的实现和autowireByName的实现过程是非常类似的,感兴趣的读者可以自己进行分析。这些autowiring的实现如代码清单2-36所示。
代码清单2-36 autowire_by_name的实现

protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw,
MutablePropertyValues pvs) {
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        if (containsBean(propertyName)) {
            //使用取得的当前Bean的属性名作为Bean的名字,向IoC容器索取Bean
            //然后把从容器得到的Bean设置到当前Bean的属性中去
            Object bean = getBean(propertyName);
            pvs.addPropertyValue(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");
            }
        }
    }
}

2.5.6 Bean的依赖检查
在使用Spring的时候,如果应用设计比较复杂,那么在这个应用中, IoC管理的Bean的个数可能非常多,这些Bean之间的相互依赖关系也会非常复杂。在一般情况下,Bean的依赖注入是在应用第一次向容器索取Bean的时候发生,在这个时候,不能保证注入一定能够成功,如果需要重新检查这些依赖关系的有效性,会是一件很繁琐的事情。为了解决这样的问题,在Spring IoC容器中,设计了一个依赖检查特性,通过它,Spring可以帮助应用检查是否所有的属性都已经被正确设置。在具体使用的时候,应用只需要在Bean定义中设置dependency-check属性来指定依赖检查模式即可,这里可以将属性设置为none、simple、object、all四种模式,默认的模式是none。如果对检查模式进行了设置,通过下面的分析,可以更好地理解这个特性的使用。具体的实现代码是在AbstractAutowireCapableBeanFactory实现createBean的过程中完成的。在这个过程中,会对Bean的Dependencies属性进行检查,如果发现不满足要求,就会抛出异常通知应用。

protected void checkDependencies(String beanName, AbstractBeanDefinition mbd, 
PropertyDescriptor[] pds, PropertyValues pvs)
    throws UnsatisfiedDependencyException {
    int dependencyCheck = mbd.getDependencyCheck();
    for (PropertyDescriptor pd : pds) {
        if (pd.getWriteMethod() != null && !pvs.contains(pd.getName())) {
            boolean isSimple = BeanUtils.isSimpleProperty
            (pd.getPropertyType());
            boolean unsatisfied = (dependencyCheck == RootBeanDefinition.
            DEPENDENCY_CHECK_ALL) ||
                (isSimple && dependencyCheck == RootBeanDefinition.
                DEPENDENCY_CHECK_SIMPLE) ||
                (!isSimple && dependencyCheck == RootBeanDefinition.
                DEPENDENCY_CHECK_OBJECTS);
            if (unsatisfied) {
                throw new UnsatisfiedDependencyException
                (mbd.getResourceDescription(), beanName, pd.getName(),
                "Set this property value or disable dependency checking for this bean.");
            }
        }
    }
}

2.5.7 Bean对IoC容器的感知
容器管理的Bean一般不需要了解容器的状态和直接使用容器,但在某些情况下,是需要在Bean中直接对IoC容器进行操作的,这时候,就需要在Bean中设定对容器的感知。Spring IoC容器也提供了该功能,它是通过特定的aware接口来完成的。aware接口有以下这些:
m BeanNameAware ,可以在Bean中得到它在IoC容器中的Bean实例名称。
m BeanFactoryAware,可以在Bean中得到Bean所在的IoC容器,从而直接在Bean中使用IoC容器的服务。
m ApplicationContextAware,可以在Bean中得到Bean所在的应用上下文,从而直接在Bean中使用应用上下文的服务。
m MessageSourceAware,在Bean中可以得到消息源。
m ApplicationEventPublisherAware,在Bean中可以得到应用上下文的事件发布器,从而可以在Bean中发布应用上下文的事件。
m ResourceLoaderAware,在Bean中可以得到ResourceLoader,从而在Bean中使用ResourceLoader加载外部对应的Resource资源。
在设置Bean的属性之后,调用初始化回调方法之前,Spring会调用aware接口中的setter方法。以ApplicationContextAware为例,分析对应的设计和实现。这个接口定义得很简单。

public interface ApplicationContextAware {
     void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
这里只有一个方法setApplicationContext(ApplicationContext applicationContext),它是一个回调函数,在Bean中通过实现这个函数,可以在容器回调该aware接口方法时使注入的applicationContext引用在Bean中保存下来,供Bean需要使用ApplicationContext的基本服务时使用。这个对setApplicationContext方法的回调是由容器自动完成的。可以看到,一个ApplicationContextAwareProcessor作为BeanPostProcessor的实现,对一系列的aware回调进行了调用,比如对ResourceLoaderAware接口的调用,对ApplicationEventPublisherAware接口的调用,以及对MessageSourceAware和ApplicationContextAware的接口调用等。
class ApplicationContextAwareProcessor implements BeanPostProcessor {
    private final ApplicationContext applicationContext;
    public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
public Object postProcessBeforeInitialization(Object bean, String beanName) throws
BeansException {
    if (bean instanceof ResourceLoaderAware) {
        ((ResourceLoaderAware) bean).setResourceLoader(this. applicationContext);
    }
    if (bean instanceof ApplicationEventPublisherAware) {
        ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher
        (this.applicationContext);
    }
    if (bean instanceof MessageSourceAware) {
        ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
    }
    if (bean instanceof ApplicationContextAware) {
        ((ApplicationContextAware) bean).
        setApplicationContext(this.applicationContext);
    }
    return bean;
}
public Object postProcessAfterInitialization(Object bean, String name) {
      return bean;
}
}

而作为依赖注入的一部分,postProcessBeforeInitialization会在initializeBean的实现过程中被调用,从而实现对aware接口的相关注入。关于initializeBean的详细过程,感兴趣的读者可以参阅前面的章节进行回顾。

相关文章
|
2月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
48 0
|
7天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
36 6
|
22天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
32 1
|
25天前
|
存储 安全 数据中心
Docker 容器凭借轻量级和高效的特性,成为应用部署的重要工具
Docker 容器凭借轻量级和高效的特性,成为应用部署的重要工具。本文探讨了 Docker 如何通过 Namespace 和 Cgroups 实现 CPU、内存、网络和存储资源的隔离,提高系统安全性和资源利用率,以及面临的挑战和应对策略。
43 1
|
1月前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
1月前
|
Java Kotlin 索引
学习Spring框架特性及jiar包下载
Spring 5作为最新版本,更新了JDK基线至8,修订了核心框架,增强了反射和接口功能,支持响应式编程及Kotlin语言,引入了函数式Web框架,并提升了测试功能。Spring框架可在其官网下载,包括文档、jar包和XML Schema文档,适用于Java SE和Java EE项目。
33 0
|
1月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
51 0
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
17天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
157 77
|
25天前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序