mini-spring|关于Bean对象作用域以及FactoryBean的实现和使用

需求

FactoryBean 直接配置FactoryBean 获取FactoryBean中的Bean对象
FactoryBean的getObject方法通过反射获取Bean对象 由此省去对实体Dao类的定义

解决方法

对外提供一个可以二次从 FactoryBean 的 getObject 方法中获取对象的功能即可

整体架构

在这里插入图片描述
整个的实现过程包括了两部分,一个解决单例还是原型对象,另外一个处理 FactoryBean 类型对象创建过程中关于获取具体调用对象的 getObject 操作。
COPE_SINGLETON、SCOPE_PROTOTYPE,对象类型的创建获取方式,主要区分在于 AbstractAutowireCapableBeanFactory#createBean 创建完成对象后是否放入到内存中,如果不放入则每次获取都会重新创建。
createBean 执行对象创建、属性填充、依赖加载、前置后置处理、初始化等操作后,就要开始做执行判断整个对象是否是一个 FactoryBean 对象,如果是这样的对象,就需要再继续执行获取 FactoryBean 具体对象中的 getObject 对象了。整个 getBean 过程中都会新增一个单例类型的判断factory.isSingleton(),用于决定是否使用内存存放对象信息。

实现

工程结构和类图

src
├── main
│ └── java
│ └── cn.bugstack.springframework
│ ├── beans
│ │ ├── factory
│ │ │ ├── factory
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java(实体类)
初始化和销毁
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java(接口) 定义了 destroySingletons 销毁方法
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java(抽象类)
主要作用:
继承关系:继承AbstractBeanFactory
实现AutowireCapableBeanFactory接口
主要方法:
CreateBean():创建Bean 调用registerDisposableBeanIfNecessary
initializeBean():初始化Bean,调用PostProcessor Before 处理,执行初始化方法invokeInitMethods,执行 BeanPostProcessor After 处理
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
实现destroySingletons 销毁方法( AbstractBeanFactory.java的父类)
│ │ │ │ ├── DisposableBeanAdapter.java
描述:销毁方法适配器
继承关系: 实现DisposableBean接口
│ │ │ │ ├── FactoryBeanRegistrySupport.java(继承 DefaultSingletonBeanRegistry)
作用:实现一个 FactoryBean 注册服务
维护一个存放FactoryBean对象的缓存 factoryBeanObjectCache
处理的就是关于 FactoryBean 此类对象的注册操作
GetObjectFromFactoryBean() 从FactoryBean通过getObject()获取对象,先判断缓存中是否有 如果有直接获取,没有则加入缓存
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ ├── support
│ │ │ │ └── XmlBeanDefinitionReader.java
│ │ │ ├── Aware.java(接口)
│ │ │ ├── BeanClassLoaderAware.java(实现Aware接口)
│ │ │ ├── BeanFactory.java
│ │ │ ├──BeanFactoryAware.java(实现Aware接口)
│ │ │ ├── BeanNameAware.java
│ │ │ ├── ConfigurableListableBeanFactory.java
│ │ │ ├── DisposableBean.java
│ │ │ ├──FactoryBean.java(实现FactoryBean)
主要方法:
getObject()获取对象
getObjectType()对象类型
isSingleton()是否是单例对象 如果是单例对象会被放到内存中
│ │ │ ├── HierarchicalBeanFactory.java
│ │ │ ├── InitializingBean.java(接口) 定义初始化方法
│ │ │ └── ListableBeanFactory.java
│ │ ├── BeansException.java
│ │ ├── PropertyValue.java
│ │ └── PropertyValues.java
│ ├── context
│ │ ├── support
│ │ │ ├── AbstractApplicationContext.java(抽象类)
继承关系:实现 ConfigurableApplicationContext接口 继承DefaultResourceLoader类
│ │ │ ├── AbstractRefreshableApplicationContext.java
│ │ │ ├── AbstractXmlApplicationContext.java
│ │ │ ├── ApplicationContextAwareProcessor.java(实现BeanPostProcessor接口)
│ │ │ └── ClassPathXmlApplicationContext.java
│ │ ├── ApplicationContext.java
│ │ ├──ApplicationContextAware.java
│ │ └── ConfigurableApplicationContext.java(接口)
主要描述:虚拟机关闭钩子注册调用销毁,定义刷新容器,关闭应用上下文
继承关系:继承ApplicationContext
主要方法:
refresh():
registerShutdownHook():注册虚拟机钩子的方法
close():手动执行关闭虚拟机钩子的方法
│ ├── core.io
│ │ ├── ClassPathResource.java
│ │ ├── DefaultResourceLoader.java(实体类)
作用:资源处理器
│ │ ├── FileSystemResource.java
│ │ ├── Resource.java
│ │ ├── ResourceLoader.java
│ │ └── UrlResource.java
│ └── utils
│ └── ClassUtils.java
└── test
类图:
在这里插入图片描述
以上整个类关系图展示的就是添加 Bean 的实例化是单例还是原型模式以及 FactoryBean 的实现。
其实整个实现的过程并不复杂,只是在现有的 AbstractAutowireCapableBeanFactory 类以及继承的抽象类 AbstractBeanFactory 中进行扩展。
不过这次我们把 AbstractBeanFactory 继承的 DefaultSingletonBeanRegistry 类,中间加了一层 FactoryBeanRegistrySupport,这个类在 Spring 框架中主要是处理关于 FactoryBean 注册的支撑操作。

实现

Bean的作用范围定义和xml解析

BeanDefinition

public class BeanDefinition {String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;private Class beanClass;private PropertyValues propertyValues;private String initMethodName;private String destroyMethodName;private String scope = SCOPE_SINGLETON;private boolean singleton = true;private boolean prototype = false;// ...get/set
}

在 BeanDefinition 类中新增加的两个属性信息,用于把从 spring.xml 中解析到的 Bean 对象作用范围填充到属性中。
XmlBeanDefinitionReader

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {for (int i = 0; i < childNodes.getLength(); i++) {// 判断元素if (!(childNodes.item(i) instanceof Element)) continue;// 判断对象if (!"bean".equals(childNodes.item(i).getNodeName())) continue;// 解析标签Element bean = (Element) childNodes.item(i);String id = bean.getAttribute("id");String name = bean.getAttribute("name");String className = bean.getAttribute("class");String initMethod = bean.getAttribute("init-method");String destroyMethodName = bean.getAttribute("destroy-method");String beanScope = bean.getAttribute("scope");// 获取 Class,方便获取类中的名称Class<?> clazz = Class.forName(className);// 优先级 id > nameString beanName = StrUtil.isNotEmpty(id) ? id : name;if (StrUtil.isEmpty(beanName)) {beanName = StrUtil.lowerFirst(clazz.getSimpleName());}// 定义BeanBeanDefinition beanDefinition = new BeanDefinition(clazz);beanDefinition.setInitMethodName(initMethod);beanDefinition.setDestroyMethodName(destroyMethodName);if (StrUtil.isNotEmpty(beanScope)) {beanDefinition.setScope(beanScope);}// ...// 注册 BeanDefinitiongetRegistry().registerBeanDefinition(beanName, beanDefinition);}}}

在解析 XML 处理类 XmlBeanDefinitionReader 中,新增加了关于 Bean 对象配置中 scope 的解析,并把这个属性信息填充到 Bean 定义中。beanDefinition.setScope(beanScope)

创建和修改对象时候判断单例和原型模式

AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();@Overrideprotected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {Object bean = null;try {bean = createBeanInstance(beanDefinition, beanName, args);// 给 Bean 填充属性applyPropertyValues(beanName, bean, beanDefinition);// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法bean = initializeBean(beanName, bean, beanDefinition);} catch (Exception e) {throw new BeansException("Instantiation of bean failed", e);}// 注册实现了 DisposableBean 接口的 Bean 对象registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);// 判断 SCOPE_SINGLETON、SCOPE_PROTOTYPEif (beanDefinition.isSingleton()) {addSingleton(beanName, bean);}return bean;}protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {// 非 Singleton 类型的 Bean 不执行销毁方法if (!beanDefinition.isSingleton()) return;if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));}}// ... 其他功能
}

单例模式和原型模式的区别就在于是否存放到内存中,如果是原型模式那么就不会存放到内存中,每次获取都重新创建对象,另外非 Singleton 类型的 Bean 不需要执行销毁方法。
所以这里的代码会有两处修改,一处是 createBean 中判断是否添加到 addSingleton(beanName, bean);,另外一处是 registerDisposableBeanIfNecessary 销毁注册中的判断 if (!beanDefinition.isSingleton()) return;。

定义 FactoryBean 接口

public interface FactoryBean<T> {T getObject() throws Exception;Class<?> getObjectType();boolean isSingleton();}

FactoryBean 中需要提供3个方法,获取对象、对象类型,以及是否是单例对象,如果是单例对象依然会被放到内存中。

实现一个 FactoryBean 注册服务

FactoryBeanRegistrySupport

public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {/*** Cache of singleton objects created by FactoryBeans: FactoryBean name --> object*/private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>();protected Object getCachedObjectForFactoryBean(String beanName) {Object object = this.factoryBeanObjectCache.get(beanName);return (object != NULL_OBJECT ? object : null);}protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) {if (factory.isSingleton()) {Object object = this.factoryBeanObjectCache.get(beanName);if (object == null) {object = doGetObjectFromFactoryBean(factory, beanName);this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));}return (object != NULL_OBJECT ? object : null);} else {return doGetObjectFromFactoryBean(factory, beanName);}}private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName){try {return factory.getObject();} catch (Exception e) {throw new BeansException("FactoryBean threw exception on object[" + beanName + "] creation", e);}}}

扩展 AbstractBeanFactory 创建对象逻辑

AbstractBeanFactory

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {protected <T> T doGetBean(final String name, final Object[] args) {Object sharedInstance = getSingleton(name);if (sharedInstance != null) {// 如果是 FactoryBean,则需要调用 FactoryBean#getObjectreturn (T) getObjectForBeanInstance(sharedInstance, name);}BeanDefinition beanDefinition = getBeanDefinition(name);Object bean = createBean(name, beanDefinition, args);return (T) getObjectForBeanInstance(bean, name);}  private Object getObjectForBeanInstance(Object beanInstance, String beanName) {if (!(beanInstance instanceof FactoryBean)) {return beanInstance;}Object object = getCachedObjectForFactoryBean(beanName);if (object == null) {FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;object = getObjectFromFactoryBean(factoryBean, beanName);}return object;}// ...
}

此处新增加的功能主要是在 doGetBean 方法中,添加了调用 (T) getObjectForBeanInstance(sharedInstance, name) 对获取 FactoryBean 的操作。
在 getObjectForBeanInstance 方法中做具体的 instanceof 判断,另外还会从 FactoryBean 的缓存中获取对象,如果不存在则调用 FactoryBeanRegistrySupport#getObjectFromFactoryBean,执行具体的操作。

测试

IUserDao

public interface IUserDao {String queryUserName(String uId);}

定义一个 IUserDao 接口,之所这样做是为了通过 FactoryBean 做一个自定义对象的代理操作。
UserService

public class UserService {private String uId;private String company;private String location;private IUserDao userDao;public String queryUserInfo() {return userDao.queryUserName(uId) + "," + company + "," + location;}// ...get/set
}

定义 FactoryBean 对象

public class ProxyBeanFactory implements FactoryBean<IUserDao> {@Overridepublic IUserDao getObject() throws Exception {InvocationHandler handler = (proxy, method, args) -> {Map<String, String> hashMap = new HashMap<>();hashMap.put("10001", "小傅哥");hashMap.put("10002", "八杯水");hashMap.put("10003", "阿毛");return "你被代理了 " + method.getName() + ":" + hashMap.get(args[0].toString());};//Proxy.newProxyInstance反射获取对象return (IUserDao) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IUserDao.class}, handler);}@Overridepublic Class<?> getObjectType() {return IUserDao.class;}@Overridepublic boolean isSingleton() {return true;}}

这是一个实现接口 FactoryBean 的代理类 ProxyBeanFactory 名称,主要是模拟了 UserDao 的原有功能,类似于 MyBatis 框架中的代理操作。
getObject() 中提供的就是一个 InvocationHandler 的代理对象,当有方法调用的时候,则执行代理对象的功能。
配置文件

<beans><bean id="userService" class="cn.bugstack.springframework.test.bean.UserService" scope="prototype"><property name="uId" value="10001"/><property name="company" value="腾讯"/><property name="location" value="深圳"/><property name="userDao" ref="proxyUserDao"/></bean><bean id="proxyUserDao" class="cn.bugstack.springframework.test.bean.ProxyBeanFactory"/></beans>

单元测试
单例&&原型
原型每次都不一样

@Test
public void test_prototype() {// 1.初始化 BeanFactoryClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");applicationContext.registerShutdownHook();   // 2. 获取Bean对象调用方法UserService userService01 = applicationContext.getBean("userService", UserService.class);UserService userService02 = applicationContext.getBean("userService", UserService.class);// 3. 配置 scope="prototype/singleton"System.out.println(userService01);System.out.println(userService02);    // 4. 打印十六进制哈希System.out.println(userService01 + " 十六进制哈希:" + Integer.toHexString(userService01.hashCode()));System.out.println(ClassLayout.parseInstance(userService01).toPrintable());}

继承一个UserDao类型的BeanFactory工厂如代理类 ProxyBeanFactory 可以完美替换掉了 UserDao 的功能

问题与思考

1.spring的作用域什么是单例,什么是原型(在搜索并思考)

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

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

相关文章

Python编程实验五:文件的读写操作

目录 一、实验目的与要求 二、实验内容 三、主要程序清单和程序运行结果 第1题 第2题 四、实验结果分析与体会 一、实验目的与要求 &#xff08;1&#xff09;通过本次实验&#xff0c;学生应掌握与文件打开、关闭相关的函数&#xff0c;以及与读写操作相关的常用方法的…

第五课:BIO高级操作

一、BIO模式下的端口转发思想 需求&#xff1a;需要实现是个客户端的消息可以发送给所有的客户端接收.(群聊实现) 我们每一个客户端都会和服务器端建立一个socket通道&#xff0c;这样服务器端和客户端就能够实现通信。为了让服务器端知道我们当前有多少个socket正在运行&…

国际黄金价格是什么?和黄金价格有何区别?

黄金是世界上最珍贵的贵金属之一&#xff0c;其价值被无数人所垂涎。而国际黄金价格作为市场上的参考指标&#xff0c;直接影响着黄金交易的买卖。那么国际黄金价格到底是什么&#xff0c;与黄金价格又有何区别呢&#xff1f;本文将为您详细解答。 国际黄金价格是指以美元计量的…

如何恢复数据?5个实用数据恢复方法!

亲爱的朋友们&#xff0c;你是否也有过这样的经历&#xff0c;电脑里的数据突然消失&#xff0c;让你手足无措&#xff1f;别担心&#xff0c;今天我就来教你如何恢复数据&#xff0c;让你不再为数据丢失而烦恼。 首先&#xff0c;我们需要了解数据丢失的原因。可能是你不小心…

ZDH-大数据采集-支持KETTLE任务

目录 目录 项目源码 预览地址 支持KETTLE介绍 新增KETTLE任务 配置调度KETTLE 重要说明 感谢支持 项目源码 预览地址 支持KETTLE介绍 新增KETTLE任务 配置调度KETTLE 重要说明 项目源码 zdh_web:GitHub - zhaoyachao/zdh_web: 大数据采集,抽取平台 预览地址 后…

【MySQL】基于Docker搭建MySQL一主二从集群

本文记录了搭建mysql一主二从集群&#xff0c;这样的一个集群master为可读写&#xff0c;slave为只读。过程中使用了docker&#xff0c;便于快速搭建单体mysql。 1&#xff0c;准备docker docker的安装可以参考之前基于yum安装docker的文章[1]。 容器相关命令[2]。 查看正在…

快速搭建网站原型!8款网站原型软件推荐

现在&#xff0c;基于云的软件已经逐渐成为主流&#xff0c;网站原型设计工具也不例外。与桌面版本相比&#xff0c;在线原型工具具有独特的优势&#xff0c;无论您使用Linux&#xff0c;Mac 或者Windows&#xff0c;都不需要安装就可以使用这些工具。下面小编就为大家推荐8款非…

nginx(三)实现反向代理客户端 IP透传

正常情况下&#xff0c;客户端去访问代理服务器&#xff0c;然后代理服务器再取访问真实服务器&#xff0c;在真实服务器上&#xff0c;只能显示代理服务器的ip地址&#xff0c;而不显示客户端的ip地址&#xff0c;如果想让客户端的ip地址也能在真实服务端看见&#xff0c;这一…

Python怎么操作Mysql数据库

目录 一、准备工作 二、连接数据库 三、执行SQL语句 四、处理查询结果 五、插入、更新和删除数据 插入数据&#xff1a; 更新数据&#xff1a; 删除数据&#xff1a; 六、错误处理 七、总结 随着数据驱动决策的理念逐渐深入人心&#xff0c;数据库操作已成为现代编程…

Java设计模式:组合模式

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

使用pyannote-audio实现声纹分割聚类

使用pyannote-audio实现声纹分割聚类 # GitHub地址 https://github.com/MasonYyp/audio1 简单介绍 pyannote.audio是用Python编写的用于声纹分割聚类的开源工具包。在PyTorch机器学习基础上&#xff0c;不仅可以借助性能优越的预训练模型和管道实现声纹分割聚类&#xff0c;还…

【React 报错】—Remove untracked files, stash or commit any changes, and try again.

【React 报错】—Remove untracked files, stash or commit any changes, and try again. 在react项目中通过.less文件进行样式定义&#xff0c;先暴露webpack配置文件&#xff0c;执行命令&#xff1a;yarn eject 或 npm run eject&#xff0c;报错如下&#xff1a; 原因是因…