2026/4/16 23:35:32
网站建设
项目流程
济南单位网站建设,网站工作沟通及建设,大连seo加盟,优秀网站设计分析参考小傅哥的教程#xff1a;第10章#xff1a;对象作用域和FactoryBean | 小傅哥 bugstack 虫洞栈
本期目标是加入对象作用域与FactoryBean。
首先对这两部分进行一个初步理解#xff1a;
对象作用域#xff1a;分为singleton和prototype#xff0c;即单例模式和原型模…参考小傅哥的教程第10章对象作用域和FactoryBean | 小傅哥 bugstack 虫洞栈本期目标是加入对象作用域与FactoryBean。首先对这两部分进行一个初步理解对象作用域分为singleton和prototype即单例模式和原型模式。FactoryBean注意是FactoryBean不是BeanFactory。从字面上理解这是一个承担工厂作用的Bean。而工厂是做什么的返回一个我们需要的对象。因此FactoryBean就像是一层中介、一层包装将我们所需要的类的工厂交给Spring管理再通过工厂来获取所需要的类。大家可能会问为什么这么干这么干不是有病吗我也是这么想的直到我参考了一个博主的文章究竟FactoryBean是什么深入理解Spring的工厂神器-CSDN博客目前我们创建Bean的方式还是通过XML设置配置文件之后Spring读取相关配置文件从而创建Bean。但是如果我们所需要的Bean特别复杂XML配置文件就不好写。因此为了解决这个问题我们可以做一个FactoryBean在FactoryBean之中进行我们所需要的对象的复杂初始化操作。最后再通过FactoryBean中的get方法得到我们所需要的Bean对象。实现Bean的作用范围BeanDefinition中包含了对象的所有元数据因此我们需要在BeanDefiniton添加字段用来表征我们需要这个对象的单例模式还是原型模式。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; //在xml注册Bean定义时通过scope字段来判断是单例还是原型 public void setScope(String scope) { this.scope scope; this.singleton SCOPE_SINGLETON.equals(scope); this.prototype SCOPE_PROTOTYPE.equals(scope); } // ...get/set }这一块首先定义了两个常量String用于表示singleton和prototype。之后定义scope字段和boolean类型的singleton和prototype。注意scope需要在XML配置文件中设置有且仅有两个值singleton和prototype。之后使用XML方式读取配置信息时还需要补充上对scope字段的读取并设置BeanDefinition的相关字段值。这里就不做展示了具体可以参考文章开头给出的小傅哥的教程。创建和修改对象时判断单例单例模式和原型模式的区别就在于是否存放到内存中如果是原型模式那么就不会存放到内存中每次获取都重新创建对象另外非 Singleton 类型的 Bean 不需要执行销毁方法。public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { private InstantiationStrategy instantiationStrategy new CglibSubclassingInstantiationStrategy(); Override protected 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_PROTOTYPE if (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)); } } // ... 其他功能 }注意这一部分的代码修改了两处。1. 第一处是判断该对象是否为单例模式如果是单例则加入单例池。这里也很好理解加入单例池之后之后还要使用这个对象就直接从单例池中获取了。如果是原型模式则不加入单例池每次需要就重新创建。2. 第二处是原型模式下不执行销毁方法。至此就完成了Bean的作用范围功能。接下来需要完成FactoryBean功能。定义FactoryBean接口public interface FactoryBeanT { /** 获取对象 */ T getObject() throws Exception; /** 获取对象类型 */ Class? getObjectType(); /** 判断单例 */ boolean isSingleton(); }注意具体方法的实现交给用户用户觉得哪个类比较复杂需要使用FactoryBean包装就去实现这个接口实现这3个方法。实现FactoryBean注册服务/** * 支持 FactoryBean 的注册中心抽象基类。 * 在 DefaultSingletonBeanRegistry 的基础上额外缓存由 FactoryBean 创建的单例对象。 */ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry { /** * 缓存由 FactoryBean 创建的单例对象 * key FactoryBean 的 beanName * value FactoryBean.getObject() 返回的对象可能为 NULL_OBJECT 标记 * 注意这个是FactoryBean的缓存池 */ private final MapString, Object factoryBeanObjectCache new ConcurrentHashMapString, Object(); /** * 从缓存中快速获取指定 FactoryBean 生成的对象。 * param beanName FactoryBean 的 beanName * return 缓存中的对象若缓存的是 NULL_OBJECT 标记则返回 null */ protected Object getCachedObjectForFactoryBean(String beanName) { Object object this.factoryBeanObjectCache.get(beanName); return (object ! NULL_OBJECT ? object : null); } /** * 获取 FactoryBean 创建的对象考虑单例/原型作用域。 * 单例对象会被缓存后续调用直接取缓存原型每次新建。 * param factory FactoryBean 实例 * param beanName FactoryBean 的 beanName * return FactoryBean.getObject() 得到的对象 */ protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) { if (factory.isSingleton()) { // 单例先查缓存 Object object this.factoryBeanObjectCache.get(beanName); if (object null) { // 缓存未命中真正创建 object doGetObjectFromFactoryBean(factory, beanName); // 放入缓存若创建结果为 null用 NULL_OBJECT 标记占位ConcurrentHashMap 不允许 null this.factoryBeanObjectCache.put(beanName, (object ! null ? object : NULL_OBJECT)); } // 将 NULL_OBJECT 标记还原为 null 返回 return (object ! NULL_OBJECT ? object : null); } else { // 原型每次都新建不缓存 return doGetObjectFromFactoryBean(factory, beanName); } } /** * 真正调用 FactoryBean.getObject() 创建对象。 * 任何异常都被包装为 BeansException 向上抛出。 */ 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); } } }这一部分代码的核心是建立了一个缓存池factoryBeanObjectCache。注意由FactoryBean创建的单例对象实际上是交给FactoryBean管理的将单例对象存在了缓存池factoryBeanObjectCache中。这一块的思路就类似于查询缓存如果缓存中有我们所需要的对象就直接返回没有的话就创建一个对象再把这个对象加入缓存池当中。而由于这里使用的ConcurrentHashMap不允许使用null作为key因此我们人为定义一个NULL_OBJECT将其等同于null。拓展AbstractBeanFactory创建对象逻辑public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { // 继承自DefaultSingletonBeanRegistry类, 用于实现单例 // 更新后: 继承自FactoryBeanRegistrySupport, 兼容FactoryBean // 实现BeanFactory接口, 用于实现getBean方法 // 采用模板模式, 每一个类仅关注自己的实现 /** * 由于有两个重载的getBean方法因此将共同部分抽出来形成一个方法 * 具体逻辑也很简单尝试从单例Map中获取对象获取不到再考虑创建 * param name * param args * return * param T */ protected T T doGetBean(final String name, final Object[] args) { Object sharedInstance getSingleton(name); // 单例池中有这个对象 if (sharedInstance ! null) { // 根据这个对象是否是FactoryBean类型做进一步判断 return (T) getObjectForBeanInstance(sharedInstance, name); } // 单例池中没有这个对象, 就创建 BeanDefinition beanDefinition getBeanDefinition(name); // 如果对象非单例, createBean之后并不会加入单例池, 那么每一次去单例池中都查不到这个对象, 每一次都会新建 Object bean createBean(name, beanDefinition, args); return (T) getObjectForBeanInstance(bean, name); } private Object getObjectForBeanInstance(Object beanInstance, String beanName) { if (!(beanInstance instanceof FactoryBean)) { // 如果不是 FactoryBean, 直接返回该对象 return beanInstance; } // 如果是 FactoryBean则需要调用 FactoryBean#getObject // 该方法内部调用getObject Object object getCachedObjectForFactoryBean(beanName); if (object null) { FactoryBean? factoryBean (FactoryBean?) beanInstance; object getObjectFromFactoryBean(factoryBean, beanName); } return object; } // ... }分析这一块代码之前我们首先需要明确FactoryBean本质上就是一个Bean它会被Spring管理它会存在于单例池中(如果是单例模式)因此加入FactoryBean后我们getBean就需要判断一下先看看单例池中有没有。单例池中有执行getObjectForBeanInstance方法先判断单例池中放的是FactoryBean对象还是我们所需要的对象如果对象对应的类没有实现FactoryBean接口那么单例池中获取到的就是我们需要的Bean直接返回即可。如果对象对应的类实现了FactoryBean接口那么单例池中拿到的就是FactoryBean对象我再从FactoryBean的缓存池中当中拿我们真正所需要的对象。单例池中没有我们就先根据BeanDefiniton创建出Bean(这里创建Bean会在方法内部判断是否为单例)之后就是同样的操作看这个Bean是不是实现了FactoryBean接口然后根据结果不同进行不同的操作。总结FactoryBean 本身会被注册到BeanFactory 的一级单例池singletonObjects里由 DefaultSingletonBeanRegistry 正常管理FactoryBean 的 getObject() 创建出来的对象才只被factoryBeanObjectCache这张表缓存不会进入一级单例池。原型prototype作用域的 Bean 永远不会进入任何“池”