Spring IoC容器(二)Bean的自定义及容器的扩展

 Spring提供了一些接口来让我们定制bean 及扩展容器。

1 定制Bean

我们可以通过bean及容器的生命周期回调及一些Aware接口来定制bean。

1.1 生命周期回调

1.1.1 InitializingBean 与 DisposableBean

我们可以通过让Bean 实现InitializingBean 及DisposableBean 接口,当容器初始化完bean后会调用InitializingBean的afterPropertiesSet()方法,当容器销毁bean前会调用DisposableBean接口的destroy()方法。

上面这两个接口方法在JSR-250中,可以使用@PostConstruct 及@PreDestory注释来代替。

也可以在xml的bean标签的init-method 的属性指定bean中的一个实例方法来作为初始化方法(与afterPropertiesSet()作用一致)。

在xml的bean标签的destory-method属性指定bean中的一个实例方法作为销毁方法(与destroy()作用一致)。

在beans 标签中通过default-init-method及default-destroy-method属性用来指定bean默认的初始化及销毁的方法名。

可以将bean中指定初始化方法(销毁方法)、继承InitializingBean或DisposableBean及使用@PostConstruct或@PreDestory注释这三种方案组合一起使用。

初始化方法的执行顺序为:@PostConstruct -> afterPropertiesSet() -> init-method 属性。

销毁方法的执行顺序为:@PreDestory -> destroy() -> destroy-method属性。

public class InitService implements InitializingBean {public InitService() {System.out.println("initService 实例化");}private String name;public void setName(String name) {this.name = name;System.out.println("赋值完成:" + name);}public void customInit() {System.out.println("init-method指定的customInit()");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("afterPropertiesSet");}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/article/article.xml");applicationContext.getBean(InitService.class);}
}<bean class="article.InitService" init-method="customInit"/>

1.1.2 启动和关闭的回调

Lifecycle 接口定义了三个跟生命周期相关的方法:start() 启动,stop() 停止,isRuning() 是否运行。某些Io C容器实现了这个接口的方法,LifecycleProcessor接口扩展了Lifecycle接口并新增两个方法:onRefresh() 及 onClose()。

public interface Lifecycle {void start();void stop();boolean isRunning();
}public interface LifecycleProcessor extends Lifecycle {void onRefresh();void onClose();
}

当spring context被启动时会调用容器的start方法,当关闭时候会调用容器的stop()方法。同时bean也可以实现这个接口,当容器被调用start方法时,bean的start方法也会被调用。

在bean中,所依赖的bean会被先创建,也可以指定depend-on关系来控制bean的创建顺序。对于那些没有直接依赖关系的,但是我们想指定start()被调用顺序,这时bean可以通过实现Phased接口来控制被调用的顺序。该接口有个getPhase()方法返回一个int类型,值越小越先启动,越小越后关闭。而没有实现Phased接口的Bean,值为0.

注意,Lifecycle 是针对的是常规启动关闭情况,如果想在热启动或其他自动启动模式下触发,那么应该使用SmartLifecycle接口。

public class StartService1 implements Lifecycle, Phased {public StartService1() {System.out.println("StartService1 实例化");}private boolean running = false;@Overridepublic void start() {System.out.println("StartService1 启动");running = true;}@Overridepublic void stop() {System.out.println("StartService1 关闭");running = false;}@Overridepublic boolean isRunning() {return running;}@Overridepublic int getPhase() {return -1; // 比没有实现Phased接口的Bean更先调用start()}public static void main(String[] args) {ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext("/article/article.xml");applicationContext.start(); // 手动发出启动的信号}
}public class StartService2 implements Lifecycle {public StartService2() {System.out.println("StartService2实例化");}private boolean running = false;@Overridepublic void start() {System.out.println("StartService2 启动");running = true;}@Overridepublic void stop() {System.out.println("StartService2 关闭");running = false;}@Overridepublic boolean isRunning() {return running;}
}<bean class="article.StartService2" lazy-init="false"/>
<bean class="article.StartService1" lazy-init="false"/>

在上面的代码中,ConfigurableApplicationContext接口继承Lifecycle接口。而ClassPathXmlApplicationContext的一个祖先类AbstractApplicationContext 实现了ConfigurableApplicationContext的方法。

图 AbstractApplicationContext类中的start 方法

图 AbstractApplicationContext类中初始化生命周期处理器的方法

而在DefaultLifecycleProcessor类中的start方法内部,调用了该类的实例方法startBeans。

图 DefaultLifecycleProcessor 的startBeans方法

在非Web 环境的容器中,要安全的来关闭容器,需要调用ConfigurableApplicationContext的registerShutdownHook实例方法,来让JVM关闭前能调用容器的stop方法。

而Web环境的容器实现了安全关闭容器的方法。

1.2 Aware 接口

Spring 提供了广泛的Aware回调接口,让bean向容器指示它们需要特定的基础结构依赖关系。

ApplicationContextAware

向bean提供ApplicationContext变量。

ApplicationEventPublisherAware

发布ApplicationContext的事件。

BeanClassLoaderAware

用于加载bean类。

BeanFactoryAware

向bean提供BeanFactory变量。

BeanNameAware

向bean提供bean的命名。

BootstrapContextAware

当前容器里的资源适配器BootstrapContext

LoadTimeWeaverAware

定义的weaver用于在加载时处理类定义。

MessageSourceAware

配置解析消息的策略(支持参数化与国际化)。

NotificationPublisherAware

Spring JMX 通知的发布者。

ResourceLoaderAware

配置用于低级访问资源的加载器。

ServletConfigAware

当前在容器运行的ServletConfig。

ServletContextAware

当前在容器运行的ServletContext。

表 Spring提供的Aware接口

2 定义Bean的继承关系

子bean 可以从父bean继承配置数据,子bean可以根据需要覆盖某些值或添加其他值。使用这种继承关系可以节省大量的键入工作,这是一种模版形式。

如果bean 标签的abstract属性标志为true,那么这个bean将不会被实例化(class属性的值为空),但是可以被子bean继承。

public class ChildrenService {private String name;private Integer age;private String address;public void setName(String name) {this.name = name;}public void setAge(Integer age) {this.age = age;}public void setAddress(String address) {this.address = address;}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/article/article.xml");ChildrenService childrenService = applicationContext.getBean(ChildrenService.class);System.out.println(childrenService);}@Overridepublic String toString() {return "ChildrenService{" +"name='" + name + '\'' +", age=" + age +", address='" + address + '\'' +'}';}
}<bean id="fatherBean" abstract="true"><property name="name" value="默认名"/><property name="age" value="28"/></bean><bean class="article.ChildrenService" parent="fatherBean"><property name="name" value="黄先生"/><property name="address" value="深圳"/></bean>

3 容器扩展

可以通过向容器中插入实现了特定接口的实例来对容器进行扩展,而不必通过继承ApplicationContext的方式。

3.1 BeanPostProcessor

BeanPostProcessor 接口提供了可以覆盖容器实例化逻辑或依赖逻辑的方法。bean 先由容器实例化,然后由BeanPostProcessor进行操作。并且可以向容器中插入多个这里的实例,控制执行顺序,则还需要实现Ordered接口。

public class CustomBeanPostProcessor1 implements BeanPostProcessor, Ordered {@Overridepublic int getOrder() {return 1; // 越小越先执行}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("CustomBeanPostProcessor1 postProcessBeforeInitialization() 实例化之前");return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("CustomBeanPostProcessor1 postProcessAfterInitialization() 实例化之后");return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}public class CustomBeanPostProcessor2 implements BeanPostProcessor, Ordered {@Overridepublic int getOrder() {return 2;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("CustomBeanPostProcessor2 postProcessBeforeInitialization() 实例化之前");return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("CustomBeanPostProcessor2 postProcessAfterInitialization() 实例化之后");return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}<bean class="article.CustomBeanPostProcessor1"/>
<bean class="article.CustomBeanPostProcessor2"/>

3.2 BeanFactoryPostProcessor 定制配置元数据

IoC容器允许BeanFactoryPostProcessor 读取配置元数据,并可能在实例化之前更改元数据。同样允许配置多个BeanFactoryPostProcessor。当被注入到容器时会自动运行。

Spring 定义了多个实现了BeanFactoryPostProcessor的类。

3.2.1 PropertySourcesPlaceholderConfigurer

通过配置Properties的方式来取代bean中定义的占位符。

使用标准的Java Properties 格式将bean定义中的属性值外部化到一个单独的文件中。这样可以使部署时能自定义特定于环境的属性,如数据库host和帐号密码,而无需修改容器的一个或多个xml中bean定义的属性。

public class DataService {private String host;private String username;private String password;public void setHost(String host) {this.host = host;}public void setUsername(String username) {this.username = username;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "DataService{" +"host='" + host + '\'' +", username='" + username + '\'' +", password='" + password + '\'' +'}';}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/article/article.xml");DataService dataService = applicationContext.getBean(DataService.class);System.out.println(dataService);}
}// data.properties
jdbc.host=localhost
jdbc.username=root
jdbc.password=123<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"><property name="location" value="classpath:article/data.properties"/>
</bean>
<bean class="article.DataService"><property name="host" value="${jdbc.host}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
</bean>

3.2.1 PropertyOverrideConfigurer

可以覆盖任何bean 中的任何属性。

public class StudentService {private String name;private Integer age;public void setName(String name) {this.name = name;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "StudentService{" +"name='" + name + '\'' +", age=" + age +'}';}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/article/article.xml");StudentService studentService = applicationContext.getBean(StudentService.class);System.out.println(studentService); // 输出值 StudentService{name='newName', age=1}}
}<bean id="studentService" class="article.StudentService"><property name="name" value="小柚柚"/><property name="age" value="3"/></bean><bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer"><property name="properties"><props><prop key="studentService.name">newName</prop><prop key="studentService.age">1</prop></props></property></bean>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/450704.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试学习笔记-测试用例的编写

7中测试分类 按照阶段可划分单元测试、集成测试、系统测试、验收测试。代码可见度划分黑盒测试、灰盒测试、白盒测试 单元测试&#xff1a;针对源代码的测试 集成测试&#xff1a;针对接口进行测试 系统测试&#xff1a;针对功能和非功能的测试 验收测试&#xff1a;公测、内测…

在Vue中如何构建复杂表单?

概述 很有可能&#xff0c;在我们的软件工程旅程中&#xff0c;我们至少要构建一次复杂的表单。本文将介绍如何创建一个复杂的表单&#xff0c;该表单可以使用一些Vue特性(如v-for和v-model)逐步增强。它还提供了一些基本的Vue核心功能的复习&#xff0c;这些功能将在您日常使…

Vue引入Axios

1.命令安装axios和vue-axios npm install axios --save npm install vue-axios --save 2.package.json查看版本 3.在main.js中引用 import axios from axios; import VueAxios from vue-axios; Vue.use(VueAxios,axios) 4.如何使用 &#xff08;初始化方法&#xff09; 将下列代…

sqli.labs靶场(29到40关)

29、第二十九关 id1 id1 尝试发现是单引号闭合&#xff0c; -1 union select 1,2,3-- -1 union select 1,2,database()-- -1 union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schemasecurity)-- -1 union select 1,2,(select…

跳表详解和实现|深挖Redis底层数据结构

文章目录 跳表前言项目代码仓库认识跳表跳表的实现思路跳表性能分析对比平衡树&#xff08;avl和红黑树&#xff09;和哈希表使用手册成员变量成员函数构造析构迭代器sizeclearemptyoperatorfindinserterase 跳表细节实现节点定义跳表结构定义构造、析构、拷贝构造和赋值重载si…

vue3 之 组合式API—reactive和ref函数

ref&#xff08;&#xff09; 作用&#xff1a;接收简单类型或者对象类型的数据传入并返回一个响应式的对象 核心步骤&#xff1a; 1️⃣ 从 vue 包中导入 ref 函数 2️⃣在 <script setup>// 导入import { ref } from vue// 执行函数 传入参数 变量接收const count …

QT 应用中集成 Sentry

QT 应用中集成 Sentry QT应用中集成 SentrySentry SDK for C/C注册 Sentry 账号QT 应用中集成 Sentry触发 Crash 上报 QT应用中集成 Sentry Sentry 是一个开源的错误监控和日志记录平台&#xff0c;旨在帮助开发团队实时捕获、跟踪和解决软件应用程序中的错误和异常。它提供了…

开关电源学习之Buck电路

一、引言 观察上方的电路&#xff0c;当开关闭合到A点时&#xff0c;电流流过电感线圈&#xff0c;形成阻碍电流流过的磁场&#xff0c;即产生相反的电动势&#xff1b;电感L被充磁&#xff0c;流经电感的电流线性增加&#xff0c;在电感未饱和前&#xff0c;电流线性增加&…

07-使用Package、Crates、Modules管理项目

上一篇&#xff1a;06-枚举和模式匹配 当你编写大型程序时&#xff0c;组织代码将变得越来越重要。通过对相关功能进行分组并将具有不同功能的代码分开&#xff0c;您可以明确在哪里可以找到实现特定功能的代码&#xff0c;以及在哪里可以改变功能的工作方式。 到目前为止&…

Python学习从0到1 day12 Python数据容器.3.字符串与数据容器的切片操作

世界之大&#xff0c;从不在手心拙劣的掌纹之下 ——24.2.3 一、字符串的定义和操作 1.定义 字符串是字符的容器&#xff0c;一个字符串可以存放任意数量的字符 2.字符串的下标&#xff08;索引&#xff09; 和其他容器如&#xff1a;列表、元组一样&#xff0c;字符串也可以通…

react+ts+antd-mobile 动态tabs➕下拉加载

1.初始化项目 //搭建项目 npm create vitelatest react-jike-mobile -- --template react-ts//安装依赖 npm i //运行 npm run dev清理项目目录结构 安装ant design mobile ant design mobile是ant design家族里专门针对于移动端的组件库 npm install --save antd-mobile测试…

蓝桥杯第九届省赛题-----彩灯控制系统笔记

题目要求&#xff1a; 一、 基本要求 1.1 使用 CT107D 单片机竞赛板&#xff0c;完成“彩灯控制器”功能的程序设计与调 试&#xff1b; 1.2 设计与调试过程中&#xff0c;可参考组委会提供的“资源数据包”&#xff1b; 1.3 Keil 工程文件以准考证号命名&#xff0c…