手写Spring:第7章-实现应用上下文

news2024/11/18 11:41:40

文章目录

  • 一、目标:实现应用上下文
  • 二、设计:实现应用上下文
  • 三、实现:实现应用上下文
    • 3.1 工程结构
    • 3.2 Spring应用上下文和Bean对象扩展类图
    • 3.3 对象工厂和对象扩展接口
      • 3.3.1 对象工厂扩展接口
      • 3.3.2 对象扩展接口
    • 3.4 定义应用上下文
      • 3.4.1 定义上下文接口
      • 3.4.2 配置应用上下文接口
      • 3.4.3 应用上下文抽象类实现
      • 3.4.4 获取Bean工厂和加载资源
      • 3.4.5 上下文中对配置信息的加载
      • 3.4.6 应用上下文实现类
    • 3.5 对象定义读取方法添加
      • 3.5.1 对象定义读取接口
      • 3.5.2 XML处理对象注册修改
    • 3.6 对象工厂整合应用上下文
      • 3.6.1 配置Bean工厂接口
      • 3.6.2 抽象的Bean工厂基类
      • 3.6.3 配置列表Bean工厂接口
      • 3.6.4 默认的Bean工厂实现类
      • 3.6.5 Bean工厂超类接口
      • 3.6.6 在Bean创建时完成前置和后置处理
  • 四、测试:实现应用上下文
    • 4.1 配置测试环境
      • 4.1.1 修改UserService用户对象
      • 4.1.2 实现BeanFactoryPostProcessor
      • 4.1.3 实现BeanPostProcessor
      • 4.1.4 基础配置,无BeanFactoryPostProcessor、BeanPostProcessor实现类
      • 4.1.5 增强配置,有BeanFactoryPostProcessor、BeanPostProcessor实现类
    • 4.2 单元测试
      • 4.2.1 不使用应用上下文测试
      • 4.2.2 使用应用上下文
  • 五、总结:实现应用上下文

一、目标:实现应用上下文

💡 如何在 Spring 接口的实现中获取 BeanFactory 以及 Bean 对象内容,并对这些内容做一些操作呢?

  • 在开发基于 Spring 的技术组件时,你一定会继承或者实现了 Spring 对外暴露的类或接口,在接口的实现中获取 BeanFactory 以及 Bean 对象等内容,并对这些内容做一些操作。
    • 例如:修改 Bean 的信息,添加日志打印、处理数据库路由对数据源的切换、给 RPC 服务连接注册中心等。
  • 在对容器中 Bean 的实例化过程添加扩展机制的同时,还需要把目前关于 spring.xml 初始化和加载策略进行优化。
    • 因为我们不太可能让面向 Spring 本身开发的 DefaultListableBeanFactory 服务,直接给予用户使用。

在这里插入图片描述

  • DefaultListableBeanFactoryXmlBeanDefinitionReader,是我们在目前 Spring 框架中对于服务功能测试的使用方式,它能很好的体现出 Spring 是如何对 xml 加载以及注册 Bean 对象的操作过程,但这种方式是面向 Spring 本身的,还不具备一定的扩展性。
  • 我们现在需要提供出一个可以在 Bean 初始化过程中,完成对 Bean 对象的扩展时,就很难做到自动化处理。
    • 所以我们要把 Bean 对象扩展机制功能和对 Spring 框架上下文的包装融合起来,对外提供完整的服务。

二、设计:实现应用上下文

💡 技术设计:实现应用上下文

  • 为了满足:在 Bean 对象从注册到实例化的过程中执行用户的自定义操作。
    • 需要在 Bean 的定义和初始化过程中插入接口类,这个接口再由外部去实现自己需要的服务。再结合对 Spring 框架上下文的处理能力。

在这里插入图片描述

  • 满足于对 Bean 对象扩展的两个接口,其实也是 Spring 框架中具有重量级的两个接口:BeanFactoryPostProcessorBeanPostProcessor,也几乎是在使用 Spring 框架额外新增开发自己组件需求的两个必备接口。
  • BeanFactoryPostProcessor:是由 Spring 框架组件提供的容器扩展机制,允许在 Bean 对象注册后但未实例化之前,对 Bean 的定义信息 BeanDefinition 执行修改操作。
  • BeanPostProcessor:也是 Spring 提供的扩展机制,不过 BeanPostProcessor 是在 Bean 对象实例化之后修改 Bean 对象,也可以替换 Bean 对象。这部分与后面的 AOP 密切相关。
  • 设计:开发 Spring 的上下文操作类,把相应的 XML 加载、注册、实例化以及新增的修改和扩展都融合进去,让 Spring 可以自动扫描到我们的新增服务,便于用户使用。

三、实现:实现应用上下文

3.1 工程结构

spring-step-06
|-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
	|			|	|	|	|-InstantiationStrategy.java
	|			|	|	|	|-SimpleInstantiationStrategy.java
	|			|	|	|-support
	|			|	|	|	|-XMLBeanDefinitionReader.java
	|			|	|	|-BeanFactory.java
	|			|	|	|-ConfigurableListableBeanFactory.java
	|			|	|	|-HierarcgicalBeanFactory.java
	|			|	|	|-ListableBeanFactory.java
	|			|	|-BeansException.java
	|			|	|-PropertyValue.java
	|			|	|-PropertyValues.java
	|			|-context
	|			|	|-support
	|			|	|	|-AbstractApplicationContext.java
	|			|	|	|-AbstractRefreshableApplicationContext.java
	|			|	|	|-AbstractXmlApplicationContext.java
	|			|	|	|-ClassPathXmlApplicationContext.java
	|			|	|-ApplicationContext.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
                |	|-UserDao.java
                |	|-UserService.java
                |-common
                |	|-MyBeanFactoryPostProcessor.java
                |	|-MyBeanPostProcessor.java
                |-ApiTest.java
		|-resources
			|-important.properties
			|-spring.xml
			|-springPostProcessor.xml

3.2 Spring应用上下文和Bean对象扩展类图

在这里插入图片描述

  • 在整个类图中主要体现出来的是关于 Spring 应用上下文以及对 Bean 对象扩展机制的实现。
  • 以继承了 ListableBeanFactory 接口的 ApplicationContext 接口开始,扩展出一系列应用上下文的抽象实现类,并最终完成 ClassPathXmlApplicationContext 类的实现。而这个类就是最后交给用户使用的类。
  • 同时在实现应用上下文的过程中,通过定义接口:BeanFactoryPostProcessorBeanPostProcessor 两个接口,把关于 Bean 的扩展机制串联进去。

3.3 对象工厂和对象扩展接口

3.3.1 对象工厂扩展接口

BeanFactoryPostProcessor.java

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

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;

/**
 * @description: 允许自定义修改 BeanDefinition 属性信息
 */
public interface BeanFactoryPostProcessor {

    /**
     * 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制
     *
     * @param beanFactory 对象工厂
     * @throws BeansException 对象异常
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
  • 这个接口是满足于在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制。

3.3.2 对象扩展接口

BeanPostProcessor.java

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

import com.lino.springframework.beans.BeansException;

/**
 * @description: 用于修改新实例化 Bean 对象的扩展点
 */
public interface BeanPostProcessor {

    /**
     * 在 Bean 对象执行初始化方法之前,执行此方法
     *
     * @param bean     对象
     * @param beanName 对象名称
     * @return 新对象
     * @throws BeansException 对象异常
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    /**
     * 在 Bean 对象执行初始化方法之后,执行此方法
     *
     * @param bean     对象
     * @param beanName 对象名称
     * @return 新对象
     * @throws BeansException 对象异常
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
  • 提供了修改新实例化 Bean 对象的扩展点。
  • 此接口提供了两个方法。
    • postProcessBeforeInitialization:用于在 Bean 对象执行初始化方法之前,执行此方法。
    • postProcessAfterInitialization:用于在 Bean 对象执行初始化方法之后,执行此方法。

3.4 定义应用上下文

3.4.1 定义上下文接口

ApplicationContext.java

package com.lino.springframework.context;

import com.lino.springframework.beans.factory.ListableBeanFactory;

/**
 * @description: 应用上下文接口
 */
public interface ApplicationContext extends ListableBeanFactory {
}
  • context 是本次实现应用上下文功能新增的服务包。
  • ApplicationContext,继承于 ListableBeanFactory,也就是继承了关于 BeanFactory 方法。比如一些 getBean 的方法。
    • 另外 ApplicationContext 本身是 Central 接口,后续会扩展。

3.4.2 配置应用上下文接口

ConfigurableApplicationContext.java

package com.lino.springframework.context;

import com.lino.springframework.beans.BeansException;

/**
 * @description: SPI 接口配置应用上下文
 */
public interface ConfigurableApplicationContext extends ApplicationContext {

    /**
     * 刷新容器
     *
     * @throws BeansException 对象异常
     */
    void refresh() throws BeansException;
}
  • ConfigurableApplicationContext 继承自 ApplicationContext,并提供了 refresh 这个核心方法。
  • 接下来也是需要在上下文的实现中完成刷新容器的操作过程。

3.4.3 应用上下文抽象类实现

AbstractApplicationContext.java

package com.lino.springframework.context.support;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.context.ConfigurableApplicationContext;
import com.lino.springframework.core.io.DefaultResourceLoader;
import java.util.Map;

/**
 * @description: 抽象应用上下文
 */
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    @Override
    public void refresh() throws BeansException {
        // 1.创建 BeanFactory,并加载 BeanDefinition
        refreshBeanFactory();

        // 2.获取 BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        // 3.在 Bean 实例化之前,执行 BeanFactoryPostProcess
        invokeBeanFactoryPostProcessor(beanFactory);

        // 4.BeanPostProcessor 需要提前与其他 Bean 对象实例化之前执行注册操作
        registerBeanPostProcessor(beanFactory);

        // 5.提前实例化单例 Bean 对象
        beanFactory.preInstantiateSingletons();
    }

    /**
     * 创建 BeanFactory,并加载 BeanDefinition
     *
     * @throws BeansException 异常
     */
    protected abstract void refreshBeanFactory() throws BeansException;

    /**
     * 获取对象工厂
     *
     * @return 配置列表 Bean工厂接口
     */
    protected abstract ConfigurableListableBeanFactory getBeanFactory();

    private void invokeBeanFactoryPostProcessor(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
        for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        }
    }

    private void registerBeanPostProcessor(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        }
    }

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        return getBeanFactory().getBeansOfType(type);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }

    @Override
    public Object getBean(String name) throws BeansException {
        return getBeanFactory().getBean(name);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return getBeanFactory().getBean(name, args);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(name, requiredType);
    }
}
  • AbstractApplicationContext 继承 DefaultResourceLoader 是为了处理 spring.xml 配置资源的加载。
  • 之后是在 refresh() 定义实现过程。
    1. 创建 BeanFactory,并加载 BeanDefinition
    2. 获取 BeanFactory
    3. Bean 实例化之前,执行 BeanFactoryPostProcessor
    4. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作。
    5. 提前实例化单例 Bean 对象。
  • 另外把定义出来的抽象方法:refreshBeanFactory()getBeanFactory() 由后面的继承此抽象类的其它抽象类实现。

3.4.4 获取Bean工厂和加载资源

AbstractRefreshableApplicationContext.java

package com.lino.springframework.context.support;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.lino.springframework.beans.factory.support.DefaultListableBeanFactory;

/**
 * @description: 抽象基类刷新应用上下文
 */
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {

    private DefaultListableBeanFactory beanFactory;

    @Override
    protected void refreshBeanFactory() throws BeansException {
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        loadBeanDefinitions(beanFactory);
        this.beanFactory = beanFactory;
    }

    private DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory();
    }

    /**
     * 加载Bean对象
     *
     * @param beanFactory 对象工厂
     */
    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory);

    @Override
    protected ConfigurableListableBeanFactory getBeanFactory() {
        return beanFactory;
    }
}
  • refreshBeanFactory() 中主要是获取 DefaultListableBeanFactory 的实例化以及对资源配置的加载操作 loadBeanDefinitions(beanFactory).
    • 在加载完成后即可对 spring.xml 配置文件中 Bean 对象的定义和注册。
    • 同时也包括实现了接口 BeanFactoryPostProcessorBeanPostProcessor 的配置 Bean 信息。
  • 但此时资源加载还是指只是定义了一个抽象方法:loadBeanDefinitions(DefaultListableBeanFactory beanFactory),继续由其他抽象类继承实现。

3.4.5 上下文中对配置信息的加载

AbstractXmlApplicationContext.java

package com.lino.springframework.context.support;

import com.lino.springframework.beans.factory.support.DefaultListableBeanFactory;
import com.lino.springframework.beans.factory.xml.XMLBeanDefinitionReader;

/**
 * @description: 抽象基类 XML 上下文
 */
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext {

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
        XMLBeanDefinitionReader beanDefinitionReader = new XMLBeanDefinitionReader(beanFactory, this);
        String[] configLocations = getConfigLocations();
        if (null != configLocations) {
            beanDefinitionReader.loadBeanDefinitions(configLocations);
        }
    }

    /**
     * 获取配置
     *
     * @return 字符串列表
     */
    protected abstract String[] getConfigLocations();
}
  • AbstractXmlApplicationContext 抽象类的 loadBeanDefinitions 方法实现中,使用 XMLBeanDefinitionReader 类,处理了关于 XML 文件配置信息的操作。
  • 同时这里又留下一个抽象类方法,getConfigLocations(),此方法是为了从入口上下文类,拿到配置信息的地址描述。

3.4.6 应用上下文实现类

ClassPathXmlApplicationContext.java

package com.lino.springframework.context.support;

import com.lino.springframework.beans.BeansException;

/**
 * @description: XML 文件应用上下文
 */
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {

    private String[] configLocations;

    public ClassPathXmlApplicationContext() {
    }

    /**
     * 从 XML 中加载 BeanDefinition,并刷新上下文
     *
     * @param configLocations 本地配置
     * @throws BeansException 异常
     */
    public ClassPathXmlApplicationContext(String configLocations) throws BeansException {
        this(new String[]{configLocations});
    }

    /**
     * 从 XML 中加载 BeanDefinition,并刷新上下文
     *
     * @param configLocations 本地配置
     * @throws BeansException 异常
     */
    public ClassPathXmlApplicationContext(String[] configLocations) throws BeansException {
        this.configLocations = configLocations;
        refresh();
    }

    @Override
    protected String[] getConfigLocations() {
        return configLocations;
    }
}
  • ClassPathXmlApplicationContext,是具体对外给用户提供的应用上下文方法。
  • 在继承了 AbstractXmlApplicationContext 以及层层抽象类的功能分离实现后,在此类 ClassPathXmlApplicationContext 的实现中就简单多,主要是对继承抽象类中方法的调用和提供配置文件地址信息。

3.5 对象定义读取方法添加

3.5.1 对象定义读取接口

BeanDefinitionReader.java

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

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.core.io.Resource;
import com.lino.springframework.core.io.ResourceLoader;

/**
 * @description: Bean定义读取接口
 */
public interface BeanDefinitionReader {

    /**
     * 获取bean对象注册对象
     *
     * @return bean对象注册对象
     */
    BeanDefinitionRegistry getRegistry();

    /**
     * 获取资源加载器
     *
     * @return 资源加载器
     */
    ResourceLoader getResourceLoader();

    /**
     * 加载bean定义方法
     *
     * @param resource 资源
     * @throws BeansException bean异常
     */
    void loadBeanDefinitions(Resource resource) throws BeansException;

    /**
     * 加载bean定义方法
     *
     * @param resources 资源列表
     * @throws BeansException bean异常
     */
    void loadBeanDefinitions(Resource... resources) throws BeansException;

    /**
     * 加载bean定义方法
     *
     * @param location 路径名称
     * @throws BeansException bean异常
     */
    void loadBeanDefinitions(String location) throws BeansException;

    /**
     * 加载bean定义方法
     *
     * @param locations 路径名称
     * @throws BeansException bean异常
     */
    void loadBeanDefinitions(String... locations) throws BeansException;
}
  • 定价加载 Bean 定义方法:loadBeanDefinitions(String... locations),支持多地址的参数构造。

3.5.2 XML处理对象注册修改

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 {

    public XMLBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XMLBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            try (InputStream inputStream = resource.getInputStream()) {
                doLoadBeanDefinitions(inputStream);
            }
        } catch (IOException | ClassNotFoundException e) {
            throw new BeansException("IOException parsing XML document from " + resource, e);
        }
    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    @Override
    public void loadBeanDefinitions(String... locations) throws BeansException {
        for (String location : locations) {
            loadBeanDefinitions(location);
        }
    }

    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");
            // 获取 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);
            // 读取属性并填充
            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);
        }
    }
}

3.6 对象工厂整合应用上下文

3.6.1 配置Bean工厂接口

ConfigurableBeanFactory.java

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

import com.lino.springframework.beans.factory.HierarchicalBeanFactory;

/**
 * @description: 配置Bean工厂接口
 */
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {

    String SCOPE_SINGLETON = "singleton";

    String SCOPE_PROTOTYPE = "prototype";

    /**
     * 添加修改新实例化 Bean 对象的扩展点
     *
     * @param beanPostProcessor 新实例化 Bean 对象
     */
    void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
}

3.6.2 抽象的Bean工厂基类

AbstractBeanFactory.java

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

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.BeanFactory;
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 java.util.ArrayList;
import java.util.List;

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

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

    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBean(name, args);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return (T) getBean(name);
    }

    protected <T> T doGetBean(final String name, final Object[] args) {
        Object bean = getSingleton(name);
        if (bean != null) {
            return (T) bean;
        }
        BeanDefinition beanDefinition = getBeanDefinition(name);
        return (T) createBean(name, beanDefinition, args);
    }

    /**
     * 获取 Bean 对象
     *
     * @param beanName 要检索的bean的名称
     * @return Bean 对象
     */
    protected abstract BeanDefinition getBeanDefinition(String beanName);

    /**
     * 创建Bean对象
     *
     * @param beanName       要检索的bean的名称
     * @param beanDefinition Bean对象
     * @param args           构造函数入参
     * @return 实例化的Bean对象
     */
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args);

    @Override
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        this.beanPostProcessors.remove(beanPostProcessor);
        this.beanPostProcessors.add(beanPostProcessor);
    }

    public List<BeanPostProcessor> getBeanPostProcessors() {
        return beanPostProcessors;
    }
}

3.6.3 配置列表Bean工厂接口

ConfigurableListableBeanFactory.java

package com.lino.springframework.beans.factory;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.config.AutowireCapableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.ConfigurableBeanFactory;

/**
 * @description: 配置列表 Bean工厂接口
 */
public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {

    /**
     * 根据对象名称获取bean对象
     *
     * @param beanName 对象名称
     * @return bean对象
     * @throws BeansException bean异常
     */
    BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    /**
     * 实例化单例 Bean 对象
     *
     * @throws BeansException bean异常
     */
    void preInstantiateSingletons() throws BeansException;
}
  • 添加实例化单例 Bean 对象方法。

3.6.4 默认的Bean工厂实现类

DefaultListableBeanFactory.java

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

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;

import java.util.HashMap;
import java.util.Map;

/**
 * @description: 默认的Bean工厂实现类
 */
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry, ConfigurableListableBeanFactory {

    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new BeansException("No bean named '" + beanName + "' is defined");
        }
        return beanDefinition;
    }

    @Override
    public void preInstantiateSingletons() throws BeansException {
        beanDefinitionMap.keySet().forEach(this::getBean);
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName, beanDefinition);
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return beanDefinitionMap.containsKey(beanName);
    }

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        Map<String, T> result = new HashMap<>(16);
        beanDefinitionMap.forEach((beanName, beanDefinition) -> {
            Class beanClass = beanDefinition.getBeanClass();
            if (type.isAssignableFrom(beanClass)) {
                result.put(beanName, (T) getBean(beanName));
            }
        });
        return result;
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return beanDefinitionMap.keySet().toArray(new String[0]);
    }
}

3.6.5 Bean工厂超类接口

AutowireCapableBeanFactory.java

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

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.BeanFactory;

/**
 * @description: Bean工厂超类接口
 */
public interface AutowireCapableBeanFactory extends BeanFactory {

    /**
     * 执行 BeanPostProcessors 接口实现类的 postProcessorsBeforeInitialization 方法
     *
     * @param existingBean 现有的对象
     * @param beanName     对象名称
     * @return 新对象
     * @throws BeansException 异常
     */
    Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;

    /**
     * 执行 BeanPostProcessors 接口实现类的 postProcessorsAfterInitialization 方法
     *
     * @param existingBean 现有的对象
     * @param beanName     对象名称
     * @return 新对象
     * @throws BeansException 异常
     */
    Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;
}
  • 添加了执行 BeanPostProcessors 接口实现类的前置和后置操作。

3.6.6 在Bean创建时完成前置和后置处理

AbstractAutowireCapableBeanFactory.java

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

import cn.hutool.core.bean.BeanUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.PropertyValues;
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;

/**
 * @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);
        }
        registerSingletonBean(beanName, bean);
        return bean;
    }

    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
        // 1.执行 BeanPostProcessor Before 处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        // 待完成内容:invokeInitMethods(beanName, wrappedBean, beanDefinition)
        invokeInitMethods(beanName, wrappedBean, beanDefinition);

        // 2.执行 BeanPostProcessor After 处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        return wrappedBean;
    }

    private void invokeInitMethods(String beanName, Object wrappedBean, BeanDefinition beanDefinition) {

    }

    private void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        try {
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();
                if (value instanceof BeanReference) {
                    // A 依赖 B,获取 B 的实例化
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                // 属性填充
                BeanUtil.setFieldValue(bean, name, value);
            }
        } catch (Exception e) {
            throw new BeansException("Error setting property values: " + beanName);
        }
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
        Constructor constructorToUse = null;
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor ctor : declaredConstructors) {
            if (null != args && ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }
        return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
    }

    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }

    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (null == current) {
                return result;
            }
            result = current;
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (null == current) {
                return result;
            }
            result = current;
        }
        return result;
    }
}
  • 实现 BeanPostProcessor 接口后,会涉及到两个接口方法:postProcessBeforeInitializationpostProcessAfterInitialization
    • 分别作用于 Bean 对象执行初始化前后的额外处理。
  • 也就是需要在创建 Bean 对象时,在 createBean 方法中添加 initializeBean(beanName, bean, beanDefinition) 操作。
    • 这个操作方法主要是对于前置和后置方法的使用。
      • applyBeanPostProcessorsBeforeInitialization()
      • applyBeanPostProcessorsAfterInitialization()

四、测试:实现应用上下文

4.1 配置测试环境

4.1.1 修改UserService用户对象

UserService.java

package com.lino.springframework.test.bean;

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

    private String uId;
    private String company;
    private String location;
    private UserDao 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 UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
  • 添加 companylocation 字段。

4.1.2 实现BeanFactoryPostProcessor

MyBeanFactoryPostProcessor.java

package com.lino.springframework.test.common;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanFactoryPostProcessor;

/**
 * @description: BeanFactoryPostProcessor 实例化 Bean 对象之前,修改 BeanDefinition 属性
 */
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
        PropertyValues propertyValues = beanDefinition.getPropertyValues();

        propertyValues.addPropertyValue(new PropertyValue("company", "改为:字节跳动"));
    }
}

4.1.3 实现BeanPostProcessor

MyBeanPostProcessor.java

package com.lino.springframework.test.common;

import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.config.BeanPostProcessor;
import com.lino.springframework.test.bean.UserService;

/**
 * @description: BeanPostProcessor 在 Bean 对象执行初始化方法前后进行扩展
 */
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("userService".equals(beanName)) {
            UserService userService = (UserService) bean;
            userService.setLocation("改为:温州");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

4.1.4 基础配置,无BeanFactoryPostProcessor、BeanPostProcessor实现类

spring.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <bean id="userDao" class="com.lino.springframework.test.bean.UserDao"/>

    <bean id="userService" class="com.lino.springframework.test.bean.UserService">
        <property name="uId" value="10001"/>
        <property name="company" value="阿里"/>
        <property name="location" value="杭州"/>
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

4.1.5 增强配置,有BeanFactoryPostProcessor、BeanPostProcessor实现类

springPostProcessor.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <bean id="userDao" class="com.lino.springframework.test.bean.UserDao"/>

    <bean id="userService" class="com.lino.springframework.test.bean.UserService">
        <property name="uId" value="10001"/>
        <property name="company" value="阿里"/>
        <property name="location" value="杭州"/>
        <property name="userDao" ref="userDao"/>
    </bean>

    <bean class="com.lino.springframework.test.common.MyBeanPostProcessor"/>
    <bean class="com.lino.springframework.test.common.MyBeanFactoryPostProcessor"/>
</beans>
  • 这里提供两个配置文件,一个不包含 BeanFactoryPostProcessor、BeanPostProcessor,另外一个是包含的。

4.2 单元测试

4.2.1 不使用应用上下文测试

ApiTest.java

@Test
public void test_BeanFactoryPostProcessorAndBeanPostProcessor() {
    // 1.初始化 BeanFactory
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

    // 2.读取配置文件&注册Bean
    XMLBeanDefinitionReader reader = new XMLBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions("classpath:spring.xml");

    // 3.BeanDefinition 加载完成 & Bean实例化之前,修改 BeanDefinition 的属性值
    MyBeanFactoryPostProcessor beanFactoryPostProcessor = new MyBeanFactoryPostProcessor();
    beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);

    // 4.Bean实例化之后,修改 Bean 属性信息
    MyBeanPostProcessor beanPostProcessor = new MyBeanPostProcessor();
    beanFactory.addBeanPostProcessor(beanPostProcessor);

    // 5.获取Bean对象调用方法
    UserService userService = beanFactory.getBean("userService", UserService.class);
    String result = userService.queryUserInfo();
    System.out.println("测试结果:" + result);
}
  • DefaultListableBeanFactory 创建 BeanFactory 并使用 XMLBeanDefinitionReader 加载配置文件的方式。
  • 接下来就是对 MyBeanFactoryPostProcessorMyBeanPostProcessor 的处理。
    • 一个是在 BeanDefinition 加载完成 & Bean 实例化之前,修改 BeanDefinition 的属性值。
    • 另外一个是在 Bean 实例化之后,修改 Bean 属性信息。

测试结果

测试结果:张三,改为:字节跳动,改为:温州
  • 从测试结果来看,我们配置的属性信息和 spring.xml 配置文件中不一样了。

4.2.2 使用应用上下文

ApiTest.java

@Test
public void test_xml() {
    // 1.初始化 BeanFactory
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:springPostProcessor.xml");

    // 3.获取Bean对象调用方法
    UserService userService = applicationContext.getBean("userService", UserService.class);
    String result = userService.queryUserInfo();
    System.out.println("测试结果:" + result);
}
  • 新增 ClassPathXmlApplicationContext 应用上下文,再操作起来就方便很多。
  • 在这里可以一步把配置文件交给 ClassPathXmlApplicationContext,也不需要管理一些自定义实现的 Spring 接口的类。

测试结果

测试结果:张三,改为:字节跳动,改为:温州

五、总结:实现应用上下文

  • 新增加了 Spring 框架中两个非常重要的接口 BeanFactoryPostProcessor、BeanPostProcessor 同时还添加了关于应用上下文的实现。
    • ApplicationContext 接口的定义是继承 BeanFactory 外新增加功能的接口,它可以满足于自动识别、资源加载、容器事件、监听器等功能。
    • 同时例如一些国际化支持、单例 Bean 自动初始化等,也是可以再这个类里实现和扩充的。
  • 通过本节的实现了解 BeanFactoryPostProcessor、BeanPostProcessor,以后再做一些关于 Spring 中间件的开发时,如果需要用到 Bean 对象的获取以及修改一些属性信息,那么就可以使用这两个接口了。同时 BeanPostProcessor 也是实现 AOP 切面技术的关键所在。

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

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

相关文章

Python数据分析实战-将dataframe某列的值分成不同区间并计算每个区间的频数(附源码和实现效果)

实现功能 将dataframe某列的值分成不同区间并计算每个区间的频数 实现代码 import pandas as pd# 创建dataframe data {Name:[Tom1, Jack1, Steve1, Ricky1, Tom2, Jack2, Steve2, Ricky2],Score:[78,60,59,42,88,34,69,142]} df pd.DataFrame(data) print(df)# 定义区间和…

Linux 部署1Panel现代化运维管理面板远程访问

文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透3. 配置1Panel公网访问地址4. 公网远程访问1Panel管理界面5. 固定1Panel公网地址 前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器&#xff0c;包括主机监控、…

el-form表单动态校验(场景: 输入框根据单选项来动态校验表单 没有选中的选项就不用校验)

el-form表单动态校验 el-form常规校验方式: // 结构部分 <el-form ref"form" :model"form" :rules"rules"><el-form-item label"活动名称: " prop"name" required><el-input v-model"form.name" /…

【网络通信 -- WebRTC】FlexFec 基本知识点总结概述

【网络通信 -- WebRTC】FlexFec 基本知识点总结概述 【1】FlexFec 的保护方案 假设存在一组源数据包(D L)&#xff0c;其序列号从 1 开始运行到 D L 一维非交错行 FEC(1-D Non-interleaved Row FEC) : 一种连续的源数据包进行保护的方案&#xff0c;可用于恢复按行分组的源…

用Pycharm开发Flask框架设置debug模式、port 端口和 host 主机无效的解决办法

方法二 &#xff08;推荐&#xff09; Name 的选择请自行填写&#xff0c;随意。 这里笔者就默认 app 注意事项 需要以这个作为启动项&#xff0c;而不是原来的 flask 启动配置文件。 参考链接 1. 用Pycharm开发Flask框架设置debug模式没有效果的解决办法 2. flask中的de…

项目管理流程文件,招标支撑文件,项目研发,验收等系列支撑文件

写在前面&#xff1a; 我花了一些时间整理了这些年从事软件行业以来的文档&#xff0c;将它们分类整理后&#xff0c;电脑瞬间变得更加简洁。我整理了数百份软件开发过程中的常用文档&#xff0c;包括项目计划、立项计划、需求分析、概要设计、详细设计、数据库设计、用户操作手…

盘点3款超好用的免费录屏软件,快来看!

在现代数字化社会中&#xff0c;录屏软件成为了各行各业中必备的工具之一&#xff0c;用于记录操作步骤、制作教程、分享游戏精彩瞬间等。而寻找一款免费的、功能强大的录屏软件也变得越来越重要。本文将为您介绍3款免费录屏软件&#xff0c;并详细说明使用步骤&#xff0c;帮助…

C语言是否快被时代所淘汰?

今日话题&#xff0c;C语言是否快被时代所淘汰&#xff1f;在移动互联网的冲击下&#xff0c;windows做的人越来越少&#xff0c;WP阵营没人做&#xff0c;后台简单的php&#xff0c;复杂的大数据处理的java&#xff0c;要求性能的c。主流一二线公司基本上没多少用C#的了。其实…

K8S:kubeadm搭建K8S+Harbor 私有仓库

文章目录 一.部署规划1.主机规划2.部署流程 二.kubeadm搭建K8S1.环境准备2.安装docker3. 安装kubeadm&#xff0c;kubelet和kubectl4.部署K8S集群&#xff08;1&#xff09;初始化&#xff08;2&#xff09;部署网络插件flannel&#xff08;3&#xff09;创建 pod 资源 5.部署 …

pikachu——一、暴力破解模块通关教程

pikachu 一、靶场介绍二、Burte Force&#xff08;暴力破解&#xff09;概述三、基于表单的暴力破解四、验证码绕过&#xff08;on client&#xff09;五、验证码绕过&#xff08;on server&#xff09;六、token防爆破&#xff1f; 一、靶场介绍 靶场搭建&#xff1a; https:…

OpenShift 4 - 在 OpenShift Virtualization 上自动部署 OpenShift 托管集群(演示视频)

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.12 OpenShift Virtualization 4.12 ACM 2.8 的环境中验证 文章目录 技术架构安装配置环境环境要求安装配置 OpenShift Virtualization安装配置 Red Hat ACM安装配置 MetalLB在 OpenShift …

无涯教程-JavaScript - DEC2HEX函数

描述 DEC2HEX函数将十进制数转换为十六进制。 语法 DEC2HEX (number, [places])争论 Argument描述Required/Optionalnumber 要转换的十进制整数。 如果number为负数,则将忽略位数,并且DEC2HEX返回10个字符(40位)的十六进制数字,其中最高有效位是符号位。其余的39位是幅度位…

24.绳子切割

目录 题目 Description Input Output 思路 注意事项 C完整代码 题目 Description 有N条绳子&#xff0c;它们的长度分别为Li&#xff0c;Li都是正整数。如果从它们中切割出K条长度相同的绳子&#xff08;绳子的长度为整数&#xff09;&#xff0c;这K条绳子每条最长能有…

照片怎么进行压缩?这几个压缩方法分享给你

照片怎么进行压缩&#xff1f;在如今这个时代&#xff0c;我们经常需要共享照片。但是&#xff0c;一些高像素的照片可能会占用大量存储空间&#xff0c;甚至可能无法通过电子邮件或社交媒体进行共享。因此&#xff0c;我们需要对照片进行压缩&#xff0c;以减小文件的大小并方…

互联网医院App开发:构建医疗服务的技术指南

互联网医院App的开发是一个复杂而具有挑战性的任务&#xff0c;但它也是一个充满潜力的领域&#xff0c;可以为患者和医疗专业人员提供更便捷的医疗服务。本文将引导您通过一些常见的技术步骤来构建一个简单的互联网医院App原型&#xff0c;以了解该过程的基本概念。 技术栈选…

A133P EC200M模块调试

Linux USB驱动框架&#xff1a; USB 是一种分层总线结构。USB 设备与主机之间的数据传输由 USB 控制器控制。Linux USB 驱动程序架构如下图所示。Linux USB 主机驱动包括三部分&#xff1a;USB 主机控制器驱动、USB 核心和 USB 设备驱动。 模块加载 USB 转串口 option 驱动程序…

教师节限定!10场数据科学校内赛与10场数据科学数据科学教学实训工作坊,充实实践教学

9月的第一天&#xff0c;上海的南北高架从早上6点开始堵了两个多小时&#xff0c;因为小区附近有个小学差点连门都出不去。那一刻才如此清晰地感受到&#xff0c;开学了。 事实上暑假的两个月&#xff0c;和鲸的员工们几乎没有感受到“假期”和“学期”之间的区别&#xff0c;…

都在说GPT,如何学习并掌握GPT1-4模型运用

了解更多点击《都在说GPT&#xff0c;如何学习并掌握GPT1-4模型运用》 GPT对于每个科研人员已经成为不可或缺的辅助工具&#xff0c;不同的研究领域和项目具有不同的需求。例如在科研编程、绘图领域&#xff1a; 1、编程建议和示例代码: 无论你使用的编程语言是Python、R、MATL…

Java低代码开发:jvs-list(列表引擎)功能(二)字段及样式配置

字段的增减 进入列表页设计器-页表设计界面&#xff0c;点击新增一行、或者删除按钮&#xff0c;可以对字段进行增减操作&#xff0c;如果对于权限的列表页&#xff0c;可以使用批量创建字段的按钮&#xff1a; 字段的批量设置&#xff0c;点击批量添加如下图所示 字段为中文名…

Linux 进程管理之内核栈和struct pt_regs

文章目录 前言一、内核栈二、struct pt_regs2.1 简介2.2 获取pt_regs 参考资料 前言 Linux内核栈是用于存储内核执行期间的函数调用和临时数据的一块内存区域。每个运行的进程都有自己的内核栈&#xff0c;用于处理中断、异常、系统调用以及内核函数的执行。 每个进程的内核栈…