当前位置: 首页 > news >正文

系列十六、Spring IOC容器的扩展点

一、概述

        Spring IOC容器的扩展点是指在IOC加载的过程中,如何对即将要创建的bean进行扩展。

二、扩展点

2.1、BeanDefinitionRegistryPostProcessor

2.1.1、概述

        BeanDefinitionRegistryPostProcessor是bean定义的后置处理器,在BeanDefinition加载后,实例化bean之前,调用 invokeBeanFactoryPostProcessors时进行扩展,通过改变BeanDefinition的定义信息进行扩展,源码如下:

2.1.2、继承结构

2.1.3、案例

2.1.3.1、ATM
/*** @Author : 一叶浮萍归大海* @Date: 2023/11/23 15:06* @Description:*/
@Slf4j
@Component(value = "atm")
public class ATM {public int withdrawMoney(int money) {log.info("取钱方法正在执行...");if (money == 100) {throw new RuntimeException("自定义的异常");}return money;}}
2.1.3.2、MySpringConfig
/*** @Author : 一叶浮萍归大海* @Date: 2023/11/23 15:29* @Description:*/
@Configuration
@ComponentScan(basePackages = {"org.star"})
public class MySpringConfig {}
 2.1.3.3、MyBeanDefinitionRegistryPostProcessor 
/*** @Author : 一叶浮萍归大海* @Date: 2023/11/25 17:38* @Description:*/
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {/*** 作用:动态注册BeanDefinition* 调用时机:IOC加载时注册BeanDefinition的时候会调用* @param registry the bean definition registry used by the application context* @throws BeansException*/@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {RootBeanDefinition definition = new RootBeanDefinition(ATM.class);// 设置ATM bean为多实例definition.setScope("prototype");registry.registerBeanDefinition("atm",definition);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinition beanDefinition = beanFactory.getBeanDefinition("atm");// 将atm设置为懒加载,这样在容器启动时将不会创建bean,只有在getBean时才会创建对象beanDefinition.setLazyInit(true);}
}
2.1.3.4、AopFullAnnotationMainApp 
/*** @Author : 一叶浮萍归大海* @Date: 2023/11/23 15:14* @Description:*/
@Slf4j
public class AopFullAnnotationMainApp {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);ATM atm1 = context.getBean("atm", ATM.class);ATM atm2 = context.getBean("atm", ATM.class);log.info("atm1:{},atm2:{},(atm1 == atm2):{}", atm1,atm2,(atm1 == atm2));}}

 

2.2、xxxAware接口

2.2.1、概述

        Spring中存在着大量的xxxAware接口实现类,用于在bean初始化完成之前做一些前置操作,程序员可以自己实现xxxAware接口,重写里边的方法修改bean的定义信息,进行扩展。

2.2.2、继承结构

2.2.3、案例

2.2.3.1、ATM

同上。

2.2.3.2、MySpringConfig

同上。

2.2.3.3、MyApplicationContextAware
/*** @Author : 一叶浮萍归大海* @Date: 2023/11/25 18:51* @Description:*/
@Component
public class MyApplicationContextAware implements ApplicationContextAware {public ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;AnnotationConfigApplicationContext context = (AnnotationConfigApplicationContext) applicationContext;ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();BeanDefinition beanDefinition = beanFactory.getBeanDefinition("atm");beanDefinition.setScope("prototype");}}
2.2.3.4、AopFullAnnotationMainApp

同上。

2.2.3、调用链路

2.2.4、注意事项

        通过观察 2.1.3.4和 2.2.3.4的执行结果,不能发现xxxAware接口中配置的扩展覆盖了BeanDefinitionRegistryPostProcessor中的配置,说明xxxAware的优先级更高,这个也很好理解,对于同样的一个bean,后边的配置肯定会覆盖掉前边的配置。

2.3、生命周期回调时进行扩展

2.3.1、概述

        bean的生命周期回调主要分为两种,一种是初始化进行调用,另外一种是销毁时进行调用,但是不管是初始化还是销毁,都对应着三种方式,即:

        a、@PostConstruct @PreDestroy
        b、实现接口 InitializingBean, DisposableBean的方式
        c、@Bean(initMethod = "init",destroyMethod = "destroy")的方式

2.4、初始化后实例化前进行扩展

2.4.1、bean创建完成的标识

        当循环完所有的DeanDefinition后,bean就创建完了。

2.4.2、大致流程

启动IOC容器 ===> refresh() ===>finishBeanFactoryInitialization(beanFactory)===>beanFactory.preInstantiateSingletons()

2.4.3、源码解析

#1、启动IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);#2、refresh()
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {this();register(componentClasses);refresh();
}# 3、finishBeanFactoryInitialization(beanFactory)
@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}
}# 4、beanFactory.preInstantiateSingletons();
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// Initialize conversion service for this context.if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));}// Register a default embedded value resolver if no bean post-processor// (such as a PropertyPlaceholderConfigurer bean) registered any before:// at this point, primarily for resolution in annotation attribute values.if (!beanFactory.hasEmbeddedValueResolver()) {beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));}// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);for (String weaverAwareName : weaverAwareNames) {getBean(weaverAwareName);}// Stop using the temporary ClassLoader for type matching.beanFactory.setTempClassLoader(null);// Allow for caching all bean definition metadata, not expecting further changes.beanFactory.freezeConfiguration();// Instantiate all remaining (non-lazy-init) singletons.beanFactory.preInstantiateSingletons();
}# 5、beanFactory.preInstantiateSingletons();
@Override
public void preInstantiateSingletons() throws BeansException {if (logger.isTraceEnabled()) {logger.trace("Pre-instantiating singletons in " + this);}// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...for (String beanName : beanNames) {RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {if (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {final FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}}else {getBean(beanName);}}}// Trigger post-initialization callback for all applicable beans...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());}else {smartSingleton.afterSingletonsInstantiated();}}}
}

2.4.4、通过监听器扩展

2.4.4.1、Computer
/*** @Author : 一叶浮萍归大海* @Date: 2023/11/27 11:52* @Description:*/
@Getter
@Setter
@Accessors(chain = true)
@Component
public class Computer {/*** 电脑名称*/private String name;/*** 品牌*/private String brand;}
2.4.4.2、 MyContextRefreshedEvent 
/*** @Author : 一叶浮萍归大海* @Date: 2023/11/27 11:48* @Description: 监听器*/
@Component
public class MyContextRefreshedEvent {@EventListener(ContextRefreshedEvent.class)public void onContextRefreshedEvent(ContextRefreshedEvent event) {System.out.println(event);// bean初始化完成后做扩展,扩展代码写在这里ConfigurableApplicationContext context = (ConfigurableApplicationContext) event.getApplicationContext();DefaultListableBeanFactory factory = (DefaultListableBeanFactory) context.getBeanFactory();BeanDefinition beanDefinition = factory.getBeanDefinition("computer");beanDefinition.setScope("prototype");factory.registerBeanDefinition("computer",beanDefinition);System.out.println("all singleton beans loaded,onContextRefreshedEvent execute success!");}}
2.4.4.3、MySpringConfig(同上)
2.4.4.4、AopFullAnnotationMainApp 
@Slf4j
public class AopFullAnnotationMainApp {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);Computer computer1 = context.getBean(Computer.class);Computer computer2 = context.getBean(Computer.class);log.info("computer1:{},computer2:{},(computer1 == computer2 ?) : {}",computer1,computer2,(computer1 == computer2));}
}

2.4.5、通过接口扩展

2.4.5.1、Computer
/*** @Author : 一叶浮萍归大海* @Date: 2023/11/27 11:52* @Description:*/
@Getter
@Setter
@Accessors(chain = true)
@Component
@Slf4j
public class Computer {/*** 电脑名称*/private String name;/*** 品牌*/private String brand;public void init() {Computer computer = new Computer().setName("OptiPlex7010MT Plus13").setBrand("戴尔");log.info("Computer's init was invoked! computer:{}", JSON.toJSONString(computer));}}
2.4.5.2、MySmartInitializingSingleton
/*** @Author : 一叶浮萍归大海* @Date: 2023/11/27 13:04* @Description:*/
@Component
public class MySmartInitializingSingleton implements SmartInitializingSingleton {@Resourceprivate ApplicationContext applicationContext;@Overridepublic void afterSingletonsInstantiated() {ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;DefaultListableBeanFactory factory = (DefaultListableBeanFactory) context.getBeanFactory();BeanDefinition beanDefinition = factory.getBeanDefinition("computer");beanDefinition.setInitMethodName("init");beanDefinition.setScope("prototype");factory.registerBeanDefinition("computer",beanDefinition);System.out.println("all singleton beans loaded,afterSingletonsInstantiated execute success!");}
}
2.4.5.3、MySpringConfig(同上)
2.4.5.4、AopFullAnnotationMainApp(同上) 

相关文章:

系列十六、Spring IOC容器的扩展点

一、概述 Spring IOC容器的扩展点是指在IOC加载的过程中&#xff0c;如何对即将要创建的bean进行扩展。 二、扩展点 2.1、BeanDefinitionRegistryPostProcessor 2.1.1、概述 BeanDefinitionRegistryPostProcessor是bean定义的后置处理器&#xff0c;在BeanDefinition加载后&a…...

eclipse项目移到idea上部署运行

1.配置web模块 另外&#xff0c;模块这里&#xff0c;也要加上Spring 2.配置Artifact &#xff08;用于tomcat&#xff09; 就是从上面配置的web模块&#xff0c;产生的工件 3.添加lib 一般是在web-inf/lib &#xff0c; 遇到的坑&#xff1a; jdk版本问题&#xff0c;这里…...

支持向量机的算法原理

支持向量机&#xff08;Support Vector Machine&#xff0c;简称SVM&#xff09;是机器学习领域中一种常用的分类算法&#xff0c;它基于统计学习理论和结构风险最小化原则&#xff0c;具有很强的理论基础和良好的分类性能。本文将详细介绍支持向量机的算法原理&#xff0c;并解…...

gitlab 12升级14(解决各种报错问题)

1.这里是从自己公司的源下载的rpm包&#xff0c;需要换成自己的 2.从12的最后一个版本升级到14的最后一个版本 # 停服务 [rootdocker test]# gitlab-ctl stop puma && gitlab-ctl stop sidekiq && gitlab-ctl stop nginx && gitlab-ctl status# 进入…...

给element plus中动态form-item增加校验的可行方法

element plus中的form组件自带校验机制。在常规使用场景中&#xff0c;表单项是固定的、明确的&#xff0c;且数量不会太多。校验规则的使用也如下&#xff1a; <template><div class"edit-page"><el-form :model"formModel" ref"for…...

C++学习之值传递

c/c中存在三种传值方式&#xff0c;在局部函数中&#xff0c;对这三种传值方式传入的参数进行修改&#xff0c;会得到不同的结果。具体见下例&#xff1a; #include <stdlib.h> #include <stdio.h>static int dummny 10000;// 传值(传过来的是原始值的副本&#…...

网络视频播放卡顿原因分析

一、问题描述 某项目通过拉摄像机rtsp流转rtmp/http-flv/ws-flv的方案&#xff0c;使用户可以在网页中观看摄像机的视频画面。在 观看视频时偶发出现卡顿现象。 二、卡顿现象分析和解决 此问题涉及的原因较多&#xff0c;所以得考虑各环节的问题可能性&#xff0c;并根据现场实…...

Android 相机库CameraView源码解析 (二) : 拍照

1. 前言 这段时间&#xff0c;在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位&#xff0c;在项目前期&#xff0c;的确为我们节省了不少时间。 但随着项目持续深入&#xff0c;对于CameraView的使用进入深水区&#xff0c;逐…...

计算机缺少d3dx9_43.dll怎么办?5个方法快速修复d3dx9_43.dll文件

在计算机使用过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“d3dx9_43.dll丢失”。这个问题可能会影响到我们的游戏体验或者软件运行。为了解决这个问题&#xff0c;我查阅了一些资料并尝试了多种方法。在这里&#xff0c;我想分享一下我对d3dx9_43.d…...

2023亚太杯数学建模C题思路分析 - 我国新能源电动汽车的发展趋势

1 赛题 问题C 我国新能源电动汽车的发展趋势 新能源汽车是指以先进技术原理、新技术、新结构的非常规汽车燃料为动力来源( 非常规汽车燃料指汽油、柴油以外的燃料&#xff09;&#xff0c;将先进技术进行汽车动力控制和驱动相结 合的汽车。新能源汽车主要包括四种类型&#x…...

c语言新龟兔赛跑

以下是一个使用C语言编写的新的龟兔赛跑游戏&#xff1a; #include <stdio.h>#include <stdlib.h>#include <time.h>int main() { int distance, turtle_speed, rabbit_speed, turtle_time, rabbit_time, rabbit_lead; srand(time(NULL)); // 随机数种…...

Linux驱动开发——网络设备驱动(理论篇)

目录 一、前言 二、网络层次结构 三、网络设备驱动核心数据结构和函数 一、前言 网络设备驱动是 Linux 的第三大类驱动&#xff0c;也是我们学习的最后一类 Linux 驱动。这里我们首先简单学习一下网络协议层次结构&#xff0c;然后简单讨论 Linux 内核中网络实现的层次结构。…...

simulink仿真

1&#xff09;系统问题 连续系统&#xff0c;离散系统&#xff08;采样周期问题&#xff09; 系统分析问题 2&#xff09;求解器问题 变步长&#xff0c;定步长&#xff0c;步长时间与采样周期问题、 3&#xff09;积分器问题 连续积分&#xff0c;离散积分问题&#xff…...

PC端页面进去先出现加载效果

自定义指令v-loading&#xff0c;只需要绑定Boolean即可 v-loading“loading” <el-table :data"list" border style"width: 100%" v-loading"loading"><el-table-column align"center" label"序号" width"5…...

磁盘清理在哪里?学会这4个方法,快速清理内存!

“在使用电脑的过程中&#xff0c;我可能经常会保存一些文件到电脑上&#xff0c;这也导致电脑经常出现内存不足的情况。我想问问磁盘清理在哪里呀&#xff1f;我应该如何打开呢&#xff1f;” 随着使用电脑的时间增长&#xff0c;用户可能经常会遇到磁盘空间不足的情况&#x…...

Error opening terminal: xterm.”的解决方法

主要是看下面这两个变量是否设置正确 $ echo $TERM $ echo $TERMINFO 通常TERM的默认值为xterm-265color, 要查看支持的term&#xff0c;可以ls -al /lib/terminfo/x/ 如果TERM是xterm-265color的话&#xff0c;TERMINFO设置为/usr/lib/terminfo make menuconfig时提示“Err…...

C#常见的设计模式-结构型模式

引言 设计模式是软件工程中用于解决常见问题的可复用解决方案。在C#编程中&#xff0c;常见的设计模式具有广泛的应用。本篇博客将重点介绍C#中常见的结构型设计模式&#xff0c;包括适配器模式、装饰器模式、代理模式、组合模式和享元模式。 目录 引言1. 适配器模式(Adapter …...

Redis分片备库切换操作

Redis分片备库切换操作 场景描述&#xff1a; 分片集群&#xff1a; 1.ipa:5001-ipa:5002 2.ipb:5001-ipb:5002 需将两个分片备库互置完成灾备 操作步骤 准备工作 主机密码&#xff1a;1qaz!QAZ 获取节点信息命令 /redispath/bin/redis-cli -a password -h ip -p port red…...

二叉树:leetcode1457. 二叉树中的伪回文路径

给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值的排列中&#xff0c;存在一个回文序列。 请你返回从根到叶子节点的所有路径中 伪回文 路径的数目。 给定二叉树的节点数目…...

【【Linux下的Petallinux 以及其他的配置】】

Linux下的Petallinux 以及其他的配置 sudo apt-get install iproute2 gawk python3 python build-essential gcc git make net-tools libncurses5-dev tftpd zlib1g-dev libssl-dev flex bison libselinux1 gnupg wget git-core diffstat chrpath socat xterm autoconf libtoo…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关

在水泥厂的生产流程中&#xff0c;工业自动化网关起着至关重要的作用&#xff0c;尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关&#xff0c;为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多&#xff0c;其中不少设备采用Devicenet协议。Devicen…...

QT开发技术【ffmpeg + QAudioOutput】音乐播放器

一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下&#xff0c;音视频内容犹如璀璨繁星&#xff0c;点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频&#xff0c;到在线课堂中知识渊博的专家授课&#xff0c;再到影视平台上扣人心弦的高清大片&#xff0c;音…...

Qwen系列之Qwen3解读:最强开源模型的细节拆解

文章目录 1.1分钟快览2.模型架构2.1.Dense模型2.2.MoE模型 3.预训练阶段3.1.数据3.2.训练3.3.评估 4.后训练阶段S1: 长链思维冷启动S2: 推理强化学习S3: 思考模式融合S4: 通用强化学习 5.全家桶中的小模型训练评估评估数据集评估细节评估效果弱智评估和民间Arena 分析展望 如果…...