# 1.6 自定义Bean

Spring提供了一系列的接口让你可以自定义Bean。

## 1.6.1 生命周期回调

要与容器中bean的生命周期进行交互，可以实现Spring的InitializingBean和DisposableBean接口。容器为前者调用afterPropertiesSet（），为后者调用destroy（），以便bean在初始化和销毁bean时执行某些操作。

> JSR-250 的@PostConstruct和@PreDestroy注解是生命周期回调的最推荐的方式，因为他们和Spring框架解耦。
>
> 如果你不想使用JSR-250,又想解耦，那么可以考虑使用init-method 和destroy-method 这样两个定义bean的元数据。

在内部，Spring框架使用BeanPostProcessor实现来处理它可以找到的任何回调接口，并调用适当的方法。如果你需要自定义特性或其他Spring默认不提供的生命周期行为，你可以自己实现BeanPostProcessor。有关详细信息，请参见容器扩展点。

除了初始化和销毁回调之外，Spring管理的对象还可以实现生命周期接口，以便这些对象可以参与由容器自身生命周期驱动的启动和关闭过程。

生命周期回调接口在本节中进行了描述。

**初始化回调**

org.springframework.beans.factory.InitializingBean 接口可以让bean在Spring初始化之后，执行自定义的初始化流程，接口只有一个方法InitializingBean：

```
void afterPropertiesSet() throws Exception;
```

但是我们不建议你使用InitializingBean，这个和Spring框架耦合在一起。我们推荐使用@PostConstruct或者定义一个POJO的初始化方法。如果使用XML配置，可以使用init-method来指定一个void返回值的方法。如果使用Java配置，可以配置@Bean的initMethod属性，参照下面例子：

```
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
```

```
public class ExampleBean {

    public void init() {
        // do some initialization work
    }
}
```

上面例子和下面是等价的：

```
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
```

```
public class AnotherExampleBean implements InitializingBean {

    public void afterPropertiesSet() {
        // do some initialization work
    }
}
```

但是，最上面的例子和Spring是解耦的。

**Destruction回调**

要实现销毁回调，可以实现接口org.springframework.beans.factory.DisposableBean， 他提供了一个DisposableBean方法：

```
void destroy() throws Exception;
```

我们不建议你使用DisposableBean，因为他和Spring框架耦合，我们推荐你使用@PreDestroy注解，或者一个能被bean定义支持的通用方法。在XML配置中，可以使用\<bean/>的destroy-method属性。在Java配置中，可以设置@Bean的destroyMethod属性。如下所示：

```
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>

public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}
```

上面例子和下面作用一致：

```
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>

public class AnotherExampleBean implements DisposableBean {

    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}
```

当然，最上面的例子和Spring解耦。

> 可以为\<bean>元素的destroy-method属性指定一个特殊（推断）值，该值指示spring自动检测特定bean类上的公共close或shutdown。（可以实现java.lang.AutoCloseable或java.io.Closeable）
>
> 你还可以在\<beans>元素的默认default-destroy-method属性上设置此特殊（推断）值，以将此行为应用于整个bean集（请参见默认初始化和销毁方法）。注意，这是Java配置的默认行为。

**默认的Initialization和Destroy方法**

在编写不使用Spring特定的InitializingBean和DisposableBean回调接口的初始化和销毁方法回调时，通常会命名为init（）、initialize（）、dispose（）。理想情况下，这样的生命周期回调方法的名称在项目中是标准化的，这样所有开发人员都使用相同的方法名称并确保一致性。

你可以配置Spring容器去“查找”特定名字的初始化和销毁方法。这意味着，作为应用程序开发人员，你可以编写应用程序类并使用名为init（）的初始化回调，而无需为每个bean定义配置init method=“init”属性。Spring IOC容器在创建bean时将会调用该方法。此功能强调命名的一致性。

假设初始化回调方法名为init（），销毁回调方法名为destroy（）。然后，你的类类似于以下示例：

```
public class DefaultBlogService implements BlogService {

    private BlogDao blogDao;

    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }

    // this is (unsurprisingly) the initialization callback method
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}
```

接下来，你可以在xml中定义它：

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

    <bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>

</beans>
```

\<beans>中的default-init-method表明所有的bean在Spring IOC容器进行初始化的时候，都会在合适的时间调用 init方法。

同样的，你可以在\<beans>中配置default-destroy-method 。

如果想覆盖默认的回调函数，可以在\<bean>中使用init-method或者destroy-method。

Spring容器保证在向bean提供所有依赖项之后立即调用配置的初始化回调。因此，在对原始bean引用调用初始化回调时，AOP拦截器等尚未作用于bean。

首先创建一个目标bean，然后才能应用一个带有拦截器链的AOP代理。如果目标bean和代理是单独定义的，那么你的代码甚至可以影响原始目标bean。因此，将拦截器应用于init方法会导致不一致的结果，因为这样做会将目标bean的生命周期耦合到其代理或拦截器，并在代码直接与原始目标bean交互时留下奇怪的语义。

**总结生命周期机制**

在Spring2.5中，你有3种方式来控制bean的生命周期：

* InitializingBean和DisposableBean回调接口
* 自定义init() 和destroy() 方法。
* @PostConstruct 和 @PreDestroy注解.

你可以将3种方式结合使用。

> 如果多种方式一起使用，每种方式都配置了一个不同的方法名，那么他们的执行顺序将会如下面的顺序所示。 但是如果他们都配置了同一个方法名，那么该方法只会执行一次。

如果为initialization配置了多种生命周期的多个名字，那么执行顺序如下：

* @PostConstruct 的方法注解
* InitializingBean接口里面的afterPropertiesSet() 方法
* 自定义的init() 方法

Destroy也是一样的顺序：

* @PreDestroy 的方法注解
* DisposableBean接口里面的destroy() 方法
* 自定义的destroy() 方法

**启动和关闭回调**

Lifecycle接口为需要管理自己生命周期的对象（例如需要启动和关闭一些后台进程）提供了最基本的方法。

```
public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}
```

任何Spring管理的对象都可以实现Lifecycle接口。当ApplicationContext收到一个start或者stop信号时，他会将该信号传递给所有的Lifecycle接口的实现。这是通过LifecycleProcessor委托来实现的：

```
public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}
```

LifecycleProcessor是Lifecycle的扩展。它还添加了另外两种方法来响应正在刷新和关闭的上下文。

> 请注意，org.springframework.context.Lifecycle接口只是提供了一个简单的start和stop通知协议，但是并没有实现上下文刷新时自动启动的功能。如果想更好的控制自动启动的功能，可以实现org.springframework.context.SmartLifecycle接口。
>
> 另外，stop通知不能保证一定在destruction之前执行。通常情况下所有的Lifecycle都会收到一个stop通知在总的destruction回调执行之前。但是在热刷新或者终止刷新尝试时，只会执行destroy方法。

启动和关闭调用的顺序可能很重要。如果任何两个对象之间存在“依赖”关系，则依赖方在依赖之后开始，在依赖之前停止。然而，有时，直接依赖性是未知的。你可能只知道某个类型的对象应该先于另一个类型的对象开始。在这些情况下，SmartLifecycle接口定义了另一个选项，即在其父接口上定义的getPhase（）方法。下表显示了Phased接口的定义：

```
public interface Phased {

    int getPhase();
}
```

下面是SmartLifecycle的定义：

```
public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}
```

启动时，具有最低phase的对象首先启动。停止时，按相反顺序执行。因此，实现SmartLifecycle且其getPhase（）方法返回Integer.MIN\_VALUE值的对象将是第一个开始和最后一个停止的对象。

同样的，Integer.MAX\_VALUE值的phase值将表面对象应在最后启动并首先停止（可能是因为它取决于要运行的其他进程）。在考虑phase值时，还必须知道不实现SmartLifecycle的任何“正常”生命周期对象的默认阶段是0。因此，任何负phase值都表示一个对象应该在这些标准组件之前开始（并在它们之后停止）。对于任何正phase值，顺序相反。

SmartLifecycle定义的Stop方法接受回调。任何实现都必须在该实现的关闭过程完成后调用该回调的run（）方法。在需要的时候，可以实现异步的关闭，因为LifecycleProcessor接口的默认实现DefaultLifecycleProcessor在每个phase中的对象中调用该回调时，会等待一个超时值。每个phase默认超时为30秒。你可以通过在上下文中定义一个名为lifecycleProcessor的bean来覆盖默认的生命周期处理器实例。如果只想修改超时，那么定义以下内容就足够了：

```
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- timeout value in milliseconds -->
    <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
```

如前所述，LifecycleProcessor接口还定义了用于刷新和关闭上下文的回调方法。当stop（）被显示调用的时候会启动shutdown的进程，但是这个方法只会在上下文关闭时被调用。另一方面，“刷新”回调启用了SmartLifecycle bean的另一个功能。刷新上下文时（在所有对象都已实例化和初始化之后），将调用该回调。此时，默认的生命周期处理器检查每个SmartLifecycle对象的isAutoStartup（）方法返回的布尔值。如果为true，则该对象将在该点启动，而不是等待显式调用上下文或其自身的start（）方法（与上下文refresh不同，对于标准上下文实现，上下文start不会自动触发）。如前所述，phase值和任何“depends-on”关系决定启动顺序。

**在非Web应用程序中优雅的关闭Spring IoC容器**

> 本小节只适用于非web的应用程序。当web应用程序关闭时，Spring的web基础的ApplicationContext实现，已经有代码优雅的关闭 Spring IoC 容器。

如果你使用Spring IOC容器在非web应用程序环境中（富文本桌面程序），需要注册一个shutdown hook到JVM中。这样将保证优雅的关闭，并且在单例bean中调用相关的销毁方法，让所有的资源得到释放。当然，你必须正确的配置和实现这些销毁方法。

调用ConfigurableApplicationContext接口中的registerShutdownHook()来注册一个shutdown hook， 如下所示：

```
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        // add a shutdown hook for the above context...
        ctx.registerShutdownHook();

        // app runs here...

        // main method exits, hook is called prior to the app shutting down...
    }
}
```

## 1.6.2 ApplicationContextAware 和 BeanNameAware

当ApplicationContext创建了org.springframework.context.ApplicationContextAware接口的一个实现对象时。实例提供了一个对ApplicationContext的引用。下面是ApplicationContextAware的定义：

```
public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
```

因此，bean可以手动控制创建他的ApplicationContext，可以通过ApplicationContext接口，也可以将这个引用转换成ApplicationContext的子类（比如提供了额外功能的ConfigurableApplicationContext）。

他的一个应用就是在程序中获取其他的beans。这种使用方式有时候会很有用，但是因为其和Spring耦合在一起，违背了IOC原则，所以应该避免这样使用。应该将协作者设置成bean的属性值。

ApplicationContext的其他方法提供了文件资源，发布应用程序事件和访问MessageSource的功能。

在Spring2.5中，自动装载是另外一个获取到ApplicationContext引用的方法。传统的constructor和byType自动装载模式，能将ApplicationContext当做constructor的参数，或者setter方法的参数。为了更灵活的使用，比如自动装载字段或者方法的多个参数，可以使用@Autowired注解在字段或者方法上。这样ApplicationContext将会自动装配到需要的地方。

当ApplicationContext创建了一个org.springframework.beans.factory.BeanNameAware的实现类时，这个类提供了对其在关联对象中定义的名字的引用。 如下所示：

```
public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}
```

在填充普通bean属性之后，在初始化回调（如InitializingBean、afterPropertiesSet或自定义init方法）之前该回调会被调用。

## 1.6.3 其他的Aware接口

除了ApplicationContextAware和BeanNameAware，Spring提供了很多Aware回调接口，让bean向容器表明它们需要依赖某种基础结构。通常来说，名字表明了依赖的类型，下表是非常重要的Aware接口列表：

| 名字                             | 注入依赖                                                              |
| ------------------------------ | ----------------------------------------------------------------- |
| ApplicationContextAware        | 声明ApplicationContext                                              |
| ApplicationEventPublisherAware | 封闭ApplicationContext的事件发布者。                                       |
| BeanClassLoaderAware           | 装载bean classes的类加载器                                               |
| BeanFactoryAware               | 声明BeanFactory                                                     |
| BeanNameAware                  | 声明bean的名字                                                         |
| BootstrapContextAware          | 容器运行的BootstrapContext资源适配器，通常只用在 JCA-aware 的ApplicationContext实例中 |
| LoadTimeWeaverAware            | 在加载时为处理类定义而定义的weaver。                                             |
| MessageSourceAware             | 处理消息所配置的策略（支持参数化和国际化）                                             |
| NotificationPublisherAware     | Spring JMX 通知publisher                                            |
| ResourceLoaderAware            | 为资源的low-level访问配置加载器                                              |
| ServletConfigAware             | 当前容器运行的ServletConfig，只在web-aware 的Spring ApplicationContext有效     |
| ServletContextAware            | 当前容器运行的ServletContext，只在web-aware 的Spring ApplicationContext有效    |

再次强调，使用这些接口将会把你的代码和Spring API相耦合，违背了控制反转原则。因此我们只推荐需要访问容器的基础架构bean中使用它们。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flydean.com/spring-framework-documentation5/core-technologies/1.the-ioc-container/1.6customizing-the-nature-of-a-bean.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
