一起来读官方文档-----SpringIOC(06)

1.6。自定义bean的性质

Spring框架提供了许多接口,可用于自定义Bean的性质。本节将它们分组如下:

  • 生命周期回调Lifecycle Callbacks
  • ApplicationContextAware 和 BeanNameAware
  • 其他Aware
1.6.1。生命周期回调

为了与参与容器对bean生命周期的管理,您可以实现Spring 的InitializingBean和DisposableBean接口。
容器初始化会调用 afterPropertiesSet()方法和销毁会调用destroy()方法来执行某些操作。

通常,@PostConstruct和@PreDestroy注解被认为是在Spring应用程序中接收生命周期回调的最佳实践。
使用这些注解意味着您的bean没有耦合到特定于Spring的接口。

如果你不希望使用JSR-250注解,但你仍然要删除的耦合,考虑init-method和destroy-methodbean定义元数据。


public class InitAndDestroyBean implements InitializingBean, DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean init" );
    }

    @PostConstruct
    public void init(){
        System.out.println("PostConstruct init");
    }

    @PreDestroy
    public void destroy2(){
        System.out.println("PreDestroy destroy");
    }

    public void initMethod(){
        System.out.println("Init-method init");
    }

    public void destroyMethod(){
        System.out.println("Destroy-method destroy");
    }
}


    

控制台打印:
    PostConstruct init
    InitializingBean init
    Init-method init
    .
    .
    .
    PreDestroy destroy
    DisposableBean destroy
    Destroy-method destroy

在内部,Spring框架使用BeanPostProcessor实现来处理它可以找到的任何回调接口并调用适当的方法。如果您需要自定义功能或其他Spring默认不提供生命周期行为,则您可以BeanPostProcessor自己实现。

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

上述的Lifecycle类上有这么一个注释
 * 

Note that the present {@code Lifecycle} interface is only supported on * top-level singleton beans. On any other component, the {@code Lifecycle} * interface will remain undetected and hence ignored. Also, note that the extended * {@link SmartLifecycle} interface provides sophisticated integration with the * application context's startup and shutdown phases. 一开始看没看懂top-level singleton beans的含义自己试了一下实现Lifecycle @Component public class CustomLifecycle implements Lifecycle { @Override public void start() { System.out.println("start-----------"); } @Override public void stop() { System.out.println("stop-----------"); } @Override public boolean isRunning() { return false; } } 控制台输出 PostConstruct init InitializingBean init Init-method init PreDestroy destroy DisposableBean destroy Destroy-method destroy 这些代码并没有起作用 调试了源码发现在启动关闭容器的时候,容器确实找到Lifecycle的所有实现类但是还有一个if操作 if (bean instanceof SmartLifecycle) { . . . } 更改了继承关系 @Component public class CustomSmartLifecycle implements SmartLifecycle { @Override public void start() { System.out.println("start-----------"); } @Override public void stop() { System.out.println("stop-----------"); } @Override public boolean isRunning() { return false; } } 控制台输出 PostConstruct init InitializingBean init Init-method init start----------- PreDestroy destroy DisposableBean destroy Destroy-method destroy 这才稍微理解那段注释,意思大概就是如果你直接继承自Lifecycle接口, 那么容器不会调用你的自定义接口,只有继承SmartLifecycle才行

本节介绍了生命周期回调接口。
初始化回调

org.springframework.beans.factory.InitializingBean接口允许容器在bean上设置了所有必要的属性之后执行初始化工作。

InitializingBean接口指定了一个方法:

void afterPropertiesSet() throws Exception;

我们建议您不要使用该InitializingBean接口,因为它将您的代码耦合到Spring。
另外,我们建议使用@PostConstruct注释或指定POJO初始化方法。
对于基于XML的配置元数据,可以使用init-method属性指定具有无效无参数签名的方法的名称。
通过Java配置,您可以使用的initMethod属性@Bean。

请参阅接收生命周期回调。考虑以下示例:


public class ExampleBean {
        public void init() {
            // do some initialization work
        }
}
您可以使用xml
    
或者JAVA Bean配置
    @Bean(initMethod = "init")
    public void exampleBean(){
        return new ExampleBean();
    }

前面的示例与下面的示例几乎具有完全相同的效果:



public class AnotherExampleBean implements InitializingBean {

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

但是,前面两个示例中的第一个示例并未将代码耦合到Spring。

销毁回调

org.springframework.beans.factory.DisposableBean当包含该接口的容器被销毁时,实现该接口可使Bean获得回调。

该DisposableBean接口指定一个方法:

void destroy() throws Exception;

我们建议您不要使用DisposableBean回调接口,因为它将您的代码耦合到Spring。
另外,我们建议使用@PreDestroy注释或指定bean定义支持的通用方法。
使用基于XML的配置元数据时,您可以在destroy-method上使用属性
通过Java配置,您可以使用的destroyMethod属性@Bean。
考虑以下定义:

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

您可以使用xml
    

或者JAVA Bean配置
    @Bean(destroyMethod = "cleanup")
    public void exampleBean(){
        return new ExampleBean();
    }

前面的定义与下面的定义几乎具有完全相同的效果:



public class AnotherExampleBean implements DisposableBean {
    @Override
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}

但是,前面两个定义中的第一个没有将代码耦合到Spring。

您可以为元素的destroy-method属性指定一个特殊的(inferred)值,
该值指示Spring自动检测特定bean类上的公共close 或shutdown 方法(任何实现java.lang.AutoCloseable or java.io.Closeable也会匹配)。

@Component
public class CustomAutoCloseable implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("AutoCloseable close");
    }
}

@Component
public class CustomCloseable implements Closeable {
    @Override
    public void close()  {
        System.out.println("Closeable close");
    }
}

控制台输出:
    Closeable close
    AutoCloseable close
    
您还可以在元素的default-destroy-method属性上设置这个特殊的(inferred)值,
以便将此行为应用于整个bean集合。
默认初始化和销毁方法

当您编写初始化和销毁没有使用特定的spring的InitializingBean和DisposableBean回调接口的特定方法时,
您通常编写具有init()、initialize()、dispose()等名称的方法。
理想情况下,这种生命周期回调方法的名称在整个项目中标准化,
以便所有开发人员使用相同的方法名称并确保一致性。

您可以配置Spring容器来“查找”已命名的初始化,并销毁每个bean上的回调方法名称。
这意味着,作为应用程序开发人员,您可以编写应用程序类并使用名为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.");
        }
    }
}

然后,您可以在类似于以下内容的Bean中使用该类:


    
        
    

在顶级元素属性中出现的default-init-method属性会导致Spring IoC容器识别bean类中名为init的方法作为初始化方法回调。
在创建和组装bean时,如果bean类有这样的方法,则会在适当的时候调用它。

您可以使用顶级元素上的default-destroy-method属性来配置销毁方法回调(也就是在XML中)。

如果现有的bean类已经有了根据约定命名的回调方法,那么您可以通过使用本身的init-method和destroy-method属性来指定(在XML中,也就是)方法名来覆盖默认值。

Spring容器保证在为bean提供所有依赖项后立即调用已配置的初始化回调。  
因此,初始化回调是在原始bean引用上调用的,这意味着AOP拦截器等还没有应用到bean。  
所以AOP执行顺序是先创建一个目标bean,然后再应用一个带有拦截器链的AOP代理(例如)。 
因此,将拦截器应用到init方法是不适用的。
组合生命周期机制

从Spring 2.5开始,您可以使用三个选项来控制Bean生命周期行为:

  • InitializingBean和 DisposableBean回调接口
  • 自定义init()和destroy()方法
  • 在@PostConstruct和@PreDestroy 注释。

您可以结合使用这些机制来控制给定的bean。

如果为一个bean配置了多个生命周期机制,并且为每个机制配置了不同的方法名称,则每个配置的方法都将按照此注释后列出的顺序运行。
但是,如果init()同时带有@PostConstruct注解和xml的init-method方法,那么这个init()方法就会被执行一次。

使用不同的初始化方法,为同一个bean配置的多个生命周期机制如下所示:
初始化方法的调用顺序:

  • 用注释的方法 @PostConstruct
  • afterPropertiesSet()由InitializingBean回调接口定义
  • 定制配置的init()方法

销毁方法的调用顺序相同:

  • 用注释的方法 @PreDestroy
  • destroy()由DisposableBean回调接口定义
  • 定制配置的destroy()方法
启动和关机回调

该Lifecycle接口为具有生命周期要求(例如启动和停止某些后台进程)的任何对象定义了基本方法:

public interface Lifecycle {
    void start();
    void stop();
    boolean isRunning();
}

任何Spring管理的对象都可以实现该Lifecycle接口。
然后,当 ApplicationContext自身接收到启动和停止信号时(例如,对于运行时的停止/重新启动场景),它将这些调用层叠到Lifecycle该上下文中定义的所有实现中。
它通过委派给LifecycleProcessor,如下面的清单所示:

public interface LifecycleProcessor extends Lifecycle {
    void onRefresh();
    void onClose();
}

请注意,LifecycleProcessor本身是Lifecycle 接口的扩展。它还添加了两种其他方法来响应正在刷新和关闭的上下文。

请注意,常规org.springframework.context.Lifecycle接口是用于显式启动和停止通知的普通协议,并不意味着在上下文刷新时自动启动。为了对特定bean的自动启动进行精细控制(包括启动阶段),请考虑实施org.springframework.context.SmartLifecycle。

另外,请注意,不能保证会在销毁之前发出停止通知。在常规关闭时,Lifecycle在传播常规销毁回调之前,所有Bean首先都会收到停止通知。但是,在上下文生命周期中进行热刷新或停止刷新尝试时,仅调用destroy方法。

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

public interface Phased {
    int getPhase();
}

以下清单显示了SmartLifecycle接口的定义:

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();
    void stop(Runnable callback);
}

启动时,相位最低的对象首先启动。
停止时,遵循相反的顺序。
因此,实现SmartLifecycle并getPhase()返回其方法的对象Integer.MIN_VALUE将是第一个开始且最后一个停止的对象。
在频谱的另一端,相位值Integer.MAX_VALUE表示该对象应最后启动并首先停止(可能因为该对象取决于正在运行的其他进程)。
当考虑相位值,同样重要的是要知道,对于任何“正常”的默认阶段 Lifecycle目标没有实现SmartLifecycle的0。
因此,任何负相位值都表明对象应在这些标准组件之前开始(并在它们之后停止)。对于任何正相位值,反之亦然。

SmartLifecycle的stop方法接受Runnable callback作为方法入参。

    default void stop(Runnable callback) {
        stop();
        callback.run();
    }

在该stop()的子类的实现执行完成之后,调用该callback.run()的方法。

由于LifecycleProcessor接口的默认实现DefaultLifecycleProcessor会在每个阶段内的对象组等待其超时值,以调用该回调,因此可以在必要时启用异步关闭。

默认的每阶段超时为30秒。
您可以通过定义lifecycleProcessor上下文中命名的bean来覆盖默认的生命周期处理器实例 。
如果只想修改超时,则定义以下内容即可:


    
    

正如前面提到的,LifecycleProcessor接口还定义了刷新和关闭上下文的回调方法。
后者驱动关闭过程,就好像是显式地调用了stop(),但是它发生在上下文关闭时。
另一方面,“刷新”回调可以实现SmartLifecycle bean的另一个特性。
当上下文被刷新时(在所有对象被实例化和初始化之后),回调被调用。
这时,默认的生命周期处理器会检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。
如果为真,则在该点启动该对象,而不是等待上下文或其自己的start()方法的显式调用(与上下文刷新不同,对于标准上下文实现,上下文启动不会自动发生)。
如上所述,相位值和任何“依赖”关系决定启动顺序。

    Map lifecycleBeans = getLifecycleBeans();
        Map phases = new HashMap<>();
        lifecycleBeans.forEach((beanName, bean) -> {
            //DefaultLifecycleProcessor 调用该方法autoStartupOnly始终未true
            //如果bean  instanceof SmartLifecycle 并且isAutoStartup()方法返回true
            if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
                int phase = getPhase(bean);
                LifecycleGroup group = phases.get(phase);
                if (group == null) {
                    group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
                    phases.put(phase, group);
                }
                group.add(beanName, bean);
            }
        });
        if (!phases.isEmpty()) {
            List keys = new ArrayList<>(phases.keySet());
            Collections.sort(keys);
            for (Integer key : keys) {
                phases.get(key).start();
            }
        }
    }
在非Web应用程序中正常关闭Spring IoC容器

本节仅适用于非Web应用程序。
Spring的基于Web的 ApplicationContext实现已经有了相应的代码,可以在相关Web应用程序关闭时正常关闭Spring IoC容器。

如果您在非web应用程序环境中(例如,在富客户机桌面环境中)使用Spring的IoC容器,请向JVM注册一个关机钩子。
这样做可以确保优雅地关闭,并调用单例bean上的相关销毁方法,从而释放所有资源。
您仍然必须正确配置和实现这些销毁回调。

要注册一个关机钩子,调用registerShutdownHook()方法,该方法在ConfigurableApplicationContext接口上声明,如下面的例子所示:

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的引用。下面的清单显示了applicationcontext taware接口的定义:

public interface ApplicationContextAware {
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

因此,bean可以通过ApplicationContext接口或通过将引用转换为该接口的已知子类(比如ConfigurableApplicationContext,它公开了其他功能),以编程方式操作创建它们的ApplicationContext。
一种用途是对其他bean进行编程检索。
有时这个功能是有用的。
但是,通常应该避免使用它,因为它将代码与Spring结合在一起,并且不遵循控制反转风格,在这种风格中协作者作为属性提供给bean。
ApplicationContext的其他方法提供了对文件资源的访问、发布应用程序事件和访问消息源。
这些附加特性在ApplicationContext的附加功能中进行了描述。

自动装配是获得对ApplicationContext的引用的另一种选择。
传统的constructor和byType自动装配模式可以分别为构造函数参数或setter方法参数提供类型ApplicationContext的依赖关系。

为了获得更大的灵活性,包括自动装配字段和多个参数方法的能力,可以使用基于注解的自动装配特性。如果你这样做了,ApplicationContext就会自动生成一个字段、构造函数参数或方法参数,如果有问题的字段、构造函数或方法带有@Autowired注解,那么这些参数期望得到ApplicationContext类型。

说了半天不如看代码:
public class CustomAutowiringApplicationContext {
    @Autowired
    private ApplicationContext applicationContext;

    public CustomAutowiringApplicationContext(ApplicationContext applicationContext){
        System.out.println(applicationContext);
    }

    public void setApplicationContext(ApplicationContext applicationContext){
        System.out.println(applicationContext);
    }
}
//只有配置byType 或者 byName set注入才会生效

当ApplicationContext创建一个实现org.springframework.beans.factory.BeanNameAware的类时。BeanNameAware接口,提供当前bean的名称的引用。下面的清单显示了BeanNameAware接口的定义:

public interface BeanNameAware {
    void setBeanName(String name) throws BeansException;
}

源码:
    private void invokeAwareMethods(String beanName, Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ClassLoader bcl = getBeanClassLoader();
                if (bcl != null) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                }
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }
执行顺序源码:
    if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction) () -> {
                //处理aware 如上方法
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            //执行初始化方法如InitializingBean、afterPropertiesSet或自定义init-method
            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);
        }
 
 

BeanNameAware,BeanClassLoaderAware,BeanFactoryAware 执行是在填充普通bean属性之后,但在初始化回调(如InitializingBean、afterPropertiesSet或自定义init-method)之前调用回调。

1.6.3。其他Aware介面

除了applicationcontext taware和BeanNameAware(前面讨论过)之外,Spring还提供了广泛的可感知回调接口,让bean向容器表明它们需要某种基础设施依赖关系。作为一般规则,名称表示依赖类型。下表总结了最重要的感知接口:

表4.Aware接口 好多没用过,没法做详细解释后续再补充,此处进行简单罗列
名称 注入的对象
ApplicationContextAware ApplicationContext
ApplicationEventPublisherAware 事件发布者ApplicationContext
BeanClassLoaderAware 类加载器,用于加载Bean类。
BeanFactoryAware BeanFactory
BeanNameAware bean的名称。
BootstrapContextAware BootstrapContext容器在其中运行的资源适配器。
通常仅在支持JCA的ApplicationContext实例中可用。
LoadTimeWeaverAware 定义了在加载时处理类定义的weaver
MessageSourceAware 解决消息的已配置策略(支持参数化和国际化)
NotificationPublisherAware Spring JMX通知发布者
ResourceLoaderAware 已配置用于解析消息的策略(支持参数化和国际化)
ServletConfigAware 当前ServletConfig容器在其中运行。
仅在WebApplicationContext环境下运行
ServletContextAware 当前ServletContext容器在其中运行。
仅在WebApplicationContext环境下运行

再次注意,使用这些接口会将您的代码与Spring API绑定在一起,并且不遵循“控制反转”样式。因此,我们建议将它们用于需要以编程方式访问容器的基础结构Bean。

1.7。Bean Definition继承

bean定义可以包含许多配置信息,包括构造函数参数、属性值和特定于容器的信息,比如初始化方法、静态工厂方法名,等等。子bean定义从父bean定义继承配置数据。子定义可以覆盖某些值,也可以根据需要添加其他值。使用父bean和子bean定义可以节省大量输入。实际上,这是一种模板形式。

如果您以编程方式使用ApplicationContext接口,则子bean定义由ChildBeanDefinition类表示。大多数用户不会在这个级别上使用它们。相反,它们在类(如ClassPathXmlApplicationContext)中声明性地配置bean定义。当您使用基于xml的配置元数据时,您可以通过使用父属性来指示子bean定义,并将父bean指定为该属性的值。下面的例子展示了如何做到这一点:


    
    


  
        
    
    

如果没有指定bean 的class属性,子bean定义使用父定义中的bean的class,但是也可以覆盖它。在后一种情况下,子bean类必须与父bean兼容(也就是说,它必须接受父bean的属性值)。

子bean定义从父bean继承scope、构造函数参数值、属性值,并且覆盖父类方法,并提供添加新值的选项。
您指定的任何Scope、初始化方法、销毁方法或静态工厂方法设置都会覆盖相应的父设置。

其余的设置总是取自子定义:依赖、自动装配模式、依赖检查、单例和 lazy init。

前面的示例通过使用抽象属性显式地将父bean定义标记为抽象。如果父定义没有指定类,则需要显式地将父bean定义标记为抽象,如下面的示例所示:


    
    



    
     

父bean不能自己实例化,因为它不完整,而且它还显式地标记为抽象。当定义是抽象的时候,它只能作为纯模板bean定义使用,作为子定义的父定义。试图单独使用这样一个抽象的父bean,通过引用它作为另一个bean的ref属性,或者使用父bean ID执行显式的getBean()调用,都会返回错误。类似地,容器的内部预实例化esingletons()方法忽略被定义为抽象的bean定义。

默认情况下,ApplicationContext预实例化所有单例。
因此,如果你有一个(父)bean定义你只打算使用作为模板,
并且这个父bean定义指定了一个类,您必须确保设置抽象属性为true,
否则应用程序上下文会(试图)预实例化abstract的bean。

你可能感兴趣的:(spring)