百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Spring官方文档解读(八) spring documentation

lipiwang 2024-10-31 15:25 25 浏览 0 评论

此篇解读自定义bean的功能。

Spring框架提供了许多接口,用于自定义bean的一些功能。

生命周期回调

为了容器与bean生命周期的交互,可以实现InitializingBean和DisposableBean接口。容器回调 afterPropertiesSet()和destroy()使bean在初始化和销毁bean时执行某些操作。

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

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

  • 初始化回调

InitializingBean接口,在容器设置好bean所必须的属性后才初始化,这个接口含有一个方法:

void afterPropertiesSet() throws Exception;

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

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

    public void init() {
        // 初始化工作,需要配置其初始化属性指定,可以自由选择是否放在spring中
    }
}

另一种写法是

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
  
  public class AnotherExampleBean implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        // 初始化工作,此代码必然存在于spring中
    }
}
  • 销毁回调

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

void destroy() throws Exception;

同上面一样,不建议使用,会将不必要的条码耦合到spring中,建议使用@PreDestroy注释或指定bean定义支持的通用方法。XML中可以使用属性destroy-method来指定回调方法,也可以使用注解@Bean的属性destroyMethod。

销毁回调使用与初始化回调相似,这里不作例了。

注:可以在destroy-method属性上指定一个特殊的值:inferred。此值可以使spring自动检测特定bean类(实现了AutoCloseable或Closeable接口)上的公共close或者shutdown方法。还可以在default-destroy-method属性上设置此值,这样这个自动检测会作用在整个<beans>集上。

  • 默认初始化和销毁??方法

当不使用提供的InitializingBean和DisposableBean接口所提供的方法,自定义方法名通常来说一般是init(),initialize(),dispose()...,在理想情况下回调方法名称应该在整个项目中标准化,以便协同工作中使用相同的方法名来保持一致。

可以让spring配置让容器去查找这些标准化的回调方法名称,因此在程序中使用init()方法即可,而不用指定init-method="init"属性,同时关于销毁方法使用destroy()即可不指定destroy-method属性。

在顶级<beans/>元素属性上使用default-init-method(或default-destroy-method)属性导致容器在其初始回调和销毁回调使用指定的方法名称。此方法可以在一些约定方法命名和spring官方推荐的不一致的情况下,覆盖默认的官方命名法。

<beans default-init-method="init">
    <bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>
</beans>

public class DefaultBlogService implements BlogService {
    private BlogDao blogDao;
    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}

Spring容器保证在其为所有bean配置所有依赖项后立即调用初始化回调,因此回调针对的是原始bean,也就是说此时AOP拦截器尚未应用于原始bean,目标Bean创建后,然后拦截链应用AOP代理,如果目标bean和代理分别定义,代码甚至可以绕过代理与原始bean交互,因此将init方法应用在拦截器中是矛盾的,因为这样做会将目标Bean的生命周期耦合到其代理或拦截器,并在代码中直接与原始目标Bean交互时留下奇怪的语句。

  • 组合生命周期机制

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

  1. 在InitializingBean和 DisposableBean接口中回调;
  2. 习惯上使用init()和destroy()方法;
  3. 在@PostConstruct和@PreDestroy注释。

可以结合使用这些机制来控制给定的bean,以上也是其回调方法执行顺序(接口、默认方法、注解,运行三次),但是假如三个顺序中使用的都是init()方法的话,则该方法只运行一次。

  • 开启和关闭回调

Lifecycle接口定义了自定义生命周期类的基本方法(比如启动、停止某些后台进程):

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

任何Spring管理的对象都可以实现该Lifecycle接口。然后当 ApplicationContext容器接收到启动和停止信号时(例如运行时的停止/重新启动场景),它将这些调用级联到Lifecycle在该容器定义的所有实现,通过委派给LifecycleProcessor:

//注意其继承Lifecycle接口,增加了两个方法刷新和关闭
public interface LifecycleProcessor extends Lifecycle {
    void onRefresh();
    void onClose();
}

Lifecycle接口是用于显式启动和停止通知的普通协议,并不意味着在容器刷新时自动启动。为了对特定bean的自动启动进行精细控制(包括启动阶段),可以使用SmartLifecycle接口。

bean之间存在依赖关系的话,关于其启动关闭顺序,注意依赖方后启动且先关闭。

但是有时候不存在直接依赖,比如数据库驱动程序的加载,static静态块中依赖的对象是没有直接依赖在属性上,SmartLifecycle接口的上级接口Phased中有一个方法getPhase():

public interface Phased {
    int getPhase();
}

public interface SmartLifecycle extends Lifecycle, Phased {
    boolean isAutoStartup();
    void stop(Runnable callback);
}

在启动时,最低相位的对象先启动,停止时这个对象反而是最后一个停止。

因此,实现了上面接口,其中的getPhase方法返回Integer.MIN_VALUE的话,则是first start and last stop。同理Integer.MAX_VALUE为last start and first stop。

getPhase()方法默认值没有0这个值,负数表示对象在标准组件之前开始,并在标准组件停止之后才停止。

SmartLifecycle定义的stop方法接受回调,任何其实现在其关闭完成后必须调用Runnable的回调方法run()。由于LifecycleProcessor接口的默认实现DefaultLifecycleProcessor会在每个阶段内的对象组等待其超时,以调用该stop回调,因此可以在必要时启用异步关闭。默认的每阶段超时为30秒。可以通过定义lifecycleProcessor这个的bean来覆盖默认的生命周期处理器实例 。

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

LifecycleProcessor接口还定义了用于刷新和关闭容器的回调方法。后者驱动关闭过程,就好像显式调用stop()一样,但是它在容器关闭时发生。

“刷新”回调启用了SmartLifecycle bean的另一个功能 。在容器刷新时(在所有bean初始化和实例化后)回调,默认的生命周期处理器都将检查每个SmartLifecycle对象的isAutoStartup()方法的返回值,如果为true,则在该点启动对象,而不是等待容器或其自身的显示调用start()方法(与容器刷新不同,对于标准容器,在容器启动时不会发生)。

  • 在非Web应用程序中正常关闭Spring IoC容器

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

要注册关闭勾子,请调用ConfigurableApplicationContext接口上声明的registerShutdownHook()方法,如以下示例所示:

public final class Boot {
    public static void main(final String[] args) throws Exception {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        // 注册关闭勾子
        ctx.registerShutdownHook();.
    }
}

ApplicationContextAware和BeanNameAware

当一个容器创建了一个实现了ApplicationContextAware接口的对象实例时,这个对象实例获取了一个对容器的引用,ApplicationContextAware接口定义:

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

因此,可以编程来操纵ApplicationContext创建的beans,通过ApplicationContext接口或已知接口的子类转为的引用(比如ConfigurableApplicationContext)。一种用途是通过编程方式获取其他bean,虽然很有用,但是它将代码耦合到了spring中并且不遵循控制反转,因此应该避免使用。ApplicationContext提供了其他方法如文件访问,发布应用程序事件和访问MessageSource这些附加功能。

自动装配是另一种获取ApplicationContext引用的替代,传统的构造器和byType自动装配模式是提供构造器参数或者setter方法参数,如果要获得更大的灵活性包括能够自动注入属性或者多参数方法请使用基于注解的自动装配(@Autowired),这样容器会自动注入其所需要的属性字段于构造函数参数或者方法参数中。

实现BeanNameAware接口,使得该类提供一个在其相关对象中定义的名称的引用。

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

这个接口方法在常用bean配置完成之后,初始化回调之前调用。

其他 Aware 接口

其他的Aware接口,可以使bean向容器提示其需要的其他基础依赖,一般在接口名称上可以看出其依赖类型。

  • ApplicationContextAware: ApplicationContext的声明
  • ApplicationEventPublisherAware:容器中封闭的事件上发布
  • BeanClassLoaderAware:类加载器用于加载beanClass
  • BeanFactoryAware:BeanFactory的声明
  • BeanNameAware:声明bean的名称
  • LoadTimeWeaverAware:定义的weaver ,用于在加载时处理类定义
  • MessageSourceAware:配置消息处理策略,支持参数化和国际化
  • NotificationPublisherAware:Spring JMX通知发布者
  • ResourceLoaderAware:配置加载低级资源访问的策略
  • ServletConfigAware:配置容器运行的ServletConfig,仅在网络中有效。
  • ServletContextAware:配置容器运行的ServletContext,仅在网络中有效。

注意使用这些接口会将代码与Spring API绑定在一起,并且不遵循“控制反转”模式。

建议通过容器编程的方式使用这些接口来访问底层bean。

相关推荐

前端 JavaScript 字符串中提取数字

varstr="4500元";varnum=parseInt(str);alert(num);//4500如果字符串前面有非数字字符,上面这种方法就不行了:var...

使用JavaScript如何获取网站网址(js如何获取浏览器信息)

在做网站开发时,我们有时候会获取当前页面的完整路径。在网页前端如何实现呢?请在网页脚本代码段中粘贴如下代码。functiongetRootPath(){//获取当前网址,...

java文本对比工具源码8(java比较文本相似度)

/***ParseatextualrepresentationofpatchesandreturnaListofPatch*objects.*@paramtextline...

JavaScript实现的9大排序算法(js排序方法)

笔试面试经常涉及各种算法,本文简要介绍常用的一些算法,并用JavaScript实现。1、插入排序1)算法简介插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通...

使用函数化的Javascript代码编写方式

对于Javascript介绍想必大家都耳熟能详啦,基于函数化的编程语言,基于浏览器运行的编程语言,web开发语言,前端开发必备语言,blablabla...Javascript是一个非常灵...

Js基础31:内置对象(js内置对象是什么意思)

js里面的对象分成三大类:内置对象ArrayDateMath宿主对象浏览器提供的对象(如bom、dom等等)自定义对象开发人员自己定义的对象内置对象——所谓内置对象,就是JavaScript自...

js获取上传文件类型以及大小的方法

前端web上传文件时,需要在上传之前判断一下文件的类型以及文件的大小,HTML为前端的标记语言是无法做到这一点,只能使用javascript动态脚本代码来实现。js获取上传文件大小的方法示例代码:&...

黑客入门实践:如何绕过前端过滤上传文件

今天开始,我们就要开始学习具体的漏洞了,但是希望大家学完后,不要轻易"入侵"网站哦,当时测试环境下除外。今天的课程是关于“文件上传漏洞”,据安界网的老师介绍,文件上传漏洞仅次于命令执行...

Java文件上传细讲(java文件夹上传)

什么是文件上传?文件上传就是把用户的信息保存起来。为什么需要文件上传?在用户注册的时候,可能需要用户提交照片。那么这张照片就应该要进行保存。免费学习资料获取方式上传组件(工具)为什么我们要使用上传工具...

鸿蒙上实现“翻译”功能(鸿蒙宴全文翻译)

本章节我们来制作中文翻译成英文的实例(运行在HarmonyOS上),通过HTTP去配合API进行实现。需求分析如下:文字输入HTTP协议使用文字翻译控件介绍①HTTP数据请求官方文档请求...

JavaScript从入门到精通(javascript 入门教程)

前几天,我们学习了JavaScript的入门课程,但是要想做网站,仅仅学会入门是不够的,今后的几天,我将带领大家精通JavaScript,希望大家好好学习!JS内置对象String对象:字符串对象,提...

第15天|16天搞定前端,javascript语法篇(干货)

JavaScript是互联网上最流行的脚本语言,这门语言可用于HTML和web,可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。它是一个脚本语言,它是一个轻量级,但功能强大的编程...

通过js来实现打字效果(js如何输入)

有时候浏览网页经常会看见一些页面出现一些打字的效果,那么是怎么实现的呢?逻辑确定目标容器,在哪个容器进行输出确定输出内容,当前直接根据目标容器确定输出内容即可需要控制输出频率,因此需要循环输出完毕代码...

手把手教你学会一键还原混淆js原理

1.短变量名在以下示例代码中,我们将变量“customerName”替换为“a”:vara="JohnSmith";console.log(a);2.随机变量名在以下示例代码...

sql中常用的字符串函数详解(sql字符串函数有哪些)

在日常开发中遇到处理最多的可能字符串要算其中一个了,什么替换啊截取啊大小写转换啊、删除空格啊等等,这些操作我们可以在前端操作,也可以直接在数据库的sql中操作,那么我们来看一下sql中处理字符串的...

取消回复欢迎 发表评论: