实现BeanPostProcessor

news2025/1/11 12:50:05

文章目录

    • 1.实现初始化方法
        • 1.目录
        • 2.InitializingBean.java
        • 3.MonsterService.java 实现初始化接口
        • 4.SunSpringApplicationContext.java 调用初始化方法
        • 5.测试
    • 2.实现后置处理器
        • 1.目录
        • 2.BeanPostProcessor.java 后置处理器接口
        • 3.SunBeanProcessor.java 自定义后置处理器
        • 4.SunSpringApplicationContext.java 具体实现
          • 1.定义一个存储后置处理器的List
          • 2.在扫描bean的时候将后置处理器放到List中
          • 3.初始化bean前后使用调用后置处理器进行处理
          • 4.测试
          • 5.完整代码

1.实现初始化方法

1.目录

CleanShot 2024-08-06 at 12.28.31@2x

2.InitializingBean.java
package com.sunxiansheng.sunspring.processor;

/**
 * Description: Bean的初始化接口
 * @Author sun
 * @Create 2024/8/5 16:35
 * @Version 1.0
 */
public interface InitializingBean {

    /**
     * 在Bean的所有属性设置完成之后调用
     */
    void afterPropertiesSet();

}
3.MonsterService.java 实现初始化接口
package com.sunxiansheng.sunspring.compent;

import com.sunxiansheng.sunspring.annotation.Component;
import com.sunxiansheng.sunspring.annotation.Resource;
import com.sunxiansheng.sunspring.annotation.Scope;
import com.sunxiansheng.sunspring.annotation.myenum.MyScope;
import com.sunxiansheng.sunspring.processor.InitializingBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Description: 怪物服务
 * @Author sun
 * @Create 2024/8/4 16:31
 * @Version 1.0
 */
// 自定义注解:将MonsterService类交给自定义Spring容器管理
// 如果指定value属性,则使用value属性值作为bean的id,否则使用类名首字母小写作为bean的id
@Component
@Scope(value = MyScope.PROTOTYPE) // 指定bean的作用域为多例
public class MonsterService implements InitializingBean {

    private static final Logger log = LoggerFactory.getLogger(MonsterService.class);

    /**
     * 自定义的Resource注解,用于自动注入MonsterDao对象
     */
    @Resource
    private MonsterDao monsterDao;

    public void query() {
        log.info("MonsterService调用MonsterDao");
        monsterDao.query();
    }

    /**
     * 初始化方法
     */
    @Override
    public void afterPropertiesSet() {
        log.info("MonsterService初始化完成...");
    }

}
4.SunSpringApplicationContext.java 调用初始化方法

CleanShot 2024-08-06 at 12.33.56@2x

5.测试

CleanShot 2024-08-06 at 12.34.18@2x

2.实现后置处理器

1.目录

CleanShot 2024-08-06 at 13.35.31@2x

2.BeanPostProcessor.java 后置处理器接口
package com.sunxiansheng.sunspring.processor;

/**
 * Description: 自定义BeanPostProcessor接口
 * @Author sun
 * @Create 2024/8/6 12:36
 * @Version 1.0
 */
public interface BeanPostProcessor {

    /**
     * 该方法在bean的初始化方法之前执行
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 该方法在bean的初始化方法之后执行
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }

}
3.SunBeanProcessor.java 自定义后置处理器
package com.sunxiansheng.sunspring.compent;

import com.sunxiansheng.sunspring.annotation.Component;
import com.sunxiansheng.sunspring.processor.BeanPostProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Description:
 * @Author sun
 * @Create 2024/8/6 12:39
 * @Version 1.0
 */
@Component
public class SunBeanProcessor implements BeanPostProcessor {

    private static final Logger log = LoggerFactory.getLogger(SunBeanProcessor.class);

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        log.info("SunBeanProcessor postProcessBeforeInitialization..." + beanName + "=>" + bean.getClass());
        // 返回空,不对bean进行处理
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        log.info("SunBeanProcessor postProcessAfterInitialization..." + beanName + "=>" + bean.getClass());
        // 返回空,不对bean进行处理
        return null;
    }

}
4.SunSpringApplicationContext.java 具体实现
1.定义一个存储后置处理器的List

CleanShot 2024-08-06 at 13.36.53@2x

2.在扫描bean的时候将后置处理器放到List中

CleanShot 2024-08-06 at 13.37.17@2x

3.初始化bean前后使用调用后置处理器进行处理

CleanShot 2024-08-06 at 13.37.53@2x

4.测试

CleanShot 2024-08-06 at 13.38.49@2x

5.完整代码
package com.sunxiansheng.sunspring.ioc;

import com.sunxiansheng.sunspring.annotation.Component;
import com.sunxiansheng.sunspring.annotation.ComponentScan;
import com.sunxiansheng.sunspring.annotation.Resource;
import com.sunxiansheng.sunspring.annotation.Scope;
import com.sunxiansheng.sunspring.annotation.myenum.MyScope;
import com.sunxiansheng.sunspring.processor.BeanPostProcessor;
import com.sunxiansheng.sunspring.processor.InitializingBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Description: 自定义Spring容器
 * @Author sun
 * @Create 2024/8/4 16:35
 * @Version 1.0
 */
public class SunSpringApplicationContext {

    private static final Logger log = LoggerFactory.getLogger(SunSpringApplicationContext.class);

    /**
     * bean定义的map
     */
    private ConcurrentHashMap<String, BeanDefintion> beanDefintionMap = new ConcurrentHashMap<>();

    /**
     * 单例池
     */
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * bean的后置处理器
     */
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    // 构造器,接收配置类的class对象
    public SunSpringApplicationContext(Class<?> configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 完成bean的扫描,将bean的信息记录到beanDefintionMap中
        beanDefinitionByScan(configClass);
        // 初始化单例池
        initSingletonObjects();
    }

    /**
     * 给某个bean对象完成依赖注入
     */
    private void populateBeans(Object bean) {
        // 扫描beanDefintionMap中的bean信息,对bean对象中的属性进行依赖注入
        // 获取Class对象
        Class<?> clazz = bean.getClass();
        // 获取所有字段
        Field[] fields = clazz.getDeclaredFields();
        // 判断字段上是否有@Resource注解
        for (Field field : fields) {
            if (field.isAnnotationPresent(Resource.class)) {
                // 获取字段名
                String fieldName = field.getName();
                // 根据字段名获取bean对象
                Object beanObject = null;
                // 从beanDefintionMap中获取bean对象
                BeanDefintion beanDefintion = beanDefintionMap.get(fieldName);
                try {
                    // 根据bean的定义信息创建bean对象
                    beanObject = getBean(fieldName);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                // 设置字段可访问
                field.setAccessible(true);
                try {
                    // 依赖注入
                    field.set(bean, beanObject);
                    log.info("依赖注入成功:{} => {}.{}", beanObject.getClass(), clazz, fieldName);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 初始化单例池
     */
    private void initSingletonObjects() {
        // 将beanDefintionMap中的bean信息创建成bean对象放到单例池中
        beanDefintionMap.forEach((beanName, beanDefintion) -> {
            try {
                // 根据bean的定义信息创建bean对象
                Object bean = createBean(beanDefintion);
                if (bean != null) {
                    // 将bean对象放到单例池中
                    singletonObjects.put(beanName, bean);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        // 打印单例池中的bean对象
        log.info("根据bean定义信息初始化单例池:{}", singletonObjects);
    }

    // 返回容器中的对象
    public Object getBean(String name) throws Exception {
        BeanDefintion beanDefintion = beanDefintionMap.get(name);
        if (beanDefintion == null) {
            throw new NullPointerException("在bean定义中没有找到bean对象");
        }
        // 根据单例和多例来获取bean对象
        MyScope scope = beanDefintion.getScope();
        Object bean = null;
        if (scope == MyScope.SINGLETON) {
            log.info("getBean单例对象:{}", singletonObjects.get(name));
            // 单例就直接从单例池中获取对象
            bean = singletonObjects.get(name);
        } else {
            // 多例就创建一个新的对象
            bean = createProtoTypeBean(beanDefintion);
        }
        // 给bean对象完成依赖注入
        populateBeans(bean);
        // 记录当前对象,因为后置处理器可能会返回一个新的对象
        Object current = bean;
        // 初始化方法之前调用后置处理器 postProcessBeforeInitialization
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            Object bean1 = beanPostProcessor.postProcessBeforeInitialization(bean, name);
            // 如果beanPostProcessor返回的对象为空,则使用原来的对象
            if (bean1 != null) {
                current = bean1;
            }
        }
        // 初始化bean
        init(current);
        // 初始化方法之后调用后置处理器 postProcessAfterInitialization
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            bean = beanPostProcessor.postProcessAfterInitialization(current, name);
            // 如果beanPostProcessor返回的对象为空,则使用原来的对象
            if (bean == null) {
                bean = current;
            }
        }
        log.info("getBean多例对象:{}", bean);
        return bean;
    }

    /**
     * 初始化bean
     * @param bean
     */
    public void init(Object bean) {
        if (bean instanceof InitializingBean) {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

    /**
     * 根据bean的定义信息创建bean对象(单例bean)
     * @param beanDefintion
     * @return
     * @throws Exception
     */
    private Object createBean(BeanDefintion beanDefintion) throws Exception {
        // 得到bean的类型
        Class<?> clazz = beanDefintion.getClazz();
        // 根据bean的作用域创建bean对象,多例就不创建了,单例就创建
        if (beanDefintion.getScope() == MyScope.PROTOTYPE) {
            return null;
        }
        Object bean = clazz.getDeclaredConstructor().newInstance();
        return bean;
    }

    /**
     * 创建多例bean
     * @param beanDefintion
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     */
    private static Object createProtoTypeBean(BeanDefintion beanDefintion) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        // 多例就创建一个新的对象
        Class<?> clazz = beanDefintion.getClazz();
        Object bean = clazz.getDeclaredConstructor().newInstance();
        return bean;
    }

    /**
     * 完成bean的扫描,将bean的信息记录到beanDefintionMap中
     * @param configClass
     * @throws ClassNotFoundException
     */
    private void beanDefinitionByScan(Class<?> configClass) {
        // 传进来一个配置类的Class对象
        // 一、获取要扫描的包
        // 1.首先反射获取类的注解信息
        ComponentScan componentScan = configClass.getDeclaredAnnotation(ComponentScan.class);
        // 2.通过注解来获取要扫描的包的路径
        String path = componentScan.packagePath();
        log.info("扫描的包路径:{}", path);
        // 二、得到要扫描包的.class文件对象,从而得到全路径进行反射
        // 1.获取App类加载器
        ClassLoader classLoader = SunSpringApplicationContext.class.getClassLoader();
        // 2.获取要扫描包的真实路径,默认刚开始在根目录下
        path = path.replace(".", "/");
        URL resource = classLoader.getResource(path);
        // 3.由该路径创建一个文件对象,可使用resource.getFile()将URL类型转化为String类型
        File file = new File(resource.getFile());
        // 4.遍历该文件夹下的所有.class文件对象
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                // 反射注入容器
                // 1.获取所有文件的绝对路径
                String absolutePath = f.getAbsolutePath();
                // 只处理class文件
                if (absolutePath.endsWith(".class")) {
                    // 2.分割出类名
                    String className = extractClassName(absolutePath);
                    // 3.得到全路径
                    String fullPath = path.replace("/", ".") + "." + className;
                    // 4.判断是否需要注入容器,查看有没有自定义的注解Component
                    Class<?> aClass = null;
                    try {
                        aClass = classLoader.loadClass(fullPath);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                    // 如果该类使用了注解Component则说明是一个spring bean
                    if (aClass.isAnnotationPresent(Component.class)) {
                        log.info("扫描到Spring Bean:{}", aClass);

                        // 将Bean的后置处理器加入到beanPostProcessorList中
                        // 判断Class对象是否实现了BeanPostProcessor接口
                        if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                            Object o = null;
                            try {
                                o = aClass.getDeclaredConstructor().newInstance();
                            } catch (Exception e) {
                                log.info("BeanPostProcessor实例化失败:{}", e);
                            }
                            if (o instanceof BeanPostProcessor) {
                                beanPostProcessorList.add((BeanPostProcessor) o);
                            }
                            log.info("BeanPostProcessor实例化成功:{}", o);
                            // 直接跳过,不需要将BeanPostProcessor加入到beanDefintionMap中
                            continue;
                        }

                        // 将bean的信息记录到beanDefintionMap中
                        BeanDefintion beanDefintion = new BeanDefintion();
                        // 1.获取Scope注解的value值
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope scope = aClass.getDeclaredAnnotation(Scope.class);
                            MyScope value = scope.value();
                            // 放到beanDefintion中
                            beanDefintion.setScope(value);
                        } else {
                            // 如果没有指定作用域,则默认为单例
                            beanDefintion.setScope(MyScope.SINGLETON);
                        }
                        beanDefintion.setClazz(aClass);

                        // 2.获取Component注解的value值
                        Component component = aClass.getDeclaredAnnotation(Component.class);
                        String beanName = component.value();
                        if ("".equals(beanName)) {
                            // 如果没有指定value属性,则使用类名首字母小写作为bean的id
                            beanName = className.substring(0, 1).toLowerCase() + className.substring(1);
                        }

                        // 3.将bean的id和bean的信息放到beanDefintionMap中
                        beanDefintionMap.put(beanName, beanDefintion);
                    } else {
                        log.info("这不是一个Spring Bean={}", aClass);
                    }
                }
            }
        }
        // 打印beanDefintionMap中的bean信息
        log.info("将bean定义信息放到beanDefintionMap:{}", beanDefintionMap);
    }

    /**
     * 分割出类名
     * 类似于 com/sunxiansheng/sunspring/compent/MonsterService.class 的类名
     * @param filePath
     * @return
     */
    private String extractClassName(String filePath) {
        // 获取最后一个 '/' 的位置
        int lastSlashIndex = filePath.lastIndexOf('/');
        // 获取最后一个 '.' 的位置
        int lastDotIndex = filePath.lastIndexOf('.');
        // 提取两者之间的字符串作为类名
        return filePath.substring(lastSlashIndex + 1, lastDotIndex);
    }

}

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

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

相关文章

萌啦数据怎么选品,萌啦数据ozon选品怎么选

在当今电商领域&#xff0c;数据已成为商家决策的重要基石。尤其是在跨境电商的广阔舞台上&#xff0c;如何借助数据分析工具精准选品&#xff0c;直接关系到商家在激烈竞争中的生存与发展。萌啦数据&#xff0c;作为业界知名的电商数据分析平台&#xff0c;凭借其强大的数据整…

DHCP协议-CSP认证

文章目录 DHCP协议 DHCP协议 stringstream的用法 应用实践 #include<bits/stdc.h>using namespace std;signed main() {string s"wo shi wwl, also wlw";stringstream ss;ss<<s;while(ss>>s){if(s[s.size()-1],) s[s.size()-1] ;cout<<s<…

128-域横向移动IPCATSC命令Impacket套件CS插件全自动

因为只有一台云服务器而且开不起台多的虚拟机只能只用几台进行演示&#x1f62d;&#x1f62d;&#x1f62d; 小迪的环境 上线之后先做的事情&#xff1a; IPC方式进行横向移动 at schtasks 他这里第三条命令有问题换成这个可以执行 schtasks /create /s 172.16.3.128 /U a…

证书学习(二)搞懂 keystore、jks、p12、pfx、crt、csr、pem文件的区别

目录 一、背景二、文件格式的区分2.1 .keystore / .jks 文件2.2 .p12 / .pfx 文件2.3 .crt 文件2.4 csr 文件2.5 .pem 文件 三、总结 一、背景 我们在日常的开发过程中&#xff0c;经常会见到各种各样的证书相关类型的文件&#xff0c;错综复杂。 其实 keystore、jks、p12、p…

濮阳示范区党工委书记杨行玉一行莅临超维机器人参观考察

8月20日&#xff0c;濮阳示范区党工委书记杨行玉带队赴郑州开展招商活动&#xff0c;并莅临超维机器人参观考察。此次考察旨在加快濮阳示范区产业升级步伐&#xff0c;推动高科技项目引进和落地&#xff0c;为区域经济高质量发展注入新动能。区三级调研员张立春、区招商局负责人…

外挂系统 -减少SAP用户许可数, 多点操作及时同步SAP

用过SAP 的人都知道SAP 是按照用户数来计算项目费用和年维护费的,所以很多公司设置了共同账号,外挂程序,WDA程序等,各种各样的规避方式。 当然SAP 标准的功能也相对简单化,要实现一些自定义的功能来满足各企业实际业务,那需要进行二次开发,有些顾问公司则通过在SAP 中开…

云计算实训33——高并发负载均衡项目(eleme)

一、配置一主两从mysql服务器&#xff08;mysql5.7&#xff09; 1.主服务器master 下载mysql5.7的包 [rootMysql ~]# rz -E rz waiting to receive. [rootMysql ~]# ls anaconda-ks.cfg mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz #解压 [rootMysql ~]# tar -zxf mysql-5…

用TensorFlow实现线性回归

说明 本文采用TensorFlow框架进行讲解&#xff0c;虽然之前的文章都采用mxnet&#xff0c;但是我发现tensorflow提供了免费的gpu可供使用&#xff0c;所以果断开始改为tensorflow&#xff0c;若要实现文章代码&#xff0c;可以使用colaboratory进行运行&#xff0c;当然&#…

ZooKeeper入门及核心知识点整理

什么是Zookeeper Zookeeper简称zk&#xff0c;先从字面意思上去理解&#xff0c;那就是动物园管理员。其实zk是大数据领域中的一员&#xff0c;为整个分布式环境提供了协调服务&#xff0c;主要可以用于存储一些配置信息&#xff0c;同时也可以基于zk实现集群。它是一个apache…

RabbitMQ的基础概念介绍

MQ的三大特点&#xff1a;削峰、异步、解耦 1.RabblitMQ概念介绍 1.1概念 RabbitMQ是由erlang语言开发&#xff0c;基于AMQP&#xff08;Advanced Message Queue 高级消息队列协议&#xff09;协议实现的消息队列&#xff0c;它是一种应用程序之间的通信方法&#xff0c;消息…

【docker】使用docker-compose的时候如何更新镜像版本

使用docker-compose的时候如何更新镜像版本。之前总是会忘记怎么操作&#xff0c;每次都得百度搜&#xff0c;干脆记录一下。 说明 我有一个memos是用docker-compose部署的&#xff0c;memos更新的挺频繁的&#xff0c;新版本的功能也不错&#xff0c;更新一下没啥问题。 注…

吴恩达机器学习课后作业-03多分类、神经网络前向传播

这里写目录标题 逻辑回归解决多分类问题&#xff08;逻辑回归的“一对多”&#xff08;One-vs-All&#xff09;策略。&#xff09;绘制图像结果 神经网络前向传播数字识别 、 逻辑回归解决多分类问题&#xff08;逻辑回归的“一对多”&#xff08;One-vs-All&#xff09;策略。…

[Algorithm][综合训练][孩子们的游戏][大数加法][拼三角]详细讲解

目录 1.孩子们的游戏1.题目链接2.算法原理详解 && 代码实现 2.大数加法1.题目链接2.算法原理详解 && 代码实现 3.拼三角1.题目链接2.算法原理详解 && 代码实现 1.孩子们的游戏 1.题目链接 孩子们的游戏 2.算法原理详解 && 代码实现 问题抽象…

LongWriter——从长文本语言模型中释放出10,000+字的生成能力

概述 当前的长上下文大型语言模型 (LLM) 可以处理多达 100,000 个词的输入&#xff0c;但它们很难生成超过 2,000 个词的输出。受控实验表明&#xff0c;该模型的有效生成长度本质上受到监督微调(SFT) 期间看到的示例的限制。换句话说&#xff0c;这种输出限制源于现有 SFT 数…

三维模型单体化软件:地理信息与遥感领域的精细化革命

在地理信息与遥感科学日新月异的发展浪潮中&#xff0c;单体化软件作为一股强大的驱动力&#xff0c;正引领着我们迈向空间信息处理与应用的新纪元。本文旨在深度解析单体化软件的核心价值、技术前沿、实践应用及面临的挑战&#xff0c;共同探讨这一技术如何塑造行业的未来。 …

【手撕OJ题】——BM8 链表中倒数最后k个结点

目录 &#x1f552; 题目⌛ 方法① - 直接遍历⌛ 方法② - 快慢指针 &#x1f552; 题目 &#x1f50e; BM8 链表中倒数最后k个结点【难度&#xff1a;简单&#x1f7e2;】 输入一个长度为 n 的链表&#xff0c;设链表中的元素的值为 a i a_i ai​ &#xff0c;返回该链表中倒…

一款MySQL数据库实时增量同步工具,能够监听MySQL二进制日志(Binlog)的变动(附源码)

背景 作为一名CURD的程序员&#xff0c;少不了跟MySQL打交道&#xff0c;在同步数据的时候&#xff0c;MySQL的Binlog显得重中之重&#xff0c;所以处理Binlog的工具尤为重要。 其中阿里巴巴开源的canal 更是耳闻目睹&#xff0c;但是今天小编给大家介绍另外一款MySQL数据库实…

【C++11】常用新语法②(类的新功能 || 可变参数模板 || lambda表达式 || 包装器)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; C 目录 &#x1f308;前言&#x1f525;类的新功能新增默认成员函数强制生成默认函数的关键字default禁止生成默认函数的关键字delete &#x1f525;可变参数模板递归函数方式展开参数包…

论文翻译:Benchmarking Large Language Models in Retrieval-Augmented Generation

https://ojs.aaai.org/index.php/AAAI/article/view/29728 检索增强型生成中的大型语言模型基准测试 文章目录 检索增强型生成中的大型语言模型基准测试摘要1 引言2 相关工作3 检索增强型生成基准RAG所需能力数据构建评估指标 4实验设置噪声鲁棒性结果负面拒绝测试平台结果信息…

算法5:位运算

文章目录 小试牛刀进入正题 没写代码的题&#xff0c;其链接点开都是有代码的。开始前请思考下图&#xff1a; 小试牛刀 位1的个数 class Solution { public:int hammingWeight(int n) {int res 0;while (n) {n & n - 1;res;}return res;} };比特位计数 class Solution…