乐趣区

关于java:Spring-FrameWork从入门到NB-定制Bean

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 生命周期

退出移动版