面试题:SpringBoot 如何自定义自己的条件注解与自动配置?

文章目录

  • 前言
  • 自定义条件注解
  • 自定义自动配置


前言

Spring Boot的核心功能就是为整合第三方框架提供自动配置,而本文则带着大家实现了自己的自动配置和Starter,一旦真正掌握了本文的内容,就会对Spring Boot产生“一览众山小”的感觉。


自定义条件注解

在SpringBoot中,所有自定义条件注解其实都是基于@Conditional而来的,使用@Conditional定义新条件注解关键就是要有一个Condition实现类,该Condition实现类就负责条件注解的处理逻辑,该实现类所实现的matches()方法决定了条件注解的要求是否得到满足。

下面是自定义条件注解的Condition实现类的代码。

src/main/java/com/example/_003configtest/condition/MyCondition.java

package com.example._003configtest.condition;import com.example._003configtest.annotation.ConditionalCustom;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;import java.util.Map;public class MyCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {//获取@ConditionalCustom注解的全部属性,其中ConditionalCustom是自定义的注解Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalCustom.class.getName());//获取注解的value属性值String[] vals = (String[]) annotationAttributes.get("value");//env是application.properties或application.yml中配置的属性Environment env = context.getEnvironment();//遍历每个value的每个属性值for (String val : vals) {//如果某个属性值对应的配置属性不存在,则返回falseif(env.getProperty(val.toString())== null){return false;}}return true;}
}

从上面的逻辑可以看到,自定义条件注解的处理逻辑比较简单:就是要求value属性所指定的所有配置属性必须存在,至于这些配置属性的值是什么无所谓,这些配置属性是否有值也无所谓。

有了上面的Condition实现类之后,接下来即可基于@Conditional来定义自定义条件注解。下面是自定义条件注解的代码。

src/main/java/com/example/_003configtest/annotation/ConditionalCustom.java

package com.example._003configtest.annotation;
import com.example._003configtest.condition.MyCondition;
import org.springframework.context.annotation.Conditional;import java.lang.annotation.*;@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//只要通过@Conditional指定Condition实现类即可,该Condition实现类就会负责该条件注解的判断逻辑
@Conditional(MyCondition.class)
public @interface ConditionalCustom {String[] value() default {};}

下面的配置类示范了如何使用该自定义的条件注解:

src/main/java/com/example/_003configtest/config/MyConfigTest.java

// proxyBeanMethods = true  :单例模式,保证每个@Bean方法被调用多少次返回的组件都是同一个
// proxyBeanMethods = false :原型模式,每个@Bean方法被调用多少次返回的组件都是新创建的
@Configuration(proxyBeanMethods = true)
public class MyConfigTest {@Bean//只有当applicaion.properties或application.yml中org.test1,org.test2两个配置属性都存在时才生效@ConditionalCustom({"org.test1","org.test2"})public MyBean myBean(){return new MyBean();}
}

在application.properties文件中添加如下配置:

org.test1 = 1
org.test2 = 2

运行测试发现成功获得了容器中对应的类:

图片

自定义自动配置

开发自己的自动配置很简单,其实也就两步:

  • 使用@Configuration和条件注解定义自动配置类。

  • 在META-INF/spring.factories文件中注册自动配置类。

为了清楚地演示Spring Boot自动配置的效果,避免引入第三方框架导致的额外复杂度,本例先自行开发一个funny框架,该框架的功能是用文件或数据库保存程序的输出信息。

新建一个Maven项目funny(注意不是用SpringInitializr创建项目),为该项目添加mysql-connector-java和slf4j-api两个依赖。由于该项目是我们自己开发的框架,因此无须为该项目添加任何Spring Boot依赖。

下面是该项目的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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>funny</artifactId><version>1.0-SNAPSHOT</version><!-- 定义所使用的Java版本和源代码使用的字符集--><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- MySQL数据库驱动依赖 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency><!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.36</version></dependency></dependencies></project>

接下来为这个框架项目开发如下类。

src/main/java/io/WriterTemplate.java

package io;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;
import javax.sql.DataSource;public class WriterTemplate {Logger log = LoggerFactory.getLogger(this.getClass());private final DataSource dataSource;private Connection conn;private File dest;private final Charset charset;private RandomAccessFile raf;public WriterTemplate(DataSource dataSource) throws SQLException {this.dataSource = dataSource;this.dest = null;this.charset = null;if(Objects.nonNull(this.dataSource)){log.debug("========获取数据库连接========");this.conn = dataSource.getConnection();}}public WriterTemplate(File dest,Charset charset) throws FileNotFoundException{this.dest = dest;this.charset = charset;this.dataSource = null;this.raf = new RandomAccessFile(this.dest,"rw");}public void write(String message) throws IOException,SQLException{if(Objects.nonNull(this.conn)){//查询当前数据库的fnny_message表是否存在ResultSet rs = conn.getMetaData().getTables(conn.getCatalog(),null,"funny_message",null);//如果funy_message表不存在,需要创建表if(!rs.next()){log.debug("~~~~~~~~~创建funny_message表~~~~~~~~~");conn.createStatement().execute("create table funny_message " + "(id int primary key auto_increment,message_text text)");}log.debug("~~~~~~~~~输出到数据表~~~~~~~~~");//往数据库中插入数据conn.createStatement().executeUpdate("insert into " + "funny_message values(null,'" + message + "')");rs.close();}else{log.debug("~~~~~~~~~输出到文件~~~~~~~~~");raf.seek(this.dest.length());raf.write((message + "\n").getBytes(this.charset));}}//关闭资源public void close() throws SQLException,IOException{if(this.conn != null){this.conn.close();}if(this.raf != null){this.raf.close();}}
}

该工具类根据是否传入 DataSource 来决定输出目标:如果为该工具类传入了DataSource,它就会向该数据源所连接的数据库中的funny_message表输出内容(如果该表不存在,该工具类将会自动建表);如果没有为该工具类传入DataSource,它就会向指定文件输出内容。

接下来使用install打包到maven仓库:

图片

有了该框架之后,接下来为该框架开发自动配置。如果为整合现有的第三方框架开发自动配置,则可直接从这一步开始(因为框架已经存在了,直接为框架开发自动配置即可)。

同样新建一个Maven项目funny-spring-boot-starter(为了方便可以用SpringInitializr创建项目),这个项目是自定义Starter项目,因此必须要有Spring Boot支持,将前面Spring Boot项目中的pom.xml文件复制过来,保留其中的spring-boot-starter依赖,并添加刚刚开发的funny框架的依赖。

此外,由于该项目不是Spring Boot应用,因此不需要主类,也不需要运行,故删除其中的spring-boot-maven-plugin插件。修改后的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><groupId>com.example</groupId><artifactId>funny-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version><name>funny-spring-boot-starter</name><description>funny-spring-boot-starter</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version></properties><dependencies><!-- Spring Boot Starter依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- 依赖自定义的funny框架,如果正在为其他第三方框架开发自动配置,则此处应该填写被整合的第三方框架的坐标。--><dependency><groupId>org.example</groupId><artifactId>funny</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement></project>

接下来定义如下自动配置类。

src/main/java/com/example/funnyspringbootstarter/autoconfig/FunnyAutoConfiguration.java

package com.example.funnyspringbootstarter.autoconfig;import io.WriterTemplate;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.charset.Charset;
import java.sql.SQLException;@Configuration
//当WriteTemplate类存在时配置生效
@ConditionalOnClass(WriterTemplate.class)
//FunnyProperties是自定义的类,后面会定义,这里表示启动FunnyProperties
@EnableConfigurationProperties(FunnyProperties.class)
//让该自动配置类位于DataSourceAutoConfiguration自动配置类之后处理
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class FunnyAutoConfiguration {private final FunnyProperties properties;//FunnyProperties类负责加载配置属性public FunnyAutoConfiguration(FunnyProperties properties) {this.properties = properties;}@Bean(destroyMethod = "close")//当单例的DataSource Bean存在时配置生效@ConditionalOnSingleCandidate(DataSource.class)//只有当容器中没有WriterTemplate Bean时,该配置才会生效@ConditionalOnMissingBean//通过@AutoConfigureOrder注解指定该配置方法比下一个配置WriterTemplate的方法的优先级更高@AutoConfigureOrder(99)public WriterTemplate writerTemplate(DataSource dataSource) throws SQLException{return new WriterTemplate(dataSource);}@Bean(destroyMethod = "close")//只有当前面的WriteTemplate配置没有生效时,该方法的配置才会生效@ConditionalOnMissingBean@AutoConfigureOrder(199)public WriterTemplate writerTemplate2() throws FileNotFoundException{File f = new File(this.properties.getDest());Charset charset = Charset.forName(this.properties.getCharset());return new WriterTemplate(f,charset);}
}

在FunnyAutoConfiguration 自动配置类中定义了两个@Bean方法,这两个@Bean 方法都用于自动配置 WriterTemplate。为了指定它们的优先级,程序使用了@AutoConfigureOrder 注解修饰它们,该注解指定的数值越小,优先级越高。

FunnyAutoConfiguration 自动配置类中的@Bean 方法同样使用了@ConditionalOnMissingBean、@ConditionalOnSingleCandidate等条件注解修饰,从而保证只有当容器中不存在WriterTemplate时,该自动配置类才会配置WriterTemplate Bean,且优先配置基于DataSource的WriterTemplate。

上面的自动配置类还用到了FunnyProperties属性处理类,该类的代码如下:

package com.example.funnyspringbootstarter.autoconfig;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = FunnyProperties.FUNNY_PREFIX)
public class FunnyProperties {public static final String FUNNY_PREFIX = "org.test";private String dest;private String charset;public String getDest() {return dest;}public void setDest(String dest) {this.dest = dest;}public String getCharset() {return charset;}public void setCharset(String charset) {this.charset = charset;}
}

上面的属性处理类负责处理以“org.test”开头的属性,这个“org.test”是必要的,它相当于这一组配置属性的“命名空间”,通过这个命名空间可以将这些配置属性与其他框架的配置属性区分开。

有了上面的自动配置类之后,接下来使用如下META-INF/spring.factories文件来注册自动配置类。

src/main/resources/META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration = \com.example.funnyspringbootstarter.autoconfig.FunnyAutoConfiguration

经过上面步骤,自动配置开发完成,接下来使用install打包到maven仓库:

图片

有了自定义的Starter之后,接下来使用该Starter与使用Spring Boot官方Starter并没有任何区别。

首先新建一个Maven项目myfunnytest,在pom文件中引入该starter:

<?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><groupId>com.example</groupId><artifactId>myfunnytest</artifactId><version>0.0.1-SNAPSHOT</version><name>myfunnytest</name><description>myfunnytest</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version></properties><dependencies><dependency><groupId>com.example</groupId><artifactId>funny-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version></dependency><!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter</artifactId>-->
<!--        </dependency>--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.7.RELEASE</version><configuration><mainClass>com.example.myfunnytest.MyfunnytestApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

由于 funny-spring-boot-starter 本身需要依赖 spring-boot-starter,因此不再需要显式配置依赖spring-boot-starter。

在添加了上面的funny-spring-boot-starter依赖之后,该Starter包含的自动配置生效,它会尝试在容器中自动配置WriterTemplate,并且还会读取application.properties因此还需要在application.properties文件中进行配置。

src/main/resources/application.properties

# 应用名称
spring.application.name=myfunnytest
org.test.dest = F:/abc-12345.txt
org.test.charset=UTF-8
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDateTimeCode=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root

该示例的主类很简单,它直接获取容器中的WriterTemplate Bean,并调用该Bean的write()方法执行输出。下面是该主类的代码:

package com.example.myfunnytest;import io.WriterTemplate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication
public class MyfunnytestApplication {public static void main(String[] args) throws Exception{ConfigurableApplicationContext run = SpringApplication.run(MyfunnytestApplication.class, args);WriterTemplate writerTemplate = run.getBean(WriterTemplate.class);System.out.println(writerTemplate);writerTemplate.write("自动配置");}}

运行该程序,由于当前Spring容器中没有DataSource Bean,因此FunnyAutoConfiguration将会自动配置输出到文件的WriterTemplate。因此,运行该程序,可以看到程序向“f:/abc-12345.txt”文件(由前面的org.test.dest属性配置)输出内容:

图片

运行结果如下:

图片

如果在项目的pom.xml文件中通过如下配置来添加依赖。

<!-- Spring Boot JDBC Starter依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

此时为项目添加了spring-boot-starter-jdbc依赖,该依赖将会在容器中自动配置一个DataSource Bean,这个自动配置的DataSource Bean将导致FunnyAutoConfiguration会自动配置输出到数据库的WriterTemplate。因此,运行该程序,可以看到程序向数据库名为springboot数据库的funny_message表输出内容:

图片

Spring Boot的核心功能就是为整合第三方框架提供自动配置,而本文则带着大家实现了自己的自动配置和Starter,一旦真正掌握了本文的内容,就会对Spring Boot产生“一览众山小”的感觉。

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

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

相关文章

ArcGIS Maps SDK for JS:关闭地图边框(v4.27)

1 问题描述 近期&#xff0c;将ArcGIS Api for JS v4.16更新到了ArcGIS Maps SDK for JS v4.27&#xff0c;原本去除地图的css代码失效了。v4.27需要用.esri-view-surface--touch-none::after控制边框属性。 下面为没有关闭地图边框的效果图。&#xff08;亮色版地图为黑色边…

LeetCode-42. 接雨水【栈 数组 双指针 动态规划 单调栈】

LeetCode-42. 接雨水【栈 数组 双指针 动态规划 单调栈】 题目描述&#xff1a;解题思路一&#xff1a;单调栈&#xff0c;维护一个单调递减栈。每当遇到当前元素大于栈顶元素就出栈&#xff0c;在出栈时更新答案。当遇到出栈的情况&#xff0c;若单调栈栈左边有一个元素则必有…

基于深度学习的Python+OpenCV的甲骨文相似度评估系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介1. 数据获取2. 深度学习模型训练3. 图像相似度评估4. 结果展示 二、功能三、系统四. 总结 一项目简介 基于深度学习的PythonOpenCV的甲骨文相似度评估系统&…

Vue前端与后端放在一起的搭建方式

1.首先把后端项目搭建好 去到项目的存放位置 2.然后cmd黑窗口输入命令创建vue项目 3.创建成功后回到后端项目进行合并 3.1在File处选择Project Structure 3.2选择模块 3.3找到自己的vue项目 3.4疯狂next最后create 3.5选择Apply并确定OK&#xff0c;恭喜您创建成功了 二、启动…

C# URL参数编码

string s "lw123abc测试信息&#xff01;#&#xffe5;%……&*&#xff08;&#xff09;——"; Console.WriteLine("原数据:\t\t" s); String s2 Uri.EscapeDataString(s);//Uri.EscapeDataString() 编码 Console.WriteLine("Hexdata:\t&qu…

一文读懂Java中的设计模式——适配器模式,对于兼容不同系统特别适用!

适配器模式概念 适配器&#xff08;变压器&#xff09;模式&#xff1a;这种模式的核心是当你想实现一个接口但又不想实现所有接口方法&#xff0c;只想去实现一部分方法时&#xff0c;就用默认的适配器模式&#xff0c;他的方法是在接口和具体实现类中添加一个抽象类&#xf…

升级Doris集群——2.0.1.1到2.0.3版本

之前安装的Doris版本官方说不太稳定&#xff0c;所以我们可以对其进行升级 1.关闭所有节点 2.下载2.0.3版本的Doris安装包&#xff0c;将旧版本的bin和lib目录替换为 对应新版本bin和lib目录&#xff0c;按照非master节点到master节点&#xff0c;be—broker—fe的顺序进行替…

Intewell-Hyper I_V2.0.0_release版本正式发布

新型工业操作系统_Intewell-Hyper I_V2.0.0_release版本正式发布 软件发布版本信息 版本号&#xff1a;V2.0.0 版本发布类型&#xff1a;release正式版本 版本特点 1.建立Intewell-Hyper I基线版本 版本或修改说明 基于Intewell-Lin V2.3.0_release版本&#xff1a; 1.Devel…

在 Windows PC 上轻松下载并安装 FFmpeg

FFmpeg 是一种开源媒体工具&#xff0c;可用于将任何视频格式转换为您需要的格式。该工具只是命令行&#xff0c;因此它没有图形、可点击的界面。如果您习惯使用常规图形 Windows 程序&#xff0c;安装 FFmpeg 一开始可能看起来很复杂&#xff0c;但不用担心&#xff0c;它;很简…

淘宝、抖音、视频号直播电商“赛马”

经过多年发展&#xff0c;货架电商和社交电商在当下已是一片红海&#xff0c;因此&#xff0c;淘宝、京东、拼多多API接口等电商巨头为寻求新的增长&#xff0c;纷纷另辟蹊径&#xff0c;开始在异域探索。而由于用户如今的购物需求已经发生了天翻地覆的变化&#xff0c;于是&am…

中国社会科学院大学-新加坡新跃社科大学教育项目招生简章

一、项目简介 全球经济正在经历由科技进步和创新、政治和人口剧烈变化所带来的巨大的不确定性和挑战。面对日趋复杂的外部竞争环境&#xff0c;企业的领导者和管理者需要具备卓越的战略思维和全球洞察力、以科学的精神和严谨务实的态度引领企业创新发展。 为此&#xff0c;中…

微信小程序自定义组件---behaviors

1.什么是behaviors behaviors是小程序中&#xff0c;用于实现组件间代码共享的特性&#xff0c;类似于vue中的mixins 2.behaviors的工作方式 每个behavior可以包含一组属性、数据、生命周期函数和方法。组件引用它时&#xff0c;它的属性、数据和方法会被合并到组件中。每个组…

数字孪生项目的开发平台

WEBGL 开发数字孪生项目的流程涵盖了需求分析、场景搭建、模型创建、数据接入、动画与交互、性能优化、测试与部署以及维护与升级等方面。WEBGL 开发数字孪生项目的流程可以分为以下几个步骤&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外…

UniGui使用CSS移动端按钮标题垂直

unigui移动端中按钮拉窄以后&#xff0c;标题无法垂直居中&#xff0c;是因为标题有一个padding属性&#xff0c;在四周撑开一段距离。会变成这样&#xff1a; 解决方法&#xff0c;用css修改padding&#xff0c;具体做法如下 首先给button的cls创建一个cls,例如 然后添加css&…

网络协议 - HTTP 协议详解

网络协议 - HTTP 协议详解 一 、基础概念URL请求和响应报文1. 请求报文2. 响应报文 二、HTTP 方法GETHEADPOSTPUTPATCHDELETEOPTIONSCONNECTTRACE 三、HTTP 状态码1XX 信息2XX 成功3XX 重定向4XX 客户端错误5XX 服务器错误 四、HTTP 首部通用首部字段请求首部字段响应首部字段实…

Maven环境搭建及配置

Maven环境搭建及配置 1.下载部署 官方网站下载正式版的Maven文件,打开bin目录&#xff0c;复制路径然后去环境变量中的path下配置环境变量&#xff0c; 如果只有一个用户只需要在上面path配置复制的路径,当然也可以直接在下面配置,下面配置默认给所有用户都配置 设置完成打开控…

【Unity】简单实现生成式电子围栏

【Unity】简单实现生成式电子围栏 三维电子围栏是一种通过使用三维技术和电子设备来建立虚拟围栏&#xff0c;用于监控和控制特定区域的系统。它可以通过使用传感器和摄像头来检测任何越界行为&#xff0c;并及时发出警报。这种技术可以应用于安防领域以及其他需要对特定区域进…

Leetcode—2413.最小偶倍数【简单】

2023每日刷题&#xff08;六十&#xff09; Leetcode—2413.最小偶倍数 class Solution { public:int smallestEvenMultiple(int n) {return (n % 2 1) * n;} };运行结果 之后我会持续更新&#xff0c;如果喜欢我的文章&#xff0c;请记得一键三连哦&#xff0c;点赞关注收藏…

【构建工具】vite2没捂热,vite5又来了,性能大幅提升!

vite2还没焐热&#xff0c;vite5又来了&#xff01;&#xff01;&#xff01; 就在一周前vite5重磅发布了&#xff01;性能大幅提升! 请看下面&#xff1a;下面是翻译过来的&#xff0c;原文&#xff1a;Vite 5.0 发布&#xff01; |维特 (vitejs.dev) Vite 4 大约在一年前发布…

MODBUS协议

一、概念 modbus是一个公开免费的协议&#xff0c;广泛应用于工业控制领域&#xff08;PLC和仪器&#xff0c;PLC和PLC&#xff0c;PLC和上位机&#xff0c;PLC和触摸屏等等&#xff0c;其中PLC是可控制逻辑单元&#xff09; 他有两种物理接口&#xff08;硬件协议&#xff09;…