SpringBoot相比于Spring的优点(自动配置和依赖管理)

自动配置

例子见真章

我们先看一下我们Spring整合Druid的过程,以及我们使用SpringBoot整合Druid的过程我们就知道我们SpringBoot的好处了。

Spring方式

Spring方式分为两种,第一种就是我们使用xml进行整合,第二种就是使用我们注解进行简化

xml方式

(1)导入坐标

 <!--引入druid数据源--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.6</version></dependency

 (2)书写jdbc.properties文件和 applicationContext.xml配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db1
jdbc.username=root
jdbc.password=1998hjy
< context :property-placeholder location ="classpath:jdbc.properties" />
< bean id ="dataSource" class ="com.mchange.v2.c3p0.ComboPooledDataSource" >
< property name ="driverClass" value ="${jdbc.driver}" />
< property name ="jdbcUrl" value ="${jdbc.url}" />
< property name ="user" value ="${jdbc.username}" />
< property name ="password" value ="${jdbc.password}" />
</ bean >

 这样我们的Druid就成功加载到我们的容器中了。可以看到不管是xml方式还是注解方式我们都需要自己写将dataSource放在IOC容器的过程。

注解方式

(1)导入坐标

(2)书写配置类

@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;@Bean("dataSource")public DataSource getDataSource() throws PropertyVetoException {ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setDriverClass(driver);dataSource.setJdbcUrl(url);dataSource.setUser(username);dataSource.setPassword(password);return dataSource;}}

 (3)启动类代替xml

@Import({DataSourceConfiguration.class})//注意这是一个数组如果还有别的配置文件直接逗号相连就可以
//标志该类是和核心配置文件
@Configurable
//<context:component-scan base-pakeage="com.itheima"
@ComponentScan("com.itheima") //扫描包public class SpringConfiguration {
//这就是基于注解开发,我们不在需要applicationContext.xml文件了。里面的Bean使用注解代替//配置文件使用配置类来代表 里面的关于数据库的配置文件(我们没有自定义实体类) 我们需要自定义实体类进行接收
}

 SpringBoot方式

使用官方starter(引入此 Starter 就不需要引用 Druid 依赖包了,因为已经包含 Druid 的依赖包了):

(1)导入起步依赖

 <!--引入druid数据源--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.6</version></dependency>

(2)配置yml

spring:datasource:druid:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/springboot_test?characterEncoding=utf8&serverTimezone=UTCusername: rootpassword: root

 这样就结束了

自定义整合方式

如果我们使用SpringBoot导入的依赖不是起步依赖,那么我们就退化成Spring使用注解方式整合Druid了如下

(1)导入坐标

   <dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.2.8</version>
</dependency>

(2)注册bean到ioc容器

        

@Configuration
public class MyDataSourceConfig {

    /**
     * 配置 Druid 数据源。(Spring Boot 会在 IoC 容器自动读取类型为 DataSource 的对象。故这个 bean 注入后,即与Spring Boot整合好了)
     * @return DruidDataSource 数据源
     * @throws SQLException
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource",ignoreUnknownFields = false) // 读取配置文件中的数据源信息。Druid会以此建立数据库连接
    public DataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setFilters("stat"); // 开启
        return druidDataSource;
    }

}

 (3)配置yml

server:
  port: 80
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost/abc
    username: root
    password: 12345678

可以看到如果我们不使用starter方式,那么我们就还要想spring一样要注册bean,那么我们springBoot中为什么使用start这种起步依赖就可以不用我们自己注册bean了呢,这就导出了我们今天的主题,自动配置

什么是 SpringBoot 自动装配?

 参考知乎 Spring Boot 自动装配原理与实现 - 知乎 (zhihu.com)

我们现在提到自动装配的时候,一般会和 Spring Boot 联系在一起。但是,实际上 Spring Framework 早就实现了这个功能。Spring Boot 只是在其基础上,通过 SPI 的方式,做了进一步优化。

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的 META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。

没有 Spring Boot 的情况下,如果我们需要引入第三方依赖,需要手动配置,非常麻烦。但是,Spring Boot 中,我们直接引入一个 starter 即可。比如你想要在项目中使用 redis 的话,直接在项目中引入对应的 starter 即可。

引入 starter 之后,我们通过少量注解和一些简单的配置就能使用第三方组件提供的功能了。

在我看来,自动装配可以简单理解为:通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能。

SpringBoot 是如何实现自动装配

我们先看一下 SpringBoot 的核心注解 SpringBootApplication 。

大概可以把 @SpringBootApplication看作是 @Configuration@EnableAutoConfiguration@ComponentScan 注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:

  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
  • @Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
  • @ComponentScan:扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如下图所示,容器中将排除TypeExcludeFilterAutoConfigurationExcludeFilter

@EnableAutoConfiguration 是实现自动装配的重要注解,我们以这个注解入手。

@EnableAutoConfiguration:实现自动装配的核心注解

EnableAutoConfiguration 只是一个简单地注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector类。

我们现在重点分析下AutoConfigurationImportSelector 类到底做了什么?

AutoConfigurationImportSelector:加载自动装配类

AutoConfigurationImportSelector类的继承体系如下:

可以看出,AutoConfigurationImportSelector 类实现了 ImportSelector接口,也就实现了这个接口中的 selectImports方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中

private static final String[] NO_IMPORTS = new String[0];public String[] selectImports(AnnotationMetadata annotationMetadata) {// <1>.判断自动装配开关是否打开if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {//<2>.获取所有需要装配的beanAutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}

这里我们需要重点关注一下getAutoConfigurationEntry()方法,这个方法主要负责加载自动配置类的。

该方法调用链如下:

现在我们结合getAutoConfigurationEntry()的源码来详细分析一下:

private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {//<1>.if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {//<2>.AnnotationAttributes attributes = this.getAttributes(annotationMetadata);//<3>.List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);//<4>.configurations = this.removeDuplicates(configurations);Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = this.filter(configurations, autoConfigurationMetadata);this.fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);}}

第 1 步:

判断自动装配开关是否打开。默认spring.boot.enableautoconfiguration=true,可在 application.properties 或 application.yml 中设置

第 2 步 :

用于获取EnableAutoConfiguration注解中的 exclude 和 excludeName

第 3 步

获取需要自动装配的所有配置类,读取META-INF/spring.factories

spring-boot/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

从下图可以看到这个文件的配置内容都被我们读取到了。XXXAutoConfiguration的作用就是按需加载组件。

不光是这个依赖下的META-INF/spring.factories被读取到,所有 Spring Boot Starter 下的META-INF/spring.factories都会被读取到。

所以,你可以清楚滴看到, druid 数据库连接池的 Spring Boot Starter 就创建了META-INF/spring.factories文件。

如果,我们自己要创建一个 Spring Boot Starter,这一步是必不可少的。

第 4 步 :

到这里可能面试官会问你:“spring.factories中这么多配置,每次启动都要全部加载么?”。

很明显,这是不现实的。我们 debug 到后面你会发现,configurations 的值变小了。

因为,这一步有经历了一遍筛选,@ConditionalOnXXX 中的所有条件都满足,该类才会生效。

 

@Configuration
// 检查相关的类:RabbitTemplate 和 Channel是否存在
// 存在才会加载
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
}

Spring Boot 提供的条件注解

  • @ConditionalOnBean:当容器里有指定 Bean 的条件下
  • @ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下
  • @ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean
  • @ConditionalOnClass:当类路径下有指定类的条件下
  • @ConditionalOnMissingClass:当类路径下没有指定类的条件下
  • @ConditionalOnProperty:指定的属性是否有指定的值
  • @ConditionalOnResource:类路径是否有指定的值
  • @ConditionalOnExpression:基于 SpEL 表达式作为判断条件
  • @ConditionalOnJava:基于 Java 版本作为判断条件
  • @ConditionalOnJndi:在 JNDI 存在的条件下差在指定的位置
  • @ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下
  • @ConditionalOnWebApplication:当前项目是 Web 项 目的条件下

 自己实现一个starter

光说不练假把式,现在就来撸一个 starter,实现自定义线程池

第一步,创建threadpool-spring-boot-starter工程

第二步,引入 Spring Boot 相关依赖

第三步,创建ThreadPoolAutoConfiguration

第四步,在threadpool-spring-boot-starter工程的 resources 包下创建META-INF/spring.factories文件

最后新建工程引入threadpool-spring-boot-starter

测试通过!!!

总结

Spring Boot 通过@EnableAutoConfiguration开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories中的自动配置类实现自动装配,自动配置类其实就是通过@Conditional按需加载的配置类,想要其生效必须引入spring-boot-starter-xxx包实现起步依赖

SpringBoot的依赖管理

2.1 父项目做依赖管理

每个SpringBoot项目,pom.xml文件都会给我们定义一个parent节点

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version><relativePath/> <!-- lookup parent from repository -->
</parent>

该节点指定了version版本号,所以在pom.xml文件里我们很多引入的jar都没有定义版本号,但这样也不会出错,因为SpringBoot帮我们为一些常用的jar包指定了版本号。

ctrl + 鼠标右键点击进入spring-boot-starter-parent这个jar包,会发现它的父项目是spring-boot-dependencies

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.3.RELEASE</version><relativePath>../../spring-boot-dependencies</relativePath>
</parent> 

而在这个jar包里,就声明了很多开发中常用jar的版本号

所以在你pom.xml文件中引入jar的时候,如果该jar在spring-boot-dependencies中定义了版本号,那么你可以不写。如果你想使用其他的版本号,那么也可以在pom.xml中定义version,遵循就近原则。比如你想使用自定义版本号的MySQL驱动,只需在pom.xml中进行定义

<properties><mysql.version>5.1.43</mysql.version>
</properties>

其他 

@Configuration和@Bean注解

@Configuration注解

@Configuration注解表示这个类是个配置类,@Bean注解往容器中注册实例。

@Configuration这个注解可以加在类上,让这个类的功能等同于一个bean xml配置文件,如下:

@Configuration
public class ConfigBean {}

上面代码类似于下面的xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"></beans>

此时ConfigBean类中没有任何内容,相当于一个空的xml配置文件,此时我们要在ConfigBean类中注册bean,那么我们就要用到@Bean注解了。

@Bean注解

这个注解类似于bean xml配置文件中的bean元素,用来在spring容器中注册一个bean。

@Bean注解用在方法上,表示通过方法来定义一个bean,默认将方法名称作为bean名称,将方法返回值作为bean对象,注册到spring容器中。

@Bean
public User user1() {return new User();
}

@Configuration注解 容器中的实例都是单例

@Configuration注解中的proxyBeanMethods属性即代理bean的方法,决定是否是单例模式,默认为true。Full模式(proxyBeanMethods = true)和Lite(proxyBeanMethods = false)模式,Full模式保证每个@Bean方法被调用多少次返回的组件都是单实例的,而Lite模式每个@Bean方法被调用多少次返回的组件都是新创建的。组件依赖必须使用Full模式默认,其他默认是否Lite模式

@Import注解

该注解定义如下,只有一个value属性,你可以传入一个Class数组,在启动过程中,会自动帮你把类注册进容器。

@Configuration
@Import({User.class, DBHelper.class})
public class MyConfig {}

 @Conditional注解

意思就是满足@Conditional指定的条件,才进行组件注入。

import com.codeliu.entity.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
// 没有名字为test的bean,才进行注册,该注解可以放在类型,也可以放在方法上,作用范围不一样
@ConditionalOnMissingBean(name = "test")
public class MyConfig {@Beanpublic User user() {User user = new User("秃头哥", 20);return user;}
}

 @ImportResource注解

@ImportResource注解可以导入Spring的配置文件,让配置文件里的内容生效。因为有些项目bean定义在xml文件里,但你必须知道xml文件的路径,这样在项目启动的时候Spring才会加载配置文件。那对于SpringBoot项目来说,所有的bean都是通过java配置实现,xml没有用武之地了吗?

@Configuration搭配@ImportResource可以实现xml配置的装载。

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;@Configuration
@ImportResource("classpath:beans.xml")
public class MyConfig {}

 @Component和@ConfigurationProperties

在Java bean上使用这两个注解,可以和配置文件中的属性相关联,不过要注意的是,Java bean必须有setter/getter方法,否则无法赋值,另外就是配置文件中的属性不能有大写字母,否则启动报错。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
@ConfigurationProperties(prefix = "my-user")
public class User {private String names;private int age;....
}

省略了getter/setter和toString方法。@ConfigurationProperties注解中指定配置文件中相关属性的前缀,在配置文件中

my-user.names=CodeTiger
my-user.age=22

启动类中进行测试获取user对象,输出就会发现属性已经赋值。

@ConfigurationProperties和@EnableConfigurationProperties 

@ConfigurationProperties注解加载java bean上指定前缀,而@EnableConfigurationProperties注解则加在配置类上,该注解有两个作用:开启配置绑定功能、把指定的java bean注册到容器中。因为该注解会把java bean注册到容器中,所以在java bean上就不需要加@Component注解了。

@Configuration
@EnableConfigurationProperties({User.class})
public class MyConfig {}

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

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

相关文章

RedissonCach的源码流程

上&#xff1a; https://blog.csdn.net/Michelle_Zhong/article/details/126384566 中&#xff1a; https://blog.csdn.net/michelle_zhong/category_11874153.html 下&#xff1a; https://blog.csdn.net/Michelle_Zhong/article/details/126391915?ops_request_misc%257B%…

GE IS420UCSBH1A 控制器模块

控制器模块是工业自动化和控制系统中的关键组件&#xff0c;用于监测、控制和管理各种工程过程。这些模块通常具有以下特点&#xff1a; 多通道控制&#xff1a; 控制器模块通常可以控制多个通道&#xff0c;允许同时管理多个设备或过程。 实时控制&#xff1a; 模块支持实时控…

使用NVIDIA GPU FFmpeg转码 YUV to H264(成功)

0. 官方教程 NVIDIA官方教程&#xff1a;链接&#xff0c;本篇内容主要参考2.2 Software Setup。 1. 安装显卡驱动 确保nvidia-smi能够正常使用&#xff1a; 2. 安装CUDA toolkit 注意要与显卡驱动版本对应&#xff0c;验证toolkit是否正确安装&#xff1a; 3. 安装ffnvco…

代码版本控制工具GitLab :从安装到使用一步到位

一、GitLab 是什么&#xff1f; 如果听说过 Git 或者 GitHub&#xff0c;那么 GitLab 你一定也听说过。GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用 Git 作为代码管理工具&#xff0c;并在此基础上搭建起来的 Web 服务。简单理解&#xff1a;GitLab 类似私人版 …

计算机毕业设计选题推荐-社区志愿者服务微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

Transformers实战(二)快速入门文本相似度、检索式对话机器人

Transformers实战&#xff08;二&#xff09;快速入门文本相似度、检索式对话机器人 1、文本相似度 1.1 文本相似度简介 文本匹配是一个较为宽泛的概念&#xff0c;基本上只要涉及到两段文本之间关系的&#xff0c;都可以被看作是一种文本匹配的任务&#xff0c; 只是在具体…

【表面缺陷检测】铝型材表面缺陷检测数据集介绍(含xml标签文件)

一、铝型材介绍 铝型材是一种由铝合金材料制成的&#xff0c;具有固定截面形状和尺寸的条形建材。由于其优良的物理性能和广泛的应用领域&#xff0c;铝型材在现代工业和生活中发挥着重要的作用。 1、铝型材的分类 根据截面形状的不同&#xff0c;铝型材可分为角铝、槽铝、工…

frp内网穿透教程搭建0.52.3版本

网上很多关于frp的教程都是04 03版本的了&#xff0c;都是配置的ini文件&#xff0c;现在都改成toml文件了&#xff0c;下面基本上都是官方文档的简单copy&#xff0c;细节推荐打开去看中文版的文档介绍&#xff08;地址放在最后了&#xff09;。下面简单介绍几个 为什么使用 …

CAN接口的PCB Layout规则要求汇总

随着时代高速发展&#xff0c;控制器局域网&#xff08;CAN&#xff09;接口的应用越来越广泛&#xff0c;尤其是在汽车电子、航空航天等领域中发挥着重要作用&#xff0c;为了确保CAN接口的可靠性和稳定性&#xff0c;工程师必须在其PCB Layout方面下功夫&#xff0c;下面来看…

1496. 判断路径是否相交

1496. 判断路径是否相交 java代码&#xff1a; class Solution {public boolean isPathCrossing(String path) {int x 0;int y 0;HashSet<String> hashSet new HashSet<>();hashSet.add("0-0");for (int i 0; i < path.length(); i) {switch (pa…

气膜场馆里面噪声很大怎么解决?

随着气膜结构在各个领域的广泛应用&#xff0c;人们开始意识到在这些场馆内部&#xff0c;特别是在大型活动和展览中&#xff0c;噪声问题可能会变得相当严重。传统的气膜结构通常难以提供良好的声学环境&#xff0c;这对于参与者的舒适度和活动的质量构成了挑战。为了解决气膜…

内网穿透实现在外远程访问NAS威联通(QNAP)

文章目录 前言1. 威联通安装cpolar内网穿透2. 内网穿透2.1 创建隧道2.2 测试公网远程访问 3. 配置固定二级子域名3.1 保留二级子域名3.2 配置二级子域名 4. 使用固定二级子域名远程访问 前言 购入威联通NAS后&#xff0c;很多用户对于如何在外在公网环境下的远程访问威联通NAS…

人工智能基础_机器学习011_梯度下降概念_梯度下降步骤_函数与导函数求解最优解---人工智能工作笔记0051

然后我们来看一下梯度下降,这里先看一个叫 无约束最优化问题,,值得是从一个问题的所有可能的备选方案中选最优的方案, 我们的知道,我们的正态分布这里,正规的一个正态分布,还有我们的正规方程,他的这个x,是正规的,比如上面画的这个曲线,他的这个x,就是大于0的对吧,而现实生活…

现代挖掘机vr在线互动展示厅是实现业务增长的加速度

VR数字博物馆全景展示充分应用5G、VR全景、web3d开发和三维动画等技术&#xff0c;将实体博物馆整体还原到3D数字空间&#xff0c;让游客360全景漫游式参观&#xff0c;无论大小、贵重、破损的典藏展品都能通过3D建模技术&#xff0c;逼真重现到三维虚拟场景中&#xff0c;让参…

从单模态到多模态,自主AI离我们还有多远?

一、从AI的诞生和发展说起 人工智能的发展&#xff0c;从思想诞生上&#xff0c;可以追逐到十七世纪的帕斯卡和莱布尼茨&#xff0c;1666年&#xff0c;德国博学家戈特弗里德威廉莱布尼茨发表了一篇题为《论组合的艺术》的神秘论文。当时的莱布尼茨只有20岁&#xff0c;他概述了…

python爬虫之正则表达式解析实战

文章目录 1. 图片爬取流程分析2. 实现代码—爬取家常菜图片 1. 图片爬取流程分析 先获取网址&#xff0c;URL&#xff1a;https://www.xiachufang.com/category/40076/ 定位想要爬取的内容使用正则表达式爬取导入模块指定URLUA伪装&#xff08;模拟浏览器&#xff09;发起请求…

SAP SPAD新建打印纸张

SAP SPAD新建打印纸张 1.事务代码SPAD 2.完全管理&#xff0d;设备类型&#xff0d;页格式-显示(创建格式页) 3.按标准A4纸张为模板参考创建。同一个纸张纵向/横向各创建1次(创建格式页) 4.完全管理&#xff0d;设备类型&#xff0d;格式类型-显示(创建格式类型&#xff0…

RabbitMQ如何保证消息不丢失呢?

RabbitMQ 是一个流行的消息队列系统&#xff0c;用于在分布式应用程序之间传递消息。要确保消息不会丢失&#xff0c;可以采取以下一些措施&#xff1a; 持久化消息&#xff1a; RabbitMQ 允许你将消息标记为持久化的。这意味着消息将被写入磁盘&#xff0c;即使 RabbitMQ 服务…

vite+vue3实现 tomcat 的本地部署

背景&#xff1a; 很多开发小伙伴在本地开发完前端项目后&#xff0c;碍于服务端环境配置麻烦&#xff0c;想先试试在本地部署&#xff0c;已开发好的前端项目&#xff0c;由于很多文章都是文字性描述&#xff0c;不太直观&#xff0c;为了给大多数新手提供一个教程&#xff0c…