手写Spring:第10章-对象作用域和FactoryBean

news2024/11/25 0:56:27

文章目录

  • 一、目标:对象作用域和FactoryBean
  • 二、设计:对象作用域和FactoryBean
  • 三、实现:对象作用域和FactoryBean
    • 3.0 引入依赖
    • 3.1 工程结构
    • 3.2 Spring单例、原型以及FactoryBean功能类图
    • 3.3 Bean的作用范围定义和xml解析
      • 3.3.1 Bean对象信息定义
      • 3.3.2 XML处理Bean注册
      • 3.3.3 创建和修改对象时判断单例和原型模式
    • 3.4 定义FactoryBean接口和注册服务
      • 3.4.1 定义FactoryBean接口
      • 3.4.2 通用的注册表实现
      • 3.4.3 实现 FactoryBean 注册服务
      • 3.4.4 扩展 AbstractBeanFactory创建对象逻辑
  • 四、测试:对象作用域和FactoryBean
    • 4.1 添加测试配置
      • 4.1.1 添加IUserDao接口
      • 4.1.2 修改UserService用户对象
      • 4.1.3 定义FactoryBean对象
      • 4.1.4 配置文件
    • 4.2 单元测试
      • 4.2.1 单例&原型测试
      • 4.2.2 代理对象测试
  • 五、总结:对象作用域和FactoryBean

一、目标:对象作用域和FactoryBean

💡 交给 Spring 管理的 Bean 对象,一定就是我们用类创建出来的 Bean 吗?创建出来的 Bean 就永远是单例吗,没有可能是原型模式吗?

  • Spring 框架中,使用 Mybatis 框架,它的核心作用是可以满足用户不需要实现 Dao 接口类,就可以通过 xml 或者注解配置的方式完成对数据库执行 CRUD 操作,那么在实现这样的 ORM 框架中,是怎么把一个数据操作的 Bean 对象交给 Spring 管理的。
  • 我们在使用 SpringMybatis 框架的时候知道,并没有手动的去创建任何操作数据库的 Bean 对象,有的仅仅是一个接口定义,而这个接口定义竟然可以被注入到其他需要使用 Dao 的属性中去。
    • 这一过程最核心待解决的问题:就是需要完成把复杂且以代理方式动态变化的对象,注册到 Spring 容器中。

二、设计:对象作用域和FactoryBean

💡 设计:提供一个能让使用者定义复杂的 Bean 对象。

  • 提供一个能让使用者定义复杂的 Bean 对象,这样的功能逻辑设计上并不复杂。因为 Spring 框架在开发的过程中就已经提供了各项扩展能力的 接口
    • 你只需要在合适的位置提供一个 接口 的处理接口调用和相应的功能逻辑实现即可。
    • 如:对外提供一个可以二次从 FactoryBeangetObject 方法中获取对象的功能即可。这样所有实现此接口的对象类,就可以扩充自己的对象功能了。
  • Mybatis 就是实现了一个 MapperFactoryBean 类,在 getObject 方法中提供 SqlSession 对执行 CRUD 方法的操作。

在这里插入图片描述

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

三、实现:对象作用域和FactoryBean

3.0 引入依赖

<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-cli -->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-cli</artifactId>
    <version>0.14</version>
</dependency>

3.1 工程结构

spring-step-09
|-src
	|-main
	|	|-java
	|		|-com.lino.springframework
	|			|-beans
	|			|	|-factory
	|			|	|	|-config
	|			|	|	|	|-AutowireCapableBeanFactory.java
	|			|	|	|	|-BeanDefinition.java
	|			|	|	|	|-BeanFactoryPostProcessor.java
	|			|	|	|	|-BeanPostProcessor.java
	|			|	|	|	|-BeanReference.java
	|			|	|	|	|-ConfigurableBeanFactory.java
	|			|	|	|	|-SingletonBeanRegistry.java
	|			|	|	|-support
	|			|	|	|	|-AbstractAutowireCapableBeanFactory.java
	|			|	|	|	|-AbstractBeabDefinitionReader.java
	|			|	|	|	|-AbstractBeabFactory.java
	|			|	|	|	|-BeabDefinitionReader.java
	|			|	|	|	|-BeanDefinitionRegistry.java
	|			|	|	|	|-CglibSubclassingInstantiationStrategy.java
	|			|	|	|	|-DefaultListableBeanFactory.java
	|			|	|	|	|-DefaultSingletonBeanRegistry.java
	|			|	|	|	|-DisposableBeanAdapter.java
	|			|	|	|	|-FactoryBeanRegistrySupport.java
	|			|	|	|	|-InstantiationStrategy.java
	|			|	|	|	|-SimpleInstantiationStrategy.java
	|			|	|	|-support
	|			|	|	|	|-XMLBeanDefinitionReader.java
	|			|	|	|-Aware.java
	|			|	|	|-BeanClassLoaderAware.java
	|			|	|	|-BeanFactory.java
	|			|	|	|-BeanFactoryAware.java
	|			|	|	|-BeanNameAware.java
	|			|	|	|-ConfigurableListableBeanFactory.java
	|			|	|	|-DisposableBean.java
	|			|	|	|-FactoryBean.java
	|			|	|	|-HierarcgicalBeanFactory.java
	|			|	|	|-InitializingBean.java
	|			|	|	|-ListableBeanFactory.java
	|			|	|-BeansException.java
	|			|	|-PropertyValue.java
	|			|	|-PropertyValues.java
	|			|-context
	|			|	|-support
	|			|	|	|-AbstractApplicationContext.java
	|			|	|	|-AbstractRefreshableApplicationContext.java
	|			|	|	|-AbstractXmlApplicationContext.java
	|			|	|	|-ApplicationContextAwareProcessor.java
	|			|	|	|-ClassPathXmlApplicationContext.java
	|			|	|-ApplicationContext.java
	|			|	|-ApplicationContextAware.java
	|			|	|-ConfigurableApplicationContext.java
	|			|-core.io
	|			|	|-ClassPathResource.java
	|			|	|-DefaultResourceLoader.java
	|			|	|-FileSystemResource.java
	|			|	|-Resource.java
	|			|	|-ResourceLoader.java
	|			|	|-UrlResource.java
	|			|-util
	|			|	|-ClassUtils.java
	|-test
		|-java
			|-com.lino.springframework.test
                |-bean
                |	|-IUserDao.java
                |	|-ProxyBeanFactory.java
                |	|-UserService.java
                |-ApiTest.java
		|-resources
			|-spring.xml

3.2 Spring单例、原型以及FactoryBean功能类图

在这里插入图片描述

  • 整个类图展示的就是添加 Bean 的实例化是单例还是原型模式以及 FactoryBean 的实现。
  • 实现:只是在现有的 AbstractAutowireCapableBeanFactory 类以及继承的抽象类 AbstractBeanFactory 中进行扩展。
  • 不过这次要把 AbstractBeanFactory 继承的 DefaultSingletonBeanRegistry 类,中间加一层 FactoryBeanRegistrySupport,这个类在 Spring 框架中主要是处理关于 FactoryBean 注册的支撑操作。

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

3.3.1 Bean对象信息定义

BeanDefinition.java

package com.lino.springframework.beans.factory.config;

import com.lino.springframework.beans.PropertyValues;

/**
 * @description: Bean 对象信息定义
 */
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;

    public BeanDefinition(Class beanClass) {
        this(beanClass, null);
    }

    public BeanDefinition(Class beanClass, PropertyValues propertyValues) {
        this.beanClass = beanClass;
        this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
    }

    public void setScope(String scope) {
        this.scope = scope;
        this.singleton = SCOPE_SINGLETON.equals(scope);
        this.prototype = SCOPE_PROTOTYPE.equals(scope);
    }

    public boolean isSingleton() {
        return singleton;
    }

    public boolean isPrototype() {
        return prototype;
    }

    public Class getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class beanClass) {
        this.beanClass = beanClass;
    }

    public PropertyValues getPropertyValues() {
        return propertyValues;
    }

    public void setPropertyValues(PropertyValues propertyValues) {
        this.propertyValues = propertyValues;
    }

    public String getInitMethodName() {
        return initMethodName;
    }

    public void setInitMethodName(String initMethodName) {
        this.initMethodName = initMethodName;
    }

    public String getDestroyMethodName() {
        return destroyMethodName;
    }

    public void setDestroyMethodName(String destroyMethodName) {
        this.destroyMethodName = destroyMethodName;
    }
}
  • singletonprototype,是本次在 BeanDefinition 类中新增加的两个属性信息。
    • 用于把从 spring.xml 中解析到的 Bean 对象作用范围填充到属性中。

3.3.2 XML处理Bean注册

XmlBeanDefinitionReader.java

package com.lino.springframework.beans.factory.xml;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanReference;
import com.lino.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import com.lino.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.lino.springframework.core.io.Resource;
import com.lino.springframework.core.io.ResourceLoader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.IOException;
import java.io.InputStream;

/**
 * @description: XML处理Bean注册
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    ...

    protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
        Document doc = XmlUtil.readXML(inputStream);
        Element root = doc.getDocumentElement();
        NodeList childNodes = root.getChildNodes();

        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);
            }

            // 读取属性并填充
            for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
                // 判断元素
                if (!(bean.getChildNodes().item(j) instanceof Element)) {
                    continue;
                }
                // 判断对象
                if (!"property".equals(bean.getChildNodes().item(j).getNodeName())) {
                    continue;
                }
                // 解析标签:property
                Element property = (Element) bean.getChildNodes().item(j);
                String attrName = property.getAttribute("name");
                String attrValue = property.getAttribute("value");
                String attrRef = property.getAttribute("ref");
                // 获取属性值:引入对象、值对象
                Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
                // 创建属性信息
                PropertyValue propertyValue = new PropertyValue(attrName, value);
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
            }
            if (getRegistry().containsBeanDefinition(beanName)) {
                throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
            }
            // 注册 BeanDefinition
            getRegistry().registerBeanDefinition(beanName, beanDefinition);
        }
    }
}
  • 在解析 XML 处理类 XmlBeanDefinitionReader 中,新增加了关于 Bean 对象配置中 scope 的解析,并把这个属性信息填充到 Bean 定义中。
    • beanDefinition.setScope(beanScope)

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

AbstractAutowireCapableBeanFactory.java

package com.lino.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.*;
import com.lino.springframework.beans.factory.config.AutowireCapableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.beans.factory.config.BeanReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @description: 实现默认bean创建的抽象bean工厂超类
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
        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()) {
            registerSingletonBean(beanName, bean);
        }
        return bean;
    }

    private 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 不需要执行销毁方法。

3.4 定义FactoryBean接口和注册服务

3.4.1 定义FactoryBean接口

FactoryBean.java

package com.lino.springframework.beans.factory;

/**
 * @description: 工厂对象接口
 */
public interface FactoryBean<T> {

    /**
     * 获取对象
     *
     * @return 泛型对象
     * @throws Exception 异常
     */
    T getObject() throws Exception;

    /**
     * 获取对象类型
     *
     * @return 对象类型
     */
    Class<?> getObjectType();

    /**
     * 是否单例对象
     *
     * @return 是否:布尔值
     */
    boolean isSingleton();
}
  • FactoryBean 中需要提供3个方法,获取对象、对象类型、以及是否是单例对象,如果是单例对象依然会被放入内存中。

3.4.2 通用的注册表实现

DefaultSingletonBeanRegistry.java

package com.lino.springframework.beans.factory.support;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.DisposableBean;
import com.lino.springframework.beans.factory.config.SingletonBeanRegistry;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @description: 通用的注册表实现
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    protected static final Object NULL_OBJECT = new Object();

    private Map<String, Object> singletonObjects = new HashMap<>();

    private final Map<String, DisposableBean> disposableBeans = new HashMap<>();

    ...
}
  • 添加空对象 Object NULL_OBJECT = new Object()

3.4.3 实现 FactoryBean 注册服务

FactoryBeanRegistrySupport.java

package com.lino.springframework.beans.factory.support;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.FactoryBean;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: FactoryBean注册对象
 */
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {

    /**
     * FactoryBean对象缓存
     */
    private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();

    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);
        }
    }
}
  • FactoryBeanRegistrySupport 类主要处理的就是关于 FactoryBean 此类对象的注册操作,之所以放到这样一个单独的类里,就是希望做到不同领域模块下只负责各自需要完成的功能,避免因为扩展导致类膨胀到难以维护。
  • 同时这里也定义了缓存操作 factoryBeanObjectCache,用于存放单例类型的对象,避免重复创建。
    • 日常使用中,基本都是创建的单例对象
  • doGetObjectFromFactoryBean 是具体的获取 FactoryBean#getObject() 方法。
    • 因为既有缓存的处理也有对象的获取,所以额外提供了 getObjectFromFactoryBean 进行逻辑包装。

3.4.4 扩展 AbstractBeanFactory创建对象逻辑

AbstractBeanFactory.java

package com.lino.springframework.beans.factory.support;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.FactoryBean;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.beans.factory.config.ConfigurableBeanFactory;
import com.lino.springframework.util.ClassUtils;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: 抽象的 Bean 工厂基类,定义模板方法
 */
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

    private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    ...

    protected <T> T doGetBean(final String name, final Object[] args) {
        Object sharedInstance = getSingleton(name);
        if (null != sharedInstance) {
            // 如果是 FactoryBean,则需要调用 FactoryBean#getObject
            return (T) getObjectFromBeanInstance(sharedInstance, name);
        }
        BeanDefinition beanDefinition = getBeanDefinition(name);
        Object bean = createBean(name, beanDefinition, args);
        return (T) getObjectFromBeanInstance(bean, name);
    }

    private Object getObjectFromBeanInstance(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;
    }

    ...
}
  • 首先这里把 AbstractBeanFactory 原来继承的 DefaultSingletonBeanRegistry,修改为继承 FactoryBeanRegistrySupport
    • 因为需要扩展出创建 FactoryBean 对象的能力,所以这就想一个链条服务上,截出一个段来处理额外的服务,再把链条再链接上。
  • 此处新增加的功能主要是在 doGetBean 方法中,添加了调用 (T) getObjectFromBeanInstance(sharedInstance, name) 对获取 FactoryBean 的操作。
  • getObjectFromBeanInstance 方法中做具体的 instanceof 判断,另外还会从 FactoryBean 的缓存中获取对象,如果不存在则调用 FactoryBeanRegistrySupport#getObjectFromFactoryBean,执行具体的操作。

四、测试:对象作用域和FactoryBean

4.1 添加测试配置

4.1.1 添加IUserDao接口

IUserDao.java

package com.lino.springframework.test.bean;

/**
 * @description: 用户dao接口
 */
public interface IUserDao {

    /**
     * 根据用户ID获取用户名称
     *
     * @param uId 用户ID
     * @return 用户名称
     */
    String queryUserName(String uId);
}
  • 定义一个 IUserDao 接口,是为了通过 FactoryBean 做一个自定义对象的代理操作。

4.1.2 修改UserService用户对象

UserService.java

package com.lino.springframework.test.bean;

/**
 * @description: 模拟用户 Bean 对象
 */
public class UserService {

    private String uId;
    private String company;
    private String location;
    private IUserDao userDao;

    /**
     * 查询用户信息
     */
    public String queryUserInfo() {
        return userDao.queryUserName(uId) + "," + company + "," + location;
    }

    public String getuId() {
        return uId;
    }

    public void setuId(String uId) {
        this.uId = uId;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public IUserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }
}
  • UserService 添加属性为 IUserDao,后面会给这个属性注入代理对象。

4.1.3 定义FactoryBean对象

ProxyBeanFactory.java

package com.lino.springframework.test.bean;

import com.lino.springframework.beans.factory.FactoryBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 代理对象工厂
 */
public class ProxyBeanFactory implements FactoryBean<IUserDao> {

    @Override
    public IUserDao getObject() throws Exception {
        InvocationHandler handler = (proxy, method, args) -> {
            // 添加排除方法
            if ("toString".equals(method.getName())) {
                return this.toString();
            }

            Map<String, String> hashMap = new HashMap<>();
            hashMap.put("10001", "张三");
            hashMap.put("10002", "李四");
            hashMap.put("10003", "王五");

            return "你被代理了 " + method.getName() + ": " + hashMap.get(args[0].toString());
        };
        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 的代理对象,当有方法调用的时候,则执行代理对象的功能。

4.1.4 配置文件

spring.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <bean id="userService" class="com.lino.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="com.lino.springframework.test.bean.ProxyBeanFactory"/>
</beans>
  • 在配置文件中,把 proxyUserDao 这个代理对象,注入到 userServiceuserDao 中。

4.2 单元测试

4.2.1 单例&原型测试

ApiTest.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());
}
  • spring.xml 配置文件中,设置了 scope="prototype",这样就每次获取到的对象都应该是一个新的对象。
  • 这里判断对象是否为一个单例,通过打印类对象的哈希值。

测试结果

com.lino.springframework.test.bean.UserService$$EnhancerByCGLIB$$5e50ce60@6bdf28bb
com.lino.springframework.test.bean.UserService$$EnhancerByCGLIB$$5e50ce60@6b71769e
com.lino.springframework.test.bean.UserService$$EnhancerByCGLIB$$5e50ce60@6bdf28bb 十六进制哈希: 6bdf28bb
com.lino.springframework.test.bean.UserService$$EnhancerByCGLIB$$5e50ce60 object internals:
 OFFSET  SIZE                                          TYPE DESCRIPTION                                               VALUE
      0     4                                               (object header)                                           01 bb 28 df (00000001 10111011 00101000 11011111) (-550978815)
      4     4                                               (object header)                                           6b 00 00 00 (01101011 00000000 00000000 00000000) (107)
      8     4                                               (object header)                                           3d e5 01 f8 (00111101 11100101 00000001 11111000) (-134093507)
     12     4                              java.lang.String UserService.uId                                           (object)
     16     4                              java.lang.String UserService.company                                       (object)
     20     4                              java.lang.String UserService.location                                      (object)
     24     4   com.lino.springframework.test.bean.IUserDao UserService.userDao                                       (object)
     28     1                                       boolean UserService$$EnhancerByCGLIB$$5e50ce60.CGLIB$BOUND        true
     29     3                                               (alignment/padding gap)
     32     4                       net.sf.cglib.proxy.NoOp UserService$$EnhancerByCGLIB$$5e50ce60.CGLIB$CALLBACK_0   (object)
     36     4                                               (loss due to the next object alignment)
Instance size: 40 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

4.2.2 代理对象测试

ApiTest.java

@Test
public void test_factory_bean() {
    // 1.初始化 BeanFactory
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
    applicationContext.registerShutdownHook();
    // 2.调用代理方法
    UserService userService = applicationContext.getBean("userService", UserService.class);
    System.out.println("测试结果:" + userService.queryUserInfo());
}

测试结果

测试结果:你被代理了 queryUserName: 张三,阿里,杭州
  • 从测试结果看,代理类 ProxyBeanFactory 完美替换掉了 UserDao 的功能。

五、总结:对象作用域和FactoryBean

  • Spring 框架整个开发的过程中,前期的各个功能接口类扩展的像膨胀了似的,但到后期在完善功能时,就没有那么难了,反而深入理解后会觉得功能的补充,都比较简单。只需要在所遇领域范围内进行扩展相应的服务实现即可。
  • 当你阅读完关于 FactoryBean 的实现以及测试过程的使用,以后再需要使用 FactoryBean 开发相应的组件时,一定会非常清楚它是如何创建自己的复杂 Bean 对象以及在什么时候初始化和调用的。遇到问题也可以快速的排查、定位和解决。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C语言】——三子棋游戏

目录 Ⅰ.初始页面&#xff1a; Ⅱ.游戏页面 Ⅲ.玩家下棋 Ⅳ.电脑下棋 Ⅴ.判断输赢 结果演示。 代码整体实现 1.test.c 2.game.c 3.game.h ① 前言 三子棋是一款非常经典的小游戏&#xff0c;三点一线即为成功&#xff0c;初学代码者&#xff0c;基本上都会接触这款小游戏…

在项目管理中,项目经理需要了解哪些内容?

在项目管理中&#xff0c;没有绝对的完美&#xff0c;每个项目和项目经理都有可以改进的空间。即使在项目交付时&#xff0c;也会面临各种问题和关系&#xff0c;使得项目经理难以充分应用最佳实践。 项目经理接触项目的第一步是沟通项目的目标&#xff0c;通过与利益相关者的…

论文研读|生成式跨模态隐写发展综述

前言&#xff1a;本文介绍近5年来生成式跨模态隐写领域的相关工作。 相关阅读&#xff1a;生成式文本隐写发展综述 不同于文本隐写&#xff0c;跨模态隐写需要考虑不同模态间的相关性&#xff0c;常见的跨模态场景有&#xff1a;Image-to-Text&#xff08;如图像描述&#xff…

【校招VIP】测试计划之loadrunner分析

考点介绍&#xff1a; LoadRunner&#xff0c;是一种预测系统行为和性能的负载测试工具。通过以模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题&#xff0c;LoadRunner能够对整个企业架构进行测试。企业使用LoadRunner能优化性能和加速应用系统的发布周期。校…

C语言 数据结构与算法 I

C语言-数据结构与算法 C语言基础 因为之前写算法都是用C&#xff0c;也有了些C基础&#xff0c;变量常量数据类型就跳过去吧。 首先是环境&#xff0c;学C时候用Clion&#xff0c;C语言也用它写吧~ 新建项目&#xff0c;选C执行文件&#xff0c;语言标准。。。就先默认C99吧…

你折腾一天都装不上的插件,函数计算部署 Stable Diffusion 都内置了

在进行函数计算 Stable Diffusion 答疑的过程中&#xff0c;遇到很多同学在装一些插件的过程中遇到了难题&#xff0c;有一些需要安装一些依赖&#xff0c;有一些需要写一些代码&#xff0c;很多时候安装一个插件就能折腾几天&#xff0c;我们收集了很多同学需要的插件&#xf…

OpenWrt通过终端查询版本

一、cat /etc/banner 二、cat /proc/version 三、cat /etc/openwrt_release 四、uname -a 五、opkg --version

从事网络安全行业 35岁会被淘汰吗?

根据我国信息部门统计显示&#xff1a;今后5年&#xff0c;我国从事网络建设、网络应用和网络服务的新型网络人才&#xff0c;尤其是网络工程师需求将达到60—100万人&#xff0c;而现有符合新型网络人才要求的人还不足20万人。不饱和度高达80%&#xff0c;人才需求量大。 这说…

1065 A+B and C (64bit)

题&#xff1a;点我 题目大意&#xff1a; 这题虽然看着像签到&#xff0c;然鹅签不过去。 因为我最初写的沙雕代码是&#xff1a; #include<iostream> #include<cstdio> using namespace std; int main(void) {int t;scanf("%d", &t);for (int i …

【C++从0到王者】第二十七站:搜索二叉树

文章目录 前言一、二叉搜索树的概念二、二叉搜索树的实现1.二叉树的结点定义2.二叉搜索树的结构3.二叉搜索树的构造函数4.二叉搜索树的插入&#xff08;非递归&#xff09;5.二叉搜索树的中序遍历&#xff08;排序&#xff09;6.二叉搜索树的查找&#xff08;非递归&#xff09…

Excel VSTO开发5 -Excel对象结构

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 5 Excel对象结构 Excel提供了几个比较重要的对象&#xff1a; Application、Workbooks、Workbook、Worksheets、Worksheet 为了便…

for循环命名

for循环命名 一、需求背景二、基本语法三、具体使用 一、需求背景 当我们使用双重循环查找某个目标&#xff0c;找到后想要在内循环跳出整个双重循环&#xff0c;我们可能想到用一个标志位&#xff0c;在外层循环判断到标志位为true时&#xff0c;跳出外层循环。 例如&#xf…

MySQL数据库和表的操作

数据库基础 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件保存数据有以下几个缺点&#xff1a; 1、文件的安全性问题 2、文件不利于数据查询和管理 3、文件不利于存储海量数据 4、文件在程序中控制不方便 数据库存储介质&#xff1a; 磁盘 内存 为了解决上…

【Spring面试】一、SpringBoot启动优化与最大连接数

文章目录 Q1、SpringBoot可以同时处理多少请求Q2、SpringBoot如何优化启动速度Q3、谈谈对Spring的理解Q4、Spring的优缺点 Q1、SpringBoot可以同时处理多少请求 调试&#xff1a; 写一个测试接口&#xff1a; RestController Slf4j public class RequestController{GetMapping…

找短视频音效、BGM,就上这5个网站

找视频剪辑音效、BGM就上这5个网站下载&#xff0c;免费、付费、商用的全都有&#xff0c;赶紧收藏起来~ 1、菜鸟图库 https://www.sucai999.com/audio.html?vNTYwNDUx 菜鸟图库是一个综合性素材网站&#xff0c;站内涵盖设计、图片、办公、视频、音效等素材。其中音效素材就…

javaee spring整合mybatis spring帮我们创建dao层

项目结构 pom依赖 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/P…

Excel VSTO开发4 -其他事件

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 4 其他事件 针对插件的事件主要有Startup、Shutdown这两个事件&#xff0c;在第2节中已经讲解。在开发窗口中&#xff0c;选择对象…

线程的6种状态

线程的6种状态 状态说明new初始状态&#xff1a;线程被创建&#xff0c;但还没有调用start()方法runnable运行状态&#xff1a;Java线程将操作系统中的就绪和运行两种状态笼统的称作“运行”blocked阻塞状态&#xff1a;表示线程阻塞于锁waiting等待状态&#xff1a;表示线程进…

Direct3D颜色

在Direct3D中颜色用RGB三元组来表示&#xff0c;RGB数据可用俩种不同的结构来保存&#xff0c;第一种是D3DCOLOR&#xff0c;它实际上与DWORD类型完全相同&#xff0c;共有32位&#xff0c;D3DCOLOR类型种的各位被分成四个8位项&#xff0c;每项存储了一种颜色分量的亮度值。 由…

U2Net使用方法和实现多类别语义分割模型改造

作者的碎碎念&#xff1a;U2Net是用来实现SOD的语义分割&#xff0c;本篇论文会介绍算法内容、主要代码、使用方法&#xff0c;以及如何将二分类语义分割修改为多类别语义模型。如果只想知道怎么训练自己的数据集&#xff0c;或者如何修改网络&#xff0c;可以通过目录进行跳转…