【Spring Boot】Spring Boot 中的 Starter

Spring Boot 中的 Starter

  • 1.常用 Starter
  • 2.为什么要用 Starter
  • 3.Starter 有哪些要素

我们都知道,Spring 的功能非常强大,但也有些弊端。比如:我们需要手动去配置大量的参数,没有默认值,需要我们管理大量的 jar 包和它们的依赖。为了提升 Spring 项目的开发效率,简化一些配置,Spring 官方引入了 SpringBoot。当然,引入 SpringBoot 还有其他原因,在这里就不过多描述了。

而 Spring Boot 为了简化配置,提供了非常多的 Starter。它先打包好与常用模块相关的所有 JAR 包,并完成自动配置,然后组装成 Starter(如把 Web 相关的 Spring MVC、容器等打包好后组装成 spring-boot-starter-web)。这使得在开发业务代码时不需要过多关注框架的配置,只需关注业务逻辑即可。

1.常用 Starter

Spring Boot 提供了很多开箱即用的 Starter,大概有近 50 种,常见的如下表所示。

Starter
说明
spring-boot-starter-web用于构建 Web。包含 RESTful 风格框架、SpringMVC 和默认的嵌入式容器 Tomcat
spring-boot-starter-test用于测试
spring-boot-starter-data-jpa带有 Hibernate 的 Spring Data JPA
spring-boot-starter-jdbc传统的 JDBC。轻量级应用可以使用,学习成本低,但最好使用 JPA 或 MyBatis
spring-boot-starter-thymeleaf支持 Thymeleaf 模板
spring-boot-starter-mail支持 Java Mail、Spring Email 发送邮件
spring-boot-starter-integrationSpring 框架创建的一个 API,面向企业应用集成(EAI)
spring-boot-starter-mobileSpring MVC 的扩展,用来简化手机上的 Web 应用程序开发
spring-boot-starter-data-redis通过 Spring Data Redis、Redis Client 使用 Redis
spring-boot-starter-validationBean Validation 是一个数据验证的规范,Hibernate Validator 是一个数据验证框架
spring-boot-starter-websocket相对于非持久的协议 HTTP,Websocket 是一个持久化的协议
spring-boot-starter-web-servicesSOAP Web Services
spring-boot-starter-hateoas为服务添加 HATEOAS 功能
spring-boot-starter-security用 Spring Security 进行身份验证和授权
spring-boot-starter-data-rest用 Spring Data REST 公布简单的 REST 服务

如果想使用 Spring 的 JPA 操作数据库,则需要在项目中添加 spring-boot-starter-data-jpa 依赖,即在 pom.xml 文件中的 <dependencies> 和元素之间加入依赖,具体代如下:

<dependencies>
	...
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
	...
</dependencies>

如果依赖项没有版本号,则 Spring Boot 会根据自己的版本号自动关联。如果需要特定的版本则需要加上 version 元素。

2.为什么要用 Starter

在 SpringBoot 还没有出来之前,我们使用 Spring 开发项目。如果程序需要连接数据库,我们一般会使用 Hibernate 或 Mybatis 等 ORM 框架,这里我以 Mybatis 为例,具体的操作步骤如下:

  • 到 Maven 仓库去找需要引入的 mybatisjar 包,选取合适的版本。
  • 到 Maven 仓库去找 mybatis-spring 整合的 jar 包,选取合适的版本。
  • 在 Spring 的 applicationContext.xml 文件中配置 dataSourcemybatis 相关信息。

当然有些朋友可能会指正,不是还需要引入数据库驱动包吗?

确实需要引入,但数据库驱动有很多,比如:mysqloraclesqlserver,这不属于 mybatis 的范畴,使用者可以根据项目的实际情况单独引入。

如果程序只是需要连接数据库这一个功能还好,按上面的步骤做基本可以满足需求。但是,连接数据库可能只是庞大的项目体系中一个环节,实际项目中往往更复杂,需要引入更多的功能,比如:连接 redis、连接 mongodb、使用 rocketmq、使用 excel 功能等等。

引入这些功能的话,需要再把上面的步骤再重复一次,工作量无形当中增加了不少,而且有很多重复的工作。

另外,还是有个问题,每次到要到 Maven 中找合适的版本,如果哪次找的 mybatis.jar 包和 mybatis-spring.jar 包版本不兼容,程序不是会出现问题?

SpringBoot 为了解决以上两个问题引入了 Starter 机制。

3.Starter 有哪些要素

我们首先一起看看 mybatis-spring-boot-starter.jar 是如何定义的。

在这里插入图片描述
可以看到它的 META-INF 目录下只包含了:

  • pom.protperties:配置 Maven 所需的项目 versiongroupIdartifactId
  • pom.xml:配置所依赖的 jar 包。
  • MANIFEST.MF:这个文件描述了该 jar 文件的很多信息。
  • spring.provides:配置所依赖的 artifactId,给 IDE 使用的,没有其他的作用。

注意一下,没有一行代码。

我们重点看一下 pom.xml,因为这个 jar 包里面除了这个没有啥重要的信息。

<?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>
    <parent>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot</artifactId>
        <version>1.3.1</version>
    </parent>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <name>mybatis-spring-boot-starter</name>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
        </dependency>
    </dependencies>
</project>

从上面可以看出,pom.xml 文件中会引入一些 jar 包,其中除了引入 spring-boot-starter,之外重点看一下:mybatis-spring-boot-autoconfigure

我们找到 mybatis-spring-boot-autoconfigure.jar 文件,打开这个文件。

在这里插入图片描述

里面包含如下文件:

  • pom.properties:配置 Maven 所需的项目 versiongroupIdartifactId
  • pom.xml:配置所依赖的 jar
  • additional-spring-configuration-metadata.json:手动添加 IDE 提示功能
  • MANIFEST.MF:这个文件描述了该 jar 文件的很多信息
  • spring.factories:SPI(Service Provider Interface)会读取的文件
  • spring-configuration-metadata.json:系统自动生成的 IDE 提示功能
  • ConfigurationCustomizer:自定义 Configuration 回调接口
  • MybatisAutoConfigurationmybatis 配置类
  • MybatisPropertiesmybatis 属性类
  • SpringBootVFS:扫描嵌套的 jar 包中的类

spring-configuration-metadata.jsonadditional-spring-configuration-metadata.json 的功能差不多,我们在 applicationContext.properties 文件中输入 spring 时,会自动出现下面的配置信息可供选择,就是这个功能了。

在这里插入图片描述

这两个文件有什么区别?

如果 pom.xml 中引入了 spring-boot-configuration-processor 包,则会自动生成 spring-configuration-metadata.json

如果需要手动修改里面的元数据,则可以在 additional-spring-configuration-metadata.json 中编辑,最终两个文件中的元数据会合并到一起。

MybatisProperties 类是 属性实体类

@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {

    public static final String MYBATIS_PREFIX = "mybatis";
    private String configLocation;
    private String[] mapperLocations;
    private String typeAliasesPackage;
    private String typeHandlersPackage;
    private boolean checkConfigLocation = false;
    private ExecutorType executorType;
    private Properties configurationProperties;
    @NestedConfigurationProperty
    private Configuration configuration;

    public String getConfigLocation() {
        return this.configLocation;
    }

    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    @Deprecated
    public String getConfig() {
        return this.configLocation;
    }

    @Deprecated
    public void setConfig(String config) {
        this.configLocation = config;
    }

    public String[] getMapperLocations() {
        return this.mapperLocations;
    }

    public void setMapperLocations(String[] mapperLocations) {
        this.mapperLocations = mapperLocations;
    }

    public String getTypeHandlersPackage() {
        return this.typeHandlersPackage;
    }

    public void setTypeHandlersPackage(String typeHandlersPackage) {
        this.typeHandlersPackage = typeHandlersPackage;
    }

    public String getTypeAliasesPackage() {
        return this.typeAliasesPackage;
    }

    public void setTypeAliasesPackage(String typeAliasesPackage) {
        this.typeAliasesPackage = typeAliasesPackage;
    }

    public boolean isCheckConfigLocation() {
        return this.checkConfigLocation;
    }

    public void setCheckConfigLocation(boolean checkConfigLocation) {
        this.checkConfigLocation = checkConfigLocation;
    }

    public ExecutorType getExecutorType() {
        return this.executorType;
    }

    public void setExecutorType(ExecutorType executorType) {
        this.executorType = executorType;
    }

    public Properties getConfigurationProperties() {
        return configurationProperties;
    }

    public void setConfigurationProperties(Properties configurationProperties) {
        this.configurationProperties = configurationProperties;
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }

    public Resource[] resolveMapperLocations() {
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        List<Resource> resources = new ArrayList<Resource>();
        if (this.mapperLocations != null) {
            for (String mapperLocation : this.mapperLocations) {
                try {
                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
                    resources.addAll(Arrays.asList(mappers));
                } catch (IOException e) {
                    // ignore
                }
            }
        }
        return resources.toArray(new Resource[resources.size()]);
    }
}

可以看到 Mybatis 初始化所需要的很多属性都在这里,相当于一个 JavaBean。

下面重点看一下 MybatisAutoConfiguration 的代码:

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {

    private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
    private final MybatisProperties properties;
    private final Interceptor[] interceptors;
    private final ResourceLoader resourceLoader;
    private final DatabaseIdProvider databaseIdProvider;
    private final List<ConfigurationCustomizer> configurationCustomizers;
    public MybatisAutoConfiguration(MybatisProperties properties,
                                    ObjectProvider<Interceptor[]> interceptorsProvider,
                                    ResourceLoader resourceLoader,
                                    ObjectProvider<DatabaseIdProvider> databaseIdProvider,
                                    ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
        this.properties = properties;
        this.interceptors = interceptorsProvider.getIfAvailable();
        this.resourceLoader = resourceLoader;
        this.databaseIdProvider = databaseIdProvider.getIfAvailable();
        this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
    }

    @PostConstruct
    public void checkConfigFileExists() {
        if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
            Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
            Assert.state(resource.exists(), "Cannot find config location: " + resource
                    + " (please add config file or check your Mybatis configuration)");
        }
    }

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }
        Configuration configuration = this.properties.getConfiguration();
        if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
            configuration = new Configuration();
        }
        if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
            for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
                customizer.customize(configuration);
            }
        }
        factory.setConfiguration(configuration);
        if (this.properties.getConfigurationProperties() != null) {
            factory.setConfigurationProperties(this.properties.getConfigurationProperties());
        }
        if (!ObjectUtils.isEmpty(this.interceptors)) {
            factory.setPlugins(this.interceptors);
        }
        if (this.databaseIdProvider != null) {
            factory.setDatabaseIdProvider(this.databaseIdProvider);
        }
        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }
        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }
        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            factory.setMapperLocations(this.properties.resolveMapperLocations());
        }

        return factory.getObject();
    }

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        ExecutorType executorType = this.properties.getExecutorType();
        if (executorType != null) {
            return new SqlSessionTemplate(sqlSessionFactory, executorType);
        } else {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    }

    public static class AutoConfiguredMapperScannerRegistrar
            implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
        private BeanFactory beanFactory;
        private ResourceLoader resourceLoader;

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

            ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
            try {
                if (this.resourceLoader != null) {
                    scanner.setResourceLoader(this.resourceLoader);
                }

                List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
                if (logger.isDebugEnabled()) {
                    for (String pkg : packages) {
                        logger.debug("Using auto-configuration base package '{}'", pkg);
                    }
                }

                scanner.setAnnotationClass(Mapper.class);
                scanner.registerFilters();
                scanner.doScan(StringUtils.toStringArray(packages));
            } catch (IllegalStateException ex) {
                logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
            }
        }

        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = beanFactory;
        }

        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
    }

    @org.springframework.context.annotation.Configuration
    @Import({ AutoConfiguredMapperScannerRegistrar.class })
    @ConditionalOnMissingBean(MapperFactoryBean.class)
    public static class MapperScannerRegistrarNotFoundConfiguration {

        @PostConstruct
        public void afterPropertiesSet() {
            logger.debug("No {} found.", MapperFactoryBean.class.getName());
        }
    }
}

这个类就是一个 Configuration(配置类),它里面定义很多 Bean,其中最重要的就是 SqlSessionFactory 的 Bean 实例,该实例是 Mybatis 的核心功能,用它创建 SqlSession,对数据库进行 CRUD 操作。

除此之外,MybatisAutoConfiguration 类还包含了:

  • @ConditionalOnClass 配置了只有包含 SqlSessionFactory.classSqlSessionFactoryBean.class,该配置类才生效。
  • @ConditionalOnBean 配置了只有包含 dataSource 实例时,该配置类才生效。
  • @EnableConfigurationProperties 该注解会自动填充 MybatisProperties 实例中的属性。
  • @AutoConfigureAfter 配置了该配置类在 DataSourceAutoConfiguration 类之后自动配置。

这些注解都是一些辅助功能,决定 Configuration 是否生效,当然这些注解不是必须的。

接下来,重点看看 spring.factories 文件有啥内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

里面只有一行配置,即 Key 为 EnableAutoConfiguration,Value 为 MybatisAutoConfiguration

好了,介绍了这么多东西,现在我们来总结一下,

Starter 几个要素如下图所示:

在这里插入图片描述
那么,编写 Starter 需要哪些步骤?

  • 需要定义一个名称为 xxx-spring-boot-starter 的空项目,里面不包含任何代码,可以有 pom.xmlpom.properties 文件。
  • pom.xml 文件中包含了名称为 xxx-spring-boot-autoconfigure 的项目。
  • xxx-spring-boot-autoconfigure 项目中包含了名称为 xxxAutoConfiguration 的类,该类可以定义一些 Bean 实例。当然,Configuration 类上可以打一些如:ConditionalOnClassConditionalOnBeanEnableConfigurationProperties 等注解。
  • 需要在 spring.factories 文件中增加 Key 为 EnableAutoConfiguration,Value 为 xxxAutoConfiguration

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于滴滴平台数据测算的城市发展环境指数数据集

01、数据简介 在数字化浪潮的推动下&#xff0c;城市发展的脉搏正以前所未有的方式被捕捉和解读。滴滴平台&#xff0c;作为城市出行领域的佼佼者&#xff0c;其海量的出行数据不仅记录了市民的每一次出行轨迹&#xff0c;更深刻反映了城市发展的内在逻辑。 滴滴城市发展指数…

基于springboot实现高校教师电子名片系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现高校教师电子名片系统演示 摘要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;名片信息因为其管理内容繁杂&#xff…

ThreadLocal全面解析

目录 一、ThreadLocal的介绍1、简介2、基本使用3、ThreadLocal与synchronized的区别 二、ThreadLocal的内部结构1、jdk早期设计2、JDK8设计3、内存泄露 三、ThreadLocal的核心方法源码1、set方法2、get方法3、initialValue方法4、withInitial方法5、remove方法6、子类Inheritab…

vue3实战Easy云盘(一):创建项目+登录注册页面+构建框架页+上传头像/修改密码/退出登录

一、创建项目 1.创建项目 2.安装各种依赖 npm install highlightjs/vue-plugin moefe/vue-aplayer aplayer axios docx-preview dplayer element-plus highlight.js js-md5 sass sass-loader spark-md5 vue-clipboard3 vue-cookies vue-pdf-embed vue-router …

动态IP避坑指南,怎样挑选合适的动态IP?

在现今这个数字化、网络化的时代&#xff0c;动态IP的使用越来越广泛&#xff0c;无论是为了保护网络安全、提高网络访问速度&#xff0c;还是为了实现某些特定的网络功能&#xff0c;动态IP都发挥着不可或缺的作用。然而&#xff0c;如何挑选一个合适的动态IP&#xff0c;避免…

git知识总结

要知道 本地回退后&#xff0c;反悔了&#xff0c;可以恢复。前提是已经提交了&#xff0c;提交了就丢不了。 git reflog git reset --hard commitId 以前git push不让推&#xff0c;就是没有对应关系。第一次推要setxxx参数。 前奏 设置用户名和邮箱&#xff0c;设置错…

docker 部署SSM项目(包含打包)

一&#xff1a;SSM项目打包 1.这个一定要勾选防止静态资源没打包上 2.第二步 3.第三步 4.更改名字(注意部署到线上的时候这里如果用docker或者window部署的话需要带这个项目名&#xff0c;不然会出现找不到接口的情况) ![在这里插入图片描述](https://img-blog.csdnimg.cn/dir…

C#中数组与列表,集合等的联系

C#中&#xff0c;所有数组都自动继承于System.Array这个抽象类&#xff0c;数组都为引用类型&#xff0c; 所有对数组的更新都会导致源数组的元素值的篡改。 而所有集合的根都来自可枚举接口IEnumerable 数组有三种样式&#xff1a; 数组的Rank&#xff08;秩&#xff09;属…

企业微信hook接口协议,ipad协议http,同步消息记录

同步消息记录 参数名必选类型说明uuid是String每个实例的唯一标识&#xff0c;根据uuid操作具体企业微信limit是int每次返回大小seq是int查询下标 请求示例 {"uuid":"ecb033af-6fcd-4ec2-880e-41f070b65eaf","limit":1000, "seq":1…

访客管理系统对于校园安全的重要性

校园访客办理计划是针对校园安全需求规划的安全办理体系&#xff0c;主要用于对校园外来人员的科学办理。要做好校园安全作业&#xff0c;把风险分子拒之门外尤为要害。校园访客办理计划实现访客实名制&#xff0c;并结合公安网、黑名单功用&#xff0c;对风险人员进行提前预警…

浅析vue3自定义指令

vue3中可以像下面这样使用自定义指令。 这里我们只是定义了一个vFoucs变量&#xff0c;vue怎么知道这是一个指令呢&#xff1f; 这是因为约定大于配置&#xff0c;vue3中有这样一个约定&#xff08;截图来自官方文档&#xff09;&#xff1a; 注意这里说的是驼峰命令&#x…

【class6】人工智能初步(选择一个合适的监督学习算法。)

【昨日内容复习】 进行监督学习时&#xff0c;第一个步骤是提取数据集的文本特征和对应的标签。 提取文本特征的具体步骤如下&#xff1a; STEP1. 构造词袋模型&#xff0c;提取数据集中的文本特征 STEP2. 使用toarray()函数&#xff0c;将X转换为一个NumPy数组&#xff0c;方…

初始化linux数据盘(3TB)分区-格式化-挂载目录

场景说明&#xff1a;某云给我们服务器加载了一块3TB的硬盘扩容&#xff08;没有直接扩&#xff0c;原因是原来的盘做的是mbr&#xff08;什么年代了&#xff0c;谁干的&#xff09;的分区&#xff0c;最大识别2TB&#xff09; 确认磁盘 输入命令lsblk 查看数据盘信息 &#…

PyQt5的多窗口设计

文章目录 步骤一步骤2步骤3步骤4步骤5完整的代码如下 步骤一 1.首先新建一个窗口&#xff0c;把对象名改为ParentWindow&#xff0c;然后保存&#xff0c;把.ui的名字改为Main_window.ui 步骤2 再新建一个窗口&#xff0c;把对象名改为ChildWindow1&#xff0c;然后保存&…

Python 旋转立方体

文章目录 效果图运行环境完整代码实现思路1. 导入库和定义常量2. 创建Cube类3. 实现Cube类的draw方法4. 实现主函数 效果图 运行环境 python版本&#xff1a;python3.x 依赖包&#xff1a; $ pip install pygame $ pip install numpy完整代码 import numpy as np # 导入 N…

【复试分数线】工科985历年分数线汇总(第三弹)

国家线 可以看作是考研上岸最最最基础的门槛。真正决定你能不能进入复试的还要看院线&#xff08;复试分数线&#xff09;&#xff01;今天我将分析考信号的5所工科类985近三年复试分数线&#xff0c;大家可以参考&#xff01;&#xff01; 分别是&#xff1a;①北京航空航天大…

k8s coredns配置

1.coredns可根据集群具体数量修改pod数&#xff0c;官方推荐比例为5/1&#xff0c;即有15台服务器最好是3个pod。 2.coredns会继承pod所在主机的dns解析,修改了主机的dns解析之后&#xff0c;coredns有一段时间的缓存&#xff0c;重启coredns才会在集群内部立刻生效该解析。 …

黑马甄选离线数仓项目day01(项目介绍)

课程介绍 项目名称 黑马甄选数仓形式 离线数仓开发业务类型 电商业务 电商介绍 B2B B2C C2C 项目属于 新零售电商 新零售 线上(网站,app,小程序&#xff09; 线下&#xff08;实体体验店&#xff09; 物流&#xff08;自营物流&#xff09; 项目行业 果蔬生鲜领域 商业模式 B…

[图解]实现领域驱动设计译文暴露的问题03

0 00:00:02,960 --> 00:00:04,940 前面我们讲了 1 00:00:05,260 --> 00:00:06,810 第①句话的 2 00:00:07,090 --> 00:00:09,750 第&#xff08;1&#xff09;个问题和第&#xff08;2&#xff09;个问题 3 00:00:13,920 --> 00:00:16,930 共享父类的对象&#…

【代码随想录算法训练Day5】今天休息,复盘总结

Day5 休息日 时机恰到好处&#xff0c;刚好学习完了数组和链表&#xff0c;从代码随想录里扒了两张总结图来&#xff0c;这就是这几天里我们一起解决的问题&#xff0c;如果以后忘了&#xff0c;还有问题&#xff0c;先回到这里&#xff0c;只看思维导图&#xff0c;还能想起来…