bean 创建时执行方法:

初始化回调,spring 在完成提供的依赖项之后调用.此时值已经被设置, 依赖项已经被设置完成.可以在这个方法之内做任何自定义的检查等操作

  1. 指定初始化方法.可在配置文件中指定初始化方法,方法可以是静态,但无论是不是静态,方法一定不能有参数.方法可以有任何返回值,但是返回值会被 Spring 忽略.
  2. 接口实现:调用实现类中实现于接口的方法.与上面这种类似, 只是无需在配置文件当中指定初始化方法,因为 spring 知道应该为所有该类型的 bean 调用哪个方法.
  3. JSR250注解:只需在配置文件当中加入注解扫描,并且在初始化方法当中加上注解.

使用初始化方法可以与 Spring 分离, 但需要记住每个 bean 的初始化方法, 如果移植性是需要考虑的问题的话, 建议使用 初始化方法与注解来实现. 否则建议使用基于接口的方法.以便减少应用程序所需要的配置, 以及由于错误配置而产生的问题.

使用 @Bean 注解指定初始化方法. 需要在注解当中指定 initMethod 属性.并将初始化方法的方法设置为该属性的值.该注解用于在 Java 配置类中声明 bean.

初始化顺序解析

以上的这些初始化方法可以作用于同一个bean上, 所以这个时候就会存在执行顺序. 参考开始那张图, 

  1. 首先调用构造函数来穿件 bean
  2. 注入依赖项(调用setter)
  3. 现在 bean 已经存在,并且提供了依赖项,预初始化的 BeanPostProcess 基础结构 bean 将被查询,以查看他们是否想从创建的bean 中调用任何东西.这些是特定于 spring 的基础架构 bean.他们在创建后执行bean修改操作. 而 @PostConstruct 注解由 CommonAnnotationBeanPostProcess 注册,所以 bean 将调用使用了 @PostConstruct 注解的方法. 该方法在 bean 被创建之后,  在类被投入使用之前, 且在bean的实际初始化之前 (即 afterPropertySet 和 init-method 之前) 执行.
  4. InitializingBean 的 afterPrope此iesSetO方法在注入依赖项后立即执行 。如果 BeanFactory 设置了提供的所有 Bean 属性并且满足 Bean.FactoryAware 和 ApplicationContextAware,将会调用 afterPropertiesSetO方法。
  5. 最后执行 init-method 属性,这是因为它是 bean 的实际初始化方法 。

bean 的销毁

如果想要指定一个在 bean 被销毁时调用的方法, 有几种种方式

  1. 指定销毁方法: 在配置文件当中指定 destroy-method 属性的值, 值为销毁方法的方法名
  2. 实现 DisposableBean 接口.实现该接口,该接口提供了一个 destroy() 方法.该方法在 bean 被销毁前被调用.作为接收销毁的回调机制.
  3. 使用 JSR250 的注解 @PreDestroy : 将该注解使用在方法上, 该方法便是一个 bean 销毁的回调方法.
  4. 使用 @Bean 声明销毁方法.在该注解当中指定 destroyMethod 属性的值,值为销毁方法的方法名.

销毁回调是确保应用程序正关闭,并且不使资源状态处于打开或不一致状态的理想机制.

销毁回调解析顺序:

与创建bean的情况一样, Spring 首先调用是用来 @PreDestroy 注解的方法,然后调用实现 DisposableBean 接口的类中的 destroy() 方法,最后调用自定义配置的 destroy() 方法.

spring 中销毁回调函数是它们不会自动触发.需要记住在应用程序关闭之前调用 AbstractApplicationContext.destroy() 方法.当应用程序作为servlet运行时,可以在 servlet 的 destroy() 方法中调用 destroy().但是在独立的应用程序中,并不是那么简单,尤其是在应用程序存在多个退出点时.不过有一个方案来解决这个问题. Java 允许创建一个关闭钩子(shutdown hook).它是在应用程序关闭之前执行的一个线程.这是调用 AbstrnctApplicationContext(所有具体的 ApplicationContext 实现都扩展了AbstractApplicationContext)的
destroy()方法的一种理想方式。利用此机制的最简单方法是使用 AbstractApplicationContext 的 registerShutdownHook()
方法。该方法自动指示 Spring 注册底层只币4 运行时的关闭钩子 。bean 的声明和配置和之前一样:唯一改变的是需要在关闭的时候添加对 ctx. registerShutdownHook 的调用.同时删除对 ctx.d巳stroy()或 close()的调用如:

public static void main(String... args) {
        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
        ctx.load("app-context-interface.xml");
        ctx.refresh();
        getBean("singerOne", ctx);
        getBean("singerTwo", ctx);
        getBean("singerThree", ctx);
        //ctx.close();
        ctx.registerShutdownHook(); //注册关闭钩子,关闭应用程序.,有了这个方法, 就无需上面的 close() 方法了.
    }

 

使用 FactoryBean

当需要一个不能简单的通过 new 运算符创建的依赖项时应该怎么办呢.假如需要一个  Calendar 实例,或者一个 MessageDigest 实例. 由于这些类并不能通过 new 运算符构建对象.所以这个时候, 就可以利用 spring 提供的 FactoryBean 接口.该接口充当不能使用标准 Spring 语义创建和管理的对象的适配器.通常,使用 FactoryB巳an 创建不能通过使用 new 运算符创建的 bean, 例如通过静态工厂方法访问的 bean (通过静态方法获取的 bean). 简单来说 FactoryBean 也是一个 bean , 不过它可以作为其他 bean 的工厂.

示例:

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

import java.security.MessageDigest;

public class MessageDigesFactoryBean implements FactoryBean<MessageDigest>, InitializingBean {

    private String algorithmName = "MD5";

    private MessageDigest messageDigest = null;

    //spring 调用该方法来获取由 FactoryBean 创建的对象.
    @Override
    public MessageDigest getObject() throws Exception {
        return messageDigest;
    }

    /**
     * 告诉 Spring FactoryBean 所返回对象的类型 。 如果事先不知道返回类型(例如,
     * FactmyBean 根据配置创建不同类型的对象,具体的类型只有在 FactoryBean 初始化后才能确定) , 那么对象类型可以
     * 为 null,但如果指定了类型,那么 Spting 可以使用该类型实现自动装配. 也可以返回一个接口.并让 getObject() 实力化具体的实现类
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return MessageDigest.class;
    }

    //告诉 spring 是否是一个单例
    @Override
    public boolean isSingleton() {
        return true;
    }

    //创建 MessageDisgest 实例.
    @Override
    public void afterPropertiesSet() throws Exception {
        messageDigest = MessageDigest.getInstance(algorithmName);
    }

    public void setAlgorithmName(String algorithmName) {
        this.algorithmName = algorithmName;
    }
}
import java.security.MessageDigest;

public class MessageDigester {

    private MessageDigest digest1;
    private MessageDigest digest2;

    public void setDigest1(MessageDigest digest1) {
        this.digest1 = digest1;
    }
    public void setDigest2(MessageDigest digest2) {
        this.digest2 = digest2;
    }

    public void digest(String msg) {
        System.out.println("Using digest1");
        digest(msg, digest1);

        System.out.println("Using digest2");
        digest(msg, digest2);
    }

    public void digest(String msg, MessageDigest disgest) {
        System.out.println("Using alogtithm: " + disgest.getAlgorithm());
        disgest.reset();
        byte[] bytes = msg.getBytes();
        byte[] out = disgest.digest(bytes);
        System.out.println(out);
    }

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="shaDigest" class="com.cn.hj.spring5.chapter4.factoryBean.MessageDigesFactoryBean" p:algorithmName="SHA1"/>

    <bean id="defaultDigest" class="com.cn.hj.spring5.chapter4.factoryBean.MessageDigesFactoryBean"/>

    <bean id="digester" class="com.cn.hj.spring5.chapter4.factoryBean.MessageDigester" p:digest1-ref="shaDigest" p:digest2-ref="defaultDigest"/>

</beans>
public class MessageDigesterDemo {
    public static void main(String... args) {
        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
        ctx.load("app-context-beanFactory.xml");
        ctx.refresh();
        MessageDigester bean = ctx.getBean("digester", MessageDigester.class);
        bean.digest("hello world");
        ctx.close();
    }
}

运行示例可以得到如下结果:

spring bean 初始化注入 spring 在bean初始化前调用注解_spring bean 初始化注入

可以看出, 当无法通过 new 创建 bean 所需的依赖项的时候, FactoryBean 是完美的解决方案.如果使用通过工厂方法创建的对象,并且希望在 Spring 应用程序中使用这些类,那么可以创建 FactoryBean 以充当适配器,从而使类可以充分利用 Spring
的 IoC 功能. 

如果通过 java 进行配置的话, 需要显示的调用 FactoryBean 的 getObject() 方法,以便获取对象进行注入.

另一个写法就是, 在配置文件当中使用 factory-bean, 以及 factory-method 来构建, 这个时候 factory-method 指定用于创建依赖项的方法, factory-bean 指定方法所在的实例 bean.如:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 工厂bean -->
    <bean id="shaDigestFactory" class="com.cn.hj.spring5.chapter4.factoryBean.secondVersion.MessageDigestFactory" p:algorithmName="SHA1"/>

    <!-- 工厂bean -->
    <bean id="defaultDigestFactory" class="com.cn.hj.spring5.chapter4.factoryBean.secondVersion.MessageDigestFactory"/>

    <!-- 需要的依赖项 factory-bean 指定方法所在的 bean 的实例, factory-method 指定需要调用的bean 实例中的方法,利用此方法创建需要的依赖项-->
    <bean id="shaDigest" factory-bean="shaDigestFactory" factory-method="createInstance"/>

    <!-- 需要的依赖项 factory-bean 指定方法所在的 bean 的实例, factory-method 指定需要调用的bean 实例中的方法,利用此方法创建需要的依赖项-->
    <bean id="defaultDigest" factory-bean="defaultDigestFactory" factory-method="createInstance"/>

    <!-- 实例bean -->
    <bean id="digester" class="com.cn.hj.spring5.chapter4.factoryBean.secondVersion.MessageDigester" p:digest1-ref="shaDigest" p:digest2-ref="defaultDigest"/>

</beans>