想减少代码量,快设置一个有感知的 Aware Spring Bean
摘要:正常情况下,Spring 中的 Bean 对 Spring 是无感知的,Spring 框架提供了这种扩展能力,能让一个 bean 成为有感知的。
本文分享自华为云社区《有感知的 Aware Spring Bean》,作者:陈皮的JavaLib。
有感知能力的 Spring Bean
正常情况下,Spring 中的 Bean 对 Spring 是无感知的,即它感知不到自己是由哪个 Bean 工厂(BeanFactory)创建的;感知不到自己在工厂中的 id,即 beanName;也感知不到 Spring 应用上下文对象(ApplicationContext)等等。
如果要想 Spring Bean 能感知到这些信息,我们可以自己通过某些手段来获取这些信息,然后设置到 bean 实例中。这种方法缺点是需要我们自己获取需要感知的信息,然后主动设置到 bean 中,会写许多冗余代码。但是优点是可以和 Spring 解耦。
当然,Spring 框架提供了这种扩展能力,能让一个 bean 成为有感知的。只要让 bean 类实现特定的接口,重写其中的方法即可。这样 Spring 会在 bean 的生命周期的某个阶段调用这些重写的方法,注入需要感知的信息。这方式的缺点是需要和 Spring 耦合,优点是能减少代码量,简单。
Aware 接口
要让 Spring 中的 bean 获得某些感知能力,需要实现特定的接口,这些接口有个共同的父接口,即 Aware 接口。
package org.springframework.beans.factory; public interface Aware { }
Aware 是一个标记接口,表明一个 bean 可以通过回调方法得到 Spring 容器中特定的对象。具体的方法签名由各个子接口定义,但通常应该只包含一个接受单个参数并返回 void 的方法。
注意,仅实现 Aware 接口不会提供默认功能。我们一般是实现 Aware 的子接口来获得特定的感知能力。
Aware 接口的子接口有很多,例如 BeanNameAware,BeanFactoryAware,ApplicationContextAware,EnvironmentAware,ApplicationEventPublisherAware 等等,以下介绍几个常用的用法。
BeanNameAware 接口
如果 bean 需要知道自己在 bean 工厂中的 beanName,即在 Spring 容器中的名字(标识)。可以实现此 BeanNameAware 接口。BeanNameAware 接口源码如下,只有一个方法,beanName 会通过这个方法设置到 bean 中。
package org.springframework.beans.factory; public interface BeanNameAware extends Aware {
void setBeanName(String name);
}
其实,我们使用的 bean 一般不需要知道它在 beanFactory 中的名字,意义不大。一般是官方在 Spring 自身框架使用比较多,官方也不推荐我们使用,因为这样会导致 bean 依赖 Spring API。
package com.chenpi; import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component; /**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/4/3
*/
@Component
public class Person implements BeanNameAware { private String beanName; @Override
public void setBeanName(String name) {
this.beanName = name;
} public String getBeanName() {
return beanName;
}
}
编写测试单元,验证如下:
package com.chenpi; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest
class ApplicationTests { @Autowired
private Person person; @Test
public void testValue() {
System.out.println("Person BeanName:" + person.getBeanName());
} } // 输出结果如下
Person BeanName:person
BeanFactoryAware 接口
如果 Bean 想感知配置自己的 BeanFactory 对象,可以实现 BeanFactoryAware 接口。如果需要,bean 可以通过 BeanFactory 对象获取其他 bean 对象,进行协作。当然也不推荐这种方式,推荐使用 DI 方式注入依赖的 bean 对象。BeanFactoryAware 接口源码如下:
package org.springframework.beans.factory; import org.springframework.beans.BeansException; public interface BeanFactoryAware extends Aware { // 在bean属性填充之后,但是在初始回调(例如afterPropertiesSet()方法)之前回调此方法
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
测试代码以及单元测试验证结果如下:
package com.chenpi; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component; /**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/4/3
*/
@Component
public class Person implements BeanNameAware, BeanFactoryAware { private String beanName;
private BeanFactory beanFactory; @Override
public void setBeanName(String name) {
this.beanName = name;
} @Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
} public String getBeanName() {
return beanName;
} public BeanFactory getBeanFactory() {
return beanFactory;
}
}
package com.chenpi; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest
class ApplicationTests { @Autowired
private Person person; @Test
public void testValue() {
System.out.println("Person BeanName:" + person.getBeanName());
System.out.println("Person Bean's BeanFactory:" + person.getBeanFactory().getClass());
System.out.println(
person == person.getBeanFactory().getBean(person.getBeanName(), Person.class));
} } // 输出结果如下
Person BeanName:person
Person Bean's BeanFactory:class org.springframework.beans.factory.support.DefaultListableBeanFactory
true
ApplicationContextAware 接口
如果 Spring Bean 需要感知 Spring 容器,即 ApplicationContext 对象,可以实现 ApplicationContextAware 接口。可以通过 Spring 容器获取其他 Bean 对象,进行协作。
当然,如果一个 bean 对象需要访问文件资源,例如调用applicationContext.getResource()方法,或想要发布一个应用程序事件applicationContext.publishEvent(ApplicationEvent event),或需要访问MessageSource,此种方式可以实现。不过,官方对于这些的特定场景,最好实现更具体的ResourceLoaderAware、ApplicationEventPublisherAware或MessageSourceAware接口更合适。
package org.springframework.context; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware; public interface ApplicationContextAware extends Aware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
测试代码以及单元测试验证结果如下:
package com.chenpi; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; /**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/4/3
*/
@Component
public class Person implements BeanNameAware, BeanFactoryAware, ApplicationContextAware { private String beanName;
private BeanFactory beanFactory;
private ApplicationContext applicationContext; @Override
public void setBeanName(String name) {
this.beanName = name;
} @Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
} @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
} public String getBeanName() {
return beanName;
} public BeanFactory getBeanFactory() {
return beanFactory;
} public ApplicationContext getApplicationContext() {
return applicationContext;
}
}
package com.chenpi; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext; @SpringBootTest
class ApplicationTests { @Autowired
private Person person; @Test
public void testValue() {
System.out.println("Person BeanName:" + person.getBeanName());
System.out.println("Person Bean's BeanFactory:" + person.getBeanFactory().getClass());
System.out.println(
person == person.getBeanFactory().getBean(person.getBeanName(), Person.class));
ApplicationContext applicationContext = person.getApplicationContext();
Person person1 = applicationContext.getBean(person.getBeanName(), Person.class);
System.out.println(applicationContext.getClass());
System.out.println(person == person1);
} } // 输出结果如下
Person BeanName:person
Person Bean's BeanFactory:class org.springframework.beans.factory.support.DefaultListableBeanFactory
true
class org.springframework.web.context.support.GenericWebApplicationContext
true
想减少代码量,快设置一个有感知的 Aware Spring Bean的更多相关文章
- 前端程序员的蜕变——JS的 event 对象属性、使用实例、兼容性处理(极大提高代码效率、减少代码量)
下面讨论一下 js 中的 Event 对象,主要从以下三个方面详细的描述(点击标题可跳转到对应部分): 1.什么是event 2.怎么用event,用他该注意什么,几个简单实际应用 3.event在不 ...
- WPF INotifyPropertyChanged 通过特性减少代码量
在很多地方需要用上INotifyPropertyChanged的接口,MVVM模式,List等集合都会用到. 通常我们使用 protected void OnChange(PropertyChange ...
- 新兵易学,老兵易用----C++(C++11的学习整理---如何减少代码量,加强代码的可读性)
1.auto类型推导 auto推导最大的优势就是在拥有初始化表达式的复杂类型变量声明时简化代码. auto第二个优势就是免去了程序员在一些类型声明时的麻烦,或者避免一些在类型声明时的错误. auto第 ...
- Java 8 中的方法引用,轻松减少代码量,提升可读性!
1. 引言 Java8中最受广大开发中喜欢的变化之一是因为引入了 lambda 表达式,因为这些表达式允许我们放弃匿名类,从而大大减少了样板代码,并提高了可读性. 方法引用是lambda表达式的一种特 ...
- 一些减少代码量、提高开发效率的利器(Java)
Spring Boot mybatis-plus代码生成器和自带CRUD接口 lombok 库: Apache Commons & guava AOP Java8: stream & ...
- springboot 使用 @data 插件,减少代码量
一.idea 安装 lombok 插件 二.重启 idea 三.添加依赖 <dependency> <groupId>org.projectlombok</groupId ...
- 让你的代码量减少3倍!使用kotlin开发Android(一)
让你的代码量减少3倍!使用kotlin开发Android(一) 创建Kotlin工程 本文同步自博主的私人博客:wing的地方酒馆 写在前面 使用kotlin开发android已经两周多了.得到的好处 ...
- 让你的代码量减少3倍!使用kotlin开发Android(二) --秘笈!扩展函数
本文承接上一篇文章:让你的代码量减少3倍!使用kotlin开发Android(一) 创建Kotlin工程 本文同步自博主的私人博客wing的地方酒馆 上一节说到,kotlin可以省去getter,se ...
- 单位分配的IP地址和电脑主机绑定了,我想用设置一个无线路由器,让我的笔记本电脑和手机都能上网?
单位分配的IP地址和电脑主机绑定了,我想用设置一个无线路由器,让我的笔记本电脑和手机都能上网? 配一个无线路由器就可以实现,将电脑IP配置成自动获取,找条网线一头插路由LAN口(路由器上有标明 ...
随机推荐
- C#序列化和反序列化(json)
一,什么是Json? json是存储和交换文本信息的方法,类似xml.但是json比xml更小,更快,j更易于解析.并且json采用完全独立于语言的文本格式(即不依赖于各种编程语言),这些特性使jso ...
- 获取ajax动态加载的多个a标签中的 点击的那个a标签对应的值
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- ms17-010-永恒之蓝漏洞利用教程
实验环境:虚拟机:kali-linux windows 7 请自行下载安装 1.打开虚拟机 启动kali-linux 启动windows7(未装补丁) 2.获取IP地址(ifconfig ipconf ...
- SQLMap参数命令
SQLMap参数命令 --method=<http方法> 指定使用的http方法 --data=<post数据> 提交post数据并对post数据进行测试 --param- ...
- spring学习一:spring入门及相关概念介绍
1:Spring的概念:(03年兴起) (1) 开源的轻量级的框架(无需复杂的环境,不依赖其他) (2) 一站式框架(Spring在javaee的三层结构中,对每一层都提供不同的解决技术: ...
- 一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 MySQL 数据库,又插入了一条数据,此时 id 是几?如何获取当前数据库版本?
一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 MySQL 数据库,又插入了一条数据,此时 id 是几? 一般情况下,我们创建的表的类型是InnoDB,如果新增一条记录(不重启mysq ...
- @Required 注解 ?
这个注解表明 bean 的属性必须在配置的时候设置,通过一个 bean 定义的显式的 属性值或通过自动装配,若@Required 注解的 bean 属性未被设置,容器将抛出 BeanInitializ ...
- Zookeeper 的典型应用场景?
Zookeeper 是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员 可以使用它来进行分布式数据的发布和订阅. 通过对 Zookeeper 中丰富的数据节点进行交叉使用,配合 Watch ...
- solr服务的搭建
首先你需要一台已经搭建好的虚拟机,下面的步骤才可以执行 安装java 安装完Centos6.5的Base Server版会默认安装OpenJDK,首先需要删除OpenJDK 1.查看以前是不是安装了o ...
- js--事件循环机制
前言 我们知道JavaScript 是单线程的编程语言,只能同一时间内做一件事,按顺序来处理事件,但是在遇到异步事件的时候,js线程并没有阻塞,还会继续执行,这又是为什么呢?本文来总结一下js 的事件 ...