SpringBoot原理(续)

news/2025/2/28 17:53:24/

上篇博客在跟踪SpringBoot自动配置的源码的时候,在自动配置类声明bean的时候,除了在方法上加了一个@Bean注解以外,还会经常用到一个注解,就是以Conditional开头的这一类的注解。以Conditional开头的这些注解都是条件装配的注解。下面来复习条件装配注解。

@Conditional注解:

  • 作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring的IOC容器中。

  • 位置:方法、类

  • @Conditional本身是一个父注解,派生出大量的子注解:

    • @ConditionalOnClass:判断环境中有对应字节码文件,才注册bean到IOC容器。

    • @ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。

    • @ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器。

  • @ConditionalOnClass注解

java">@Configuration
public class HeaderConfig {@Bean@ConditionalOnClass(name="io.jsonwebtoken.Jwts")//环境中存在指定的这个类,才会将该bean加入IOC容器public HeaderParser headerParser(){return new HeaderParser();}//省略其他代码...
}
  • pom.xml

java"><!--JWT令牌-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

测试类

java">@SpringBootTest
public class AutoConfigurationTests {@Autowiredprivate ApplicationContext applicationContext;@Testpublic void testHeaderParser(){System.out.println(applicationContext.getBean(HeaderParser.class));}//省略其他代码...
}

  • @ConditionalOnMissingBean注解

java">@Configuration
public class HeaderConfig {@Bean@ConditionalOnMissingBean //不存在该类型的bean,才会将该bean加入IOC容器public HeaderParser headerParser(){return new HeaderParser();}//省略其他代码...
}

SpringBoot在调用@Bean标识的headerParser()前,IOC容器中是没有HeaderParser类型的bean,所以HeaderParser对象正常创建,并注册到IOC容器中。

  • @ConditionalOnProperty注解(这个注解和配置文件当中配置的属性有关系)

 先在application.yml配置文件中添加如下的键值对:

java">name: itheima

在声明bean的时候就可以指定一个条件@ConditionalOnProperty

java">@Configuration
public class HeaderConfig {@Bean@ConditionalOnProperty(name ="name",havingValue = "itheima")//配置文件中存在指定属性名与值,才会将bean加入IOC容器public HeaderParser headerParser(){return new HeaderParser();}@Beanpublic HeaderGenerator headerGenerator(){return new HeaderGenerator();}
}

最后再过一遍流程:

自动配置的核心就在@SpringBootApplication注解上,SpringBootApplication这个注解底层包含了3个注解,分别是:

  • @SpringBootConfiguration

  • @ComponentScan

  • @EnableAutoConfiguration

@EnableAutoConfiguration这个注解才是自动配置的核心。

  • 它封装了一个@Import注解,Import注解里面指定了一个ImportSelector接口的实现类。

  • 在这个实现类中,重写了ImportSelector接口中的selectImports()方法。

  • 而selectImports()方法中会去读取两份配置文件,并将配置文件中定义的配置类做为selectImports()方法的返回值返回,返回值代表的就是需要将哪些类交给Spring的IOC容器进行管理。

  • 那么所有自动配置类的中声明的bean都会加载到Spring的IOC容器中吗? 其实并不会,因为这些配置类中在声明bean时,通常都会添加@Conditional开头的注解,这个注解就是进行条件装配。而Spring会根据Conditional注解有选择性的进行bean的创建。

  • @Enable 开头的注解底层,它就封装了一个注解 import 注解,它里面指定了一个类,是 ImportSelector 接口的实现类。在实现类当中,我们需要去实现 ImportSelector 接口当中的一个方法 selectImports 这个方法。这个方法的返回值代表的就是我需要将哪些类交给 spring 的 IOC容器进行管理。

  • 此时它会去读取两份配置文件,一份儿是 spring.factories,另外一份儿是 autoConfiguration.imports。而在 autoConfiguration.imports 这份儿文件当中,它就会去配置大量的自动配置的类。

  • 而前面我们也提到过这些所有的自动配置类当中,所有的 bean都会加载到 spring 的 IOC 容器当中吗?其实并不会,因为这些配置类当中,在声明 bean 的时候,通常会加上这么一类@Conditional 开头的注解。这个注解就是进行条件装配。所以SpringBoot非常的智能,它会根据 @Conditional 注解来进行条件装配。只有条件成立,它才会声明这个bean,才会将这个 bean 交给 IOC 容器管理。

 案例:

自定义starter分析

前面复习了SpringBoot中自动配置的原理,下面就通过一个自定义starter案例来加深大家对于自动配置原理的理解。首先介绍一下自定义starter的业务场景,再来分析一下具体的操作步骤。

所谓starter指的就是SpringBoot当中的起步依赖。在SpringBoot当中已经给我们提供了很多的起步依赖了,我们为什么还需要自定义 starter 起步依赖?这是因为在实际的项目开发当中,我们可能会用到很多第三方的技术,并不是所有的第三方的技术官方都给我们提供了与SpringBoot整合的starter起步依赖,但是这些技术又非常的通用,在很多项目组当中都在使用。

业务场景:

  • 前面案例当中所使用的阿里云OSS对象存储服务,现在阿里云的官方并没有给我们提供对应的起步依赖,这个时候使用起来就会比较繁琐,我们需要引入对应的依赖。还需要在配置文件当中进行配置,还需要基于官方SDK示例来改造对应的工具类,我们在项目当中才可以进行使用。

  • 大家想在我们当前项目当中使用了阿里云OSS,我们需要进行这么多步的操作。在别的项目组当中要想使用阿里云OSS,是不是也需要进行这么多步的操作,所以这个时候我们就可以自定义一些公共组件,在这些公共组件当中,我就可以提前把需要配置的bean都提前配置好。将来在项目当中,我要想使用这个技术,我直接将组件对应的坐标直接引入进来,就已经自动配置好了,就可以直接使用了。我们也可以把公共组件提供给别的项目组进行使用,这样就可以大大的简化我们的开发。

在SpringBoot项目中,一般都会将这些公共组件封装为SpringBoot当中的starter,也就是我们所说的起步依赖。

SpringBoot官方starter命名: spring-boot-starter-xxxx

第三组织提供的starter命名: xxxx-spring-boot-starter

 

Mybatis提供了配置类,并且也提供了springboot会自动读取的配置文件。当SpringBoot项目启动时,会读取到spring.factories配置文件中的配置类并加载配置类,生成相关bean对象注册到IOC容器中。

结果:我们可以直接在SpringBoot程序中使用Mybatis自动配置的bean对象。

 在自定义一个起步依赖starter的时候,按照规范需要定义两个模块:

  1. starter模块(进行依赖管理[把程序开发所需要的依赖都定义在starter起步依赖中])

  2. autoconfigure模块(自动配置)

将来在项目当中进行相关功能开发时,只需要引入一个起步依赖就可以了,因为它会将autoconfigure自动配置的依赖给传递下来。

需求:自定义aliyun-oss-spring-boot-starter,完成阿里云OSS操作工具类AliyunOSSUtils的自动配置。

目标:引入起步依赖引入之后,要想使用阿里云OSS,注入AliyunOSSUtils直接使用即可。

 之前的阿里云OSS的使用:

  • 配置文件

java">#配置阿里云OSS参数
aliyun:oss:endpoint: https://oss-cn-shanghai.aliyuncs.comaccessKeyId: LTAI5t9MZK8iq5T2Av5GLDxXaccessKeySecret: C0IrHzKZGKqU8S7YQcevcotD3Zd5TcbucketName: web-framework01
  • AliOSSProperties类

java">@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {//区域private String endpoint;//身份IDprivate String accessKeyId ;//身份密钥private String accessKeySecret ;//存储空间private String bucketName;
}
  • AliOSSUtils工具类

java">@Component //当前类对象由Spring创建和管理
public class AliOSSUtils {@Autowiredprivate AliOSSProperties aliOSSProperties;/*** 实现上传图片到OSS*/public String upload(MultipartFile multipartFile) throws IOException {// 获取上传的文件的输入流InputStream inputStream = multipartFile.getInputStream();// 避免文件覆盖String originalFilename = multipartFile.getOriginalFilename();String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));//上传文件到 OSSOSS ossClient = new OSSClientBuilder().build(aliOSSProperties.getEndpoint(),aliOSSProperties.getAccessKeyId(), aliOSSProperties.getAccessKeySecret());ossClient.putObject(aliOSSProperties.getBucketName(), fileName, inputStream);//文件访问路径String url =aliOSSProperties.getEndpoint().split("//")[0] + "//" + aliOSSProperties.getBucketName() + "." + aliOSSProperties.getEndpoint().split("//")[1] + "/" + fileName;// 关闭ossClientossClient.shutdown();return url;// 把上传到oss的路径返回}
}

当我们在项目当中要使用阿里云OSS,就可以注入AliOSSUtils工具类来进行文件上传。但这种方式其实是比较繁琐的。

需求明确了,接下来我们再来分析一下具体的实现步骤:

  • 第1步:创建自定义starter模块(进行依赖管理)

    • 把阿里云OSS所有的依赖统一管理起来

  • 第2步:创建autoconfigure模块

    • 在starter中引入autoconfigure (我们使用时只需要引入starter起步依赖即可)

  • 第3步:在autoconfigure中完成自动配置

    1. 定义一个自动配置类,在自动配置类中将所要配置的bean都提前配置好

    2. 定义配置文件,把自动配置类的全类名定义在配置文件中

我们分析完自定义阿里云OSS自动配置的操作步骤了,下面我们就按照分析的步骤来实现自定义starter。

1). aliyun-oss-spring-boot-starter模块

创建完starter模块后,删除多余的文件,最终保留内容如下:

删除pom.xml文件中多余的内容后:

<?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/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.aliyun.oss</groupId><artifactId>aliyun-oss-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version><properties><java.version>11</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies></project>

2) aliyun-oss-spring-boot-autoconfigure模块

 创建完starter模块后,删除多余的文件,最终保留内容如下:

删除pom.xml文件中多余的内容后:

<?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/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.aliyun.oss</groupId><artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId><version>0.0.1-SNAPSHOT</version><properties><java.version>11</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies></project>

按照我们之前的分析,是需要在starter模块中来引入autoconfigure这个模块的。打开starter模块中的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/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.aliyun.oss</groupId><artifactId>aliyun-oss-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version><properties><java.version>11</java.version></properties><dependencies><!--引入autoconfigure模块--><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies></project>

前两步已经完成了,接下来是最关键的就是第三步:

  在autoconfigure模块当中来完成自动配置操作。我们将之前案例中所使用的阿里云OSS部分的代码直接拷贝到autoconfigure模块下,然后进行改造就行了。

 拷贝过来后,还缺失一些相关的依赖,需要把相关依赖也拷贝过来:

<?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/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.aliyun.oss</groupId><artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId><version>0.0.1-SNAPSHOT</version><properties><java.version>11</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!--引入web起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--Lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--阿里云OSS--><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.15.1</version></dependency><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version></dependency><dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version></dependency><!-- no more than 2.3.3--><dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId><version>2.3.3</version></dependency></dependencies>
</project>

思考下,在类上添加的@Component注解还有用吗?

 答:没用了。 在SpringBoot项目中,并不会去扫描com.aliyun.oss这个包,不扫描这个包那类上的注解也就失去了作用。@Component注解不需要使用了,可以从类上删除了。删除AliOSSUtils类中的@Component注解、@Autowired注解

下面我们就要定义一个自动配置类了,在自动配置类当中来声明AliOSSUtils的bean对象。

AliOSSAutoConfiguration类:

这里需要用@EnableConfigurationProperties注解,因为AliOSSUtils里需要AlioOSSProperties对象调用get set 方法来为oss相关属性赋值。这个注解可以作用在类上或方法上且底层就是@import注解,前面复习到,我们在声明第三方bean时需要注入哪个对象,直接在方法形参中声明即可。

java">@Configuration//当前类为Spring配置类
@EnableConfigurationProperties(AliOSSProperties.class)//导入AliOSSProperties类,并交给SpringIOC管理
public class AliOSSAutoConfiguration {//创建AliOSSUtils对象,并交给SpringIOC容器@Beanpublic AliOSSUtils aliOSSUtils(AliOSSProperties aliOSSProperties){AliOSSUtils aliOSSUtils = new AliOSSUtils();aliOSSUtils.setAliOSSProperties(aliOSSProperties);return aliOSSUtils;}
}

 AliOSSProperties类:

java">/*阿里云OSS相关配置*/
@Data
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {//区域private String endpoint;//身份IDprivate String accessKeyId ;//身份密钥private String accessKeySecret ;//存储空间private String bucketName;
}

AliOSSUtils类:

java">@Data 
public class AliOSSUtils {private AliOSSProperties aliOSSProperties;/*** 实现上传图片到OSS*/public String upload(MultipartFile multipartFile) throws IOException {// 获取上传的文件的输入流InputStream inputStream = multipartFile.getInputStream();// 避免文件覆盖String originalFilename = multipartFile.getOriginalFilename();String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));//上传文件到 OSSOSS ossClient = new OSSClientBuilder().build(aliOSSProperties.getEndpoint(),aliOSSProperties.getAccessKeyId(), aliOSSProperties.getAccessKeySecret());ossClient.putObject(aliOSSProperties.getBucketName(), fileName, inputStream);//文件访问路径String url =aliOSSProperties.getEndpoint().split("//")[0] + "//" + aliOSSProperties.getBucketName() + "." + aliOSSProperties.getEndpoint().split("//")[1] + "/" + fileName;// 关闭ossClientossClient.shutdown();return url;// 把上传到oss的路径返回}
}

在aliyun-oss-spring-boot-autoconfigure模块中的resources下,新建自动配置文件:

  • META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
java">com.aliyun.oss.AliOSSAutoConfiguration

测试: 

阿里云OSS的starter已经定义好了,接下来做一个测试。

 

1.在test工程中引入阿里云starter依赖

  • 通过依赖传递,会把autoconfigure依赖也引入了

<!--引入阿里云OSS起步依赖-->
<dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-oss-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>

2.在test工程中的application.yml文件中,配置阿里云OSS配置参数信息(从以前的工程中拷贝即可

#配置阿里云OSS参数
aliyun:oss:endpoint: https://oss-cn-shanghai.aliyuncs.comaccessKeyId: LTAI5t9MZK8iq5T2Av5GLDxXaccessKeySecret: C0IrHzKZGKqU8S7YQcevcotD3Zd5TcbucketName: web-framework01

3.在test工程中的UploadController类编写代码

java">@RestController
public class UploadController {@Autowiredprivate AliOSSUtils aliOSSUtils;@PostMapping("/upload")public String upload(MultipartFile image) throws Exception {//上传文件到阿里云 OSSString url = aliOSSUtils.upload(image);return url;}}

编写完代码后,启动当前的SpringBoot测试工程:

  • 随着SpringBoot项目启动,自动配置会把AliOSSUtils的bean对象装配到IOC容器中

用postman工具进行文件上传:

通过断点可以看到自动注入AliOSSUtils的bean对象:

最后进行一下Web后端开发总结:

  web后端开发现在基本上都是基于标准的三层架构进行开发的,在三层架构当中,Controller控制器层负责接收请求响应数据,Service业务层负责具体的业务逻辑处理,而Dao数据访问层也叫持久层,就是用来处理数据访问操作的,来完成数据库当中数据的增删改查操作。

 在三层架构当中,前端发起请求首先会到达Controller(不进行逻辑处理),然后Controller会直接调用Service 进行逻辑处理, Service再调用Dao完成数据访问操作。

 如果我们在执行具体的业务处理之前,需要去做一些通用的业务处理,比如:要进行统一的登录校验,统一的字符编码等这些操作时,我们就可以借助于Javaweb当中三大组件之一的过滤器Filter或者是Spring当中提供的拦截器Interceptor来实现。


 而为了实现三层架构层与层之间的解耦,我们学习了Spring框架当中的第一大核心:IOC控制反转与DI依赖注入。

 所谓控制反转,指的是将对象创建的控制权由应用程序自身交给外部容器,这个容器就是我们常说的IOC容器或Spring容器。

而DI依赖注入指的是容器为程序提供运行时所需要的资源

除了IOC与DI还有AOP面向切面编程,Spring中的事务管理、全局异常处理器,传递会话技术Cookie、Session、新的会话跟踪解决方案JWT令牌,阿里云OSS对象存储服务,以及通过Mybatis持久层架构操作数据库等技术。

我们在学习这些web后端开发技术的时候,都是基于主流的SpringBoot进行整合使用的。而SpringBoot又是用来简化开发,提高开发效率的。像过滤器、拦截器、IOC、DI、AOP、事务管理等这些技术到底是哪个框架提供的核心功能?

Filter过滤器、Cookie、 Session这些都是传统的JavaWeb提供的技术。

JWT令牌、阿里云OSS对象存储服务,是现在企业项目中常见的一些解决方案。

IOC控制反转、DI依赖注入、AOP面向切面编程、事务管理、全局异常处理、拦截器等,这些技术都是 Spring Framework框架当中提供的核心功能。

Mybatis就是一个持久层的框架,是用来操作数据库的。

 在Spring框架的生态中,对web程序开发提供了很好的支持,如:全局异常处理器、拦截器这些都是Spring框架中web开发模块所提供的功能,而Spring框架的web开发模块,我们也称为:SpringMVC

 SpringMVC不是一个单独的框架,它是Spring框架的一部分,是Spring框架中的web开发模块,是用来简化原始的Servlet程序开发的。

 外界俗称的SSM,就是由:SpringMVC、Spring Framework、Mybatis三块组成。

基于传统的SSM框架进行整合开发项目会比较繁琐,而且效率也比较低,所以在现在的企业项目开发当中,基本上都是直接基于SpringBoot整合SSM进行项目开发的。


http://www.ppmy.cn/news/1539894.html

相关文章

详解Oracle审计(二)

题记&#xff1a; 本文将承接上篇详细介绍oracle的审计功能&#xff0c;基于11g版本&#xff0c;但对12c&#xff0c;19c也同样适用。 1. 语句审计实操演示实例 sqlplus / as sysdba show parameter audit_trail alter system set audit_traildb_extended scopespfile; star…

在Linux中搭建WordPress并实现Windows主机远程访问

WordPreWordPress是一个基于PHP开发的开源平台&#xff0c;适用于在支持PHP与MySQL数据库的服务器上搭建个性化博客或网站。同时&#xff0c;它也能够作为功能强大的内容管理系统&#xff08;CMS&#xff09;被广泛应用。 虚拟机&#xff1a;VirtualBox 虚拟机安装&#x1f449…

如何在Android平板上使用谷歌浏览器进行网页缩放

在使用Android平板时&#xff0c;我们经常会浏览各种网页&#xff0c;但有时网页内容可能无法适应屏幕大小&#xff0c;这时就需要用到网页缩放功能。本文将为您详细介绍如何在Android平 板上的谷歌浏览器中进行网页缩放&#xff0c;帮助您更好地浏览网页。&#xff08;本文由h…

状态机与行为树的实现;Behavior Designer的使用与自写状态机的几种方法;

以下部分内容将会涉及插件BehaviorDesigner 代码仅为演示所需&#xff0c;并非实际实现代码&#xff0c;非本人所使用代码&#xff1b; 前往个人博客&#xff0c;获取更好的阅读体验 状态机与行为树 为何写&#xff1f; 笔者在初学的时候并不写状态机&#xff0c;而是写到一…

Unity-Shader-语义

1、从程序传给顶点函数&#xff1a; POSITION&#xff1a;顶点坐标&#xff08;模型空间下&#xff09; NORMAL&#xff1a;法线向量&#xff08;模型空间下&#xff09; TANGENT&#xff1a;切线向量&#xff08;模型空间下&#xff09; TEXCOORD0~n&#xff1a;纹理坐标 …

设计模式概览

设计模式是一种解决常见编程问题的经验总结&#xff0c;提供了代码的可重用性、可扩展性和可维护性。常见的设计模式有23个&#xff0c;主要分为三大类&#xff1a;创建型模式、结构型模式和行为型模式。下面是这三类设计模式的详细分类和讲解&#xff1a; 一、创建型模式 创建…

MFC工控项目实例二十四模拟量校正值输入

承接专栏《MFC工控项目实例二十三模拟量输入设置界面》 对模拟量输入的零点校正值及满量程对应的电压值进行输入。 1、在SenSet.h文件中添加代码 #include "BtnST.h" #include "ShadeButtonST.h"/ // SenSet dialogclass SenSet : public CDialog { // Co…

抖音大模型面试经历分享

我主要从事自然语言处理&#xff08;NLP&#xff09;工作&#xff0c;同时也涉及多模态和强化学习。当前大环境不太好&#xff0c;可投递的公司并不多&#xff0c;像腾讯主要招聘高级别岗位&#xff0c;所以我没有投递腾讯。 抖音一面 面试官首先与我聊了项目。 接着询问了 A…