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

java 复制代码
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

java 复制代码
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 > name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }

            // 定义Bean
            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            beanDefinition.setInitMethodName(initMethod);
            beanDefinition.setDestroyMethodName(destroyMethodName);

            if (StrUtil.isNotEmpty(beanScope)) {
                beanDefinition.setScope(beanScope);
            }
            
            // ...
            
            // 注册 BeanDefinition
            getRegistry().registerBeanDefinition(beanName, beanDefinition);
        }
    }

}

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

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

AbstractAutowireCapableBeanFactory

java 复制代码
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));
        }
    }
    
    // ... 其他功能
}

单例模式和原型模式的区别就在于是否存放到内存中,如果是原型模式那么就不会存放到内存中,每次获取都重新创建对象,另外非 Singleton 类型的 Bean 不需要执行销毁方法。

所以这里的代码会有两处修改,一处是 createBean 中判断是否添加到 addSingleton(beanName, bean);,另外一处是 registerDisposableBeanIfNecessary 销毁注册中的判断 if (!beanDefinition.isSingleton()) return;。

定义 FactoryBean 接口

java 复制代码
public interface FactoryBean<T> {

    T getObject() throws Exception;

    Class<?> getObjectType();

    boolean isSingleton();

}

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

实现一个 FactoryBean 注册服务

FactoryBeanRegistrySupport

java 复制代码
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

java 复制代码
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#getObject
            return (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

java 复制代码
public interface IUserDao {

    String queryUserName(String uId);

}

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

UserService

java 复制代码
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 对象

java 复制代码
public class ProxyBeanFactory implements FactoryBean<IUserDao> {

    @Override
    public 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);
    }

    @Override
    public Class<?> getObjectType() {
        return IUserDao.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

}

这是一个实现接口 FactoryBean 的代理类 ProxyBeanFactory 名称,主要是模拟了 UserDao 的原有功能,类似于 MyBatis 框架中的代理操作。

getObject() 中提供的就是一个 InvocationHandler 的代理对象,当有方法调用的时候,则执行代理对象的功能。
配置文件

java 复制代码
<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>

单元测试

单例&&原型

原型每次都不一样

java 复制代码
@Test
public void test_prototype() {
    // 1.初始化 BeanFactory
    ClassPathXmlApplicationContext 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的作用域什么是单例,什么是原型(在搜索并思考)

相关推荐
qinzechen几秒前
分享几个做题网站------学习网------工具网;
java·c语言·c++·python·c#
hakesashou1 分钟前
python交互式命令时如何清除
java·前端·python
攒了一袋星辰2 分钟前
今日指数项目项目集成RabbitMQ与CaffienCatch
java·分布式·rabbitmq
wrx繁星点点9 分钟前
事务的四大特性(ACID)
java·开发语言·数据库
不写八个15 分钟前
Python办公自动化教程(005):Word添加段落
开发语言·python·word
IT学长编程16 分钟前
计算机毕业设计 Java酷听音乐系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·音乐系统·计算机毕业设计选题
_.Switch33 分钟前
Python机器学习框架介绍和入门案例:Scikit-learn、TensorFlow与Keras、PyTorch
python·机器学习·架构·tensorflow·keras·scikit-learn
IT学长编程33 分钟前
计算机毕业设计 基于协同过滤算法的个性化音乐推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·毕业论文·协同过滤算法·计算机毕业设计选题·个性化音乐推荐系统
小小娥子38 分钟前
Redis的基础认识与在ubuntu上的安装教程
java·数据库·redis·缓存
赵荏苒1 小时前
Python小白之Pandas1
开发语言·python