Customizing the Nature of a Bean,最早准备跳过这部分内容,但是觉得这部分内容是Spring Bean生命周期中的一个重要部分,跳过了可能会影响通往NB之路,所以还是要认真学习一下。

Spring通过三种类型的接口实现对Bean行为或状态的改变或定制:

  1. 生命周期回调
  2. ApplicationContextAware and BeanNameAware
  3. 其他Aware接口

生命周期回调

有两种类型的回调:初始化回调、临终回调。对不起我不应该管这个DisposableBean接口的destroy()方法叫临终回调,不过确实比较形象。

Spring提供了多种不同选择来实现生命周期回调:

  1. 通过InitializingBean接口或DisposableBean接口实现
  2. 通过配置init-method实现
  3. 使用JSR-250 @PostConstruct和@PreDestroy注解
  4. 通过BeanPostProcessor接口
  5. 通过Lifecycle接口

初始化回调

实现InitializingBean的afterPropertiesSet()方法,afterPropertiesSet()方法在我们上一篇文章分析Bean创建过程的9步工作法的第6步中会被调用到:

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

        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isTraceEnabled()) {
                logger.trace("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.hasAnyExternallyManagedInitMethod(initMethodName)) {
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }

从上面源码可以看到,除InitializingBean接口之外,还可以通过init-method的方式进行初始化,不过,极端情况下:两者同时使用,并且init-method也配置为afterPropertiesSet()方法,那么InitializingBean接口的afterPropertiesSet()方法会被回调、init-method方法不会。

Destruction 回调

与初始化方法回调类似,以下方式供选择:

  1. 实现接口DisposableBean的destroy()方法
  2. 定义destroy-method方法

默认初始化和Destruction 回调

定义在<beans> 属性上的初始化以及销毁回调,可以对所有bean生效。这种情况下默认所有bean都统一使用初始化和销毁回调方法,因此可以简化配置。

<beans default-init-method="init">

    <bean id="xxx" 
    </bean>

    ...
</beans>

即使设置了默认的初始化及销毁回调,依然可以通过bean标签的init-method和destory-method来覆盖默认方法。

组合使用生命周期回调机制

Spring2.5之后,你可以组合使用以下方法实现生命周期回调:

  1. InitializingBean / DisposableBean 接口
  2. 配置init() / destroy() 方法
  3. 采用JSR250 @PostConstruct 和 @PreDestroy 注解

如果不同的生命周期回调机制以相同的方法名作用在同一个bean上,则该方法只会被回调一次。我们在前面章节中已经看到过Spring初始化回调方法中的有关afterPropertiesSet方法的例子了。

如果不同的回调机制、以不同的方法名作用在同一个bean上,则按照如下顺序执行回调:

  1. @PostConstruct注解标注的方法
  2. InitializingBean接口的afterPropertiesSet() 方法
  3. 配置的init() 方法

销毁回调方法的顺序同上。

启动及关机回调

有时候我们可能需要在系统启动(或者Spring IoC容器启动)时执行特定的资源申请操作,在系统shutdown前执行资源释放操作。

Lifecycle接口可以满足以上需求,start()方法负责启动回调、stop()方法执行关机回调。

ApplicationContextAware and BeanNameAware

ApplicationContextAware接口提供了一个机会,让你从普通的Spring Bean中可以获得到创建他的ApplicationContext。

BeanNameAware接口通过setBeanName方法,为bean提供了一种能力:知道自己在Spring Ioc容器中的名字。

个人认为BeanNameAware接口用途有限,因为一般来讲bean不需要通过BeanNameAware接口的方式、而是通过其他方式知道自己在Spring IoC中的名字(或许有什么特殊场景需要,需要的时候你能立刻想到这种方式即可)。

ApplicationContextAware,一种用途是为当前bean获取Spring IoC容器中的其他Bean提供了方便,但是Spring官网并不建议这么使用:

One use would be the programmatic retrieval of other beans. Sometimes this capability is useful. However, in general, you should avoid it, because it couples the code to Spring and does not follow the Inversion of Control style, where collaborators are provided to beans as properties.

另外一种用途是通过这种方式,利用ApplicationContext去获取Spring容器的文件资源、发布Application事件等等。这些特性我们后续会深入研究。

ApplicationContextAware接口的方式获取ApplicationContext其实非常简单,你的bean实现ApplicationContextAware接口,然后实现setApplicationContext方法,Spring IoC容器就会在postProcessBeforeInitialization阶段回调setApplicationContext方法,乖乖地把ApplicationContext给你送出来。

另外你还可以通过@Autowired方式获取到ApplicationContext,这种方式更加简单方便、灵活,一切都遵循@Autowired注解的原则。

为什么通过@Autowired可以注入ApplicationContext对象?

倒着查,很快就能找到答案。先去看DefaultlistableBeanFactory的findAutowireCandidates方法,这个方法负责查找自动装配的对象:

protected Map<String, Object> findAutowireCandidates(
            @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

        //1. 按照类型匹配,包括父类
        String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this, requiredType, true, descriptor.isEager());
        Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
        //2.处理resolvableDependencies,比如ResourceLoader、ApplicationEventPublisher、ApplicationContext等
        for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
            Class<?> autowiringType = classObjectEntry.getKey();
            if (autowiringType.isAssignableFrom(requiredType)) {
                Object autowiringValue = classObjectEntry.getValue();
                autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
                if (requiredType.isInstance(autowiringValue)) {
                    result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                    break;
                }
            }
        }
       ....省略n行代码

发现会从resolvableDependencies中匹配对象,然后再来看看resolvableDependencies中究竟放了啥。

找到AbstactApplicationContext的prepareBeanFactory方法,Spring IoC初始化过程中会调用,发现有如下几行代码:

        // BeanFactory interface not registered as resolvable type in a plain factory.
        // MessageSource registered (and found for autowiring) as a bean.
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

最后一行registerResolvableDependency方法,参数是ApplicationContext自己。

然后我们看一下registerResolvableDependency方法:

    @Override
    public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
        Assert.notNull(dependencyType, "Dependency type must not be null");
        if (autowiredValue != null) {
            if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
                throw new IllegalArgumentException("Value [" + autowiredValue +
                        "] does not implement specified dependency type [" + dependencyType.getName() + "]");
            }
            this.resolvableDependencies.put(dependencyType, autowiredValue);
        }
    }

真相大白!

上一篇 Spring FrameWork从入门到NB - 单例Bean生命周期
下一篇 Spring FrameWork从入门到NB - BeanPostProcessor


45 声望17 粉丝