Sprig boot自动配置

1、概述

Spring Boot是Spring旗下众多的子项目之一,其理念是约定优于配置,它通过实现了自动配置(大多数用户平时习惯设置的配置作为默认配置)的功能来为用户快速构建出标准化的应用。Spring Boot的特点可以概述为如下几点:

  • 内置了嵌入式的Tomcat、Jetty等Servlet容器,应用可以不用打包成War格式,而是可以直接以Jar格式运行。

  • 提供了多个可选择的”starter”以简化Maven的依赖管理(也支持Gradle),让您可以按需加载需要的功能模块。

  • 尽可能地进行自动配置,减少了用户需要动手写的各种冗余配置项,Spring Boot提倡无XML配置文件的理念,使用Spring Boot生成的应用完全不会生成任何配置代码与XML配置文件。

  • 提供了一整套的对应用状态的监控与管理的功能模块(通过引入spring-boot-starter-actuator),包括应用的线程信息、内存信息、应用是否处于健康状态等,为了满足更多的资源监控需求,Spring Cloud中的很多模块还对其进行了扩展。

有关Spring Boot的使用方法就不做多介绍了,如有兴趣请自行阅读官方文档Spring Boot或其他文章。

如今微服务的概念愈来愈热,转型或尝试微服务的团队也在如日渐增,而对于技术选型,Spring Cloud是一个比较好的选择,它提供了一站式的分布式系统解决方案,包含了许多构建分布式系统与微服务需要用到的组件,例如服务治理、API网关、配置中心、消息总线以及容错管理等模块。可以说,Spring Cloud”全家桶”极其适合刚刚接触微服务的团队。似乎有点跑题了,不过说了这么多,我想要强调的是,Spring Cloud中的每个组件都是基于Spring Boot构建的,而理解了Spring Boot的自动配置的原理,显然也是有好处的。

Spring Boot的自动配置看起来神奇,其实原理非常简单,背后全依赖于@Conditional注解来实现的。

2、什么是@Conditional?

@Conditional是由Spring 4提供的一个新特性,用于根据特定条件来控制Bean的创建行为。而在我们开发基于Spring的应用的时候,难免会需要根据条件来注册Bean。

例如,你想要根据不同的运行环境,来让Spring注册对应环境的数据源Bean,对于这种简单的情况,完全可以使用@Profile注解实现,就像下面代码所示:

@Configuration
public class AppConfig {@Bean@Profile("DEV")public DataSource devDataSource() {...}@Bean@Profile("PROD")public DataSource prodDataSource() {...}
}

剩下只需要设置对应的Profile属性即可,设置方法有如下三种:

  • 通过context.getEnvironment().setActiveProfiles(“PROD”)来设置Profile属性。
  • 通过设定jvm的spring.profiles.active参数来设置环境(Spring Boot中可以直接在application.properties配置文件中设置该属性)。
  • 通过在DispatcherServlet的初始参数中设置。
<servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>spring.profiles.active</param-name><param-value>PROD</param-value></init-param>
</servlet>

但这种方法只局限于简单的情况,而且通过源码我们可以发现@Profile自身也使用了@Conditional注解。

package org.springframework.context.annotation;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class}) // 组合了Conditional注解
public @interface Profile {String[] value();
}
package org.springframework.context.annotation;
class ProfileCondition implements Condition {ProfileCondition() {}// 通过提取出@Profile注解中的value值来与profiles配置信息进行匹配public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {if(context.getEnvironment() != null) {MultiValueMap attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());if(attrs != null) {Iterator var4 = ((List)attrs.get("value")).iterator();Object value;do {if(!var4.hasNext()) {return false;}value = var4.next();} while(!context.getEnvironment().acceptsProfiles((String[])((String[])value)));return true;}}return true;}
}

在业务复杂的情况下,显然需要使用到@Conditional注解来提供更加灵活的条件判断,例如以下几个判断条件:

  • 在类路径中是否存在这样的一个类。
  • 在Spring容器中是否已经注册了某种类型的Bean(如未注册,我们可以让其自动注册到容器中,上一条同理)。
  • 一个文件是否在特定的位置上。
  • 一个特定的系统属性是否存在。
  • 在Spring的配置文件中是否设置了某个特定的值。

举个栗子,假设我们有两个基于不同数据库实现的DAO,它们全都实现了UserDao,其中JdbcUserDAO与MySql进行连接,MongoUserDAO与MongoDB进行连接。现在,我们有了一个需求,需要根据命令行传入的系统参数来注册对应的UserDao,就像java -jar app.jar -DdbType=MySQL会注册JdbcUserDao,而java -jar app.jar -DdbType=MongoDB则会注册MongoUserDao。使用@Conditional可以很轻松地实现这个功能,仅仅需要在你自定义的条件类中去实现Condition接口,让我们来看下面的代码。(以下案例来自:https://dzone.com/articles/how-springboot-autoconfiguration-magic-works)

public interface UserDAO {....
}
public class JdbcUserDAO implements UserDAO {....
}
public class MongoUserDAO implements UserDAO {....
}
public class MySQLDatabaseTypeCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {String enabledDBType = System.getProperty("dbType"); // 获得系统参数 dbType// 如果该值等于MySql,则条件成立return (enabledDBType != null && enabledDBType.equalsIgnoreCase("MySql"));}
}
// 与上述逻辑一致
public class MongoDBDatabaseTypeCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {String enabledDBType = System.getProperty("dbType");return (enabledDBType != null && enabledDBType.equalsIgnoreCase("MongoDB"));}
}
// 根据条件来注册不同的Bean
@Configuration
public class AppConfig {@Bean@Conditional(MySQLDatabaseTypeCondition.class)public UserDAO jdbcUserDAO() {return new JdbcUserDAO();}@Bean@Conditional(MongoDBDatabaseTypeCondition.class)public UserDAO mongoUserDAO() {return new MongoUserDAO();}
}

现在,我们又有了一个新需求,我们想要根据当前工程的类路径中是否存在MongoDB的驱动类来确认是否注册MongoUserDAO。为了实现这个需求,可以创建检查MongoDB驱动是否存在的两个条件类。

public class MongoDriverPresentsCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {try {Class.forName("com.mongodb.Server");return true;} catch (ClassNotFoundException e) {return false;}}
}
public class MongoDriverNotPresentsCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {try {Class.forName("com.mongodb.Server");return false;} catch (ClassNotFoundException e) {return true;}}
}

假如,你想要在UserDAO没有被注册的情况下去注册一个UserDAOBean,那么我们可以定义一个条件类来检查某个类是否在容器中已被注册。

public class UserDAOBeanNotPresentsCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {UserDAO userDAO = conditionContext.getBeanFactory().getBean(UserDAO.class);return (userDAO == null);}
}

如果你想根据配置文件中的某项属性来决定是否注册MongoDAO,例如app.dbType是否等于MongoDB,我们可以实现以下的条件类。

public class MongoDbTypePropertyCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {String dbType = conditionContext.getEnvironment().getProperty("app.dbType");return "MONGO".equalsIgnoreCase(dbType);}
}

我们已经尝试并实现了各种类型的条件判断,接下来,我们可以选择一种更为优雅的方式,就像@Profile一样,以注解的方式来完成条件判断。首先,我们需要定义一个注解类。

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(DatabaseTypeCondition.class)
public @interface DatabaseType {String value();
}

具体的条件判断逻辑在DatabaseTypeCondition类中,它会根据系统参数dbType来判断注册哪一个Bean。

public class DatabaseTypeCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {Map<String, Object> attributes = metadata.getAnnotationAttributes(DatabaseType.class.getName());String type = (String) attributes.get("value");// 默认值为MySqlString enabledDBType = System.getProperty("dbType", "MySql");return (enabledDBType != null && type != null && enabledDBType.equalsIgnoreCase(type));}
}

最后,在配置类应用该注解即可。

@Configuration
@ComponentScan
public class AppConfig {@Bean@DatabaseType("MySql")public UserDAO jdbcUserDAO() {return new JdbcUserDAO();}@Bean@DatabaseType("mongoDB")public UserDAO mongoUserDAO() {return new MongoUserDAO();}
}

下面列举Spring Boot对@Conditional的扩展注解
在这里插入图片描述

3、AutoConfigure源码分析

通过了解@Conditional注解的机制其实已经能够猜到自动配置是如何实现的了,接下来我们通过源码来看看它是怎么做的。本文中讲解的源码基于Spring Boot 1.5.9版本(最新的正式版本)。

SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties 或者 @ConfigurationProperties 等几个注解来进行自动配置完成的。

@EnableAutoConfiguration 开启自动配置,主要作用就是调用 Spring-Core 包里的 loadFactoryNames(),将 autoconfig 包里的已经写好的自动配置加载进来。

@Conditional 条件注解,通过判断类路径下有没有相应配置的 jar 包来确定是否加载和自动配置这个类。

@EnableConfigurationProperties 的作用就是,给自动配置提供具体的配置参数,只需要写在 application.properties 中,就可以通过映射写入配置类的 POJO 属性中。

@EnableAutoConfiguration

@Enable*注释并不是SpringBoot新发明的注释,Spring 3框架就引入了这些注释,用这些注释替代XML配置文件。比如:
@EnableTransactionManagement注释,它能够声明事务管理
@EnableWebMvc注释,它能启用Spring MVC
@EnableScheduling注释,它可以初始化一个调度器。

这些注释事实上都是简单的配置,通过@Import注释导入。
从启动类的@SpringBootApplication进入,在里面找到了@EnableAutoConfiguration,

在这里插入图片描述

在这里插入图片描述

@EnableAutoConfiguration里通过@Import导入了 EnableAutoConfigurationImportSelector

在这里插入图片描述

进入他的父类AutoConfigurationImportSelector

在这里插入图片描述

找到selectImports()方法,他调用了getCandidateConfigurations()方法,在这里,这个方法又调用了Spring Core包中的loadFactoryNames()方法。这个方法的作用是,会查询META-INF/spring.factories文件中包含的JAR文件。

在这里插入图片描述

找到spring.factories文件后,SpringFactoriesLoader将查询配置文件命名的属性。
在这里插入图片描述

在这里插入图片描述

Jar文件在org.springframework.boot.autoconfigure的spring.factories
在这里插入图片描述
spring.factories内容如下(截取部分),在这个文件中,可以看到一系列Spring Boot自动配置的列表

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\

自动配置类中的条件注解

接下来,我们在spring.factories文件中随便找一个自动配置类,来看看是怎样实现的。我查看了MongoDataAutoConfiguration的源码,发现它声明了@ConditionalOnClass注解,通过看该注解的源码后可以发现,这是一个组合了@Conditional的组合注解,它的条件类是OnClassCondition。

@Configuration
@ConditionalOnClass({Mongo.class, MongoTemplate.class})
@EnableConfigurationProperties({MongoProperties.class})
@AutoConfigureAfter({MongoAutoConfiguration.class})
public class MongoDataAutoConfiguration {....
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnClass {Class<?>[] value() default {};String[] name() default {};
}

然后,我们开始看OnClassCondition的源码,发现它并没有直接实现Condition接口,只好往上找,发现它的父类SpringBootCondition实现了Condition接口。

class OnClassCondition extends SpringBootCondition implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {.....
}
public abstract class SpringBootCondition implements Condition {private final Log logger = LogFactory.getLog(this.getClass());public SpringBootCondition() {}public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {String classOrMethodName = getClassOrMethodName(metadata);try {ConditionOutcome ex = this.getMatchOutcome(context, metadata);this.logOutcome(classOrMethodName, ex);this.recordEvaluation(context, classOrMethodName, ex);return ex.isMatch();} catch (NoClassDefFoundError var5) {throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to " + var5.getMessage() + " not found. Make sure your own configuration does not rely on that class. This can also happen if you are @ComponentScanning a springframework package (e.g. if you put a @ComponentScan in the default package by mistake)", var5);} catch (RuntimeException var6) {throw new IllegalStateException("Error processing condition on " + this.getName(metadata), var6);}}public abstract ConditionOutcome getMatchOutcome(ConditionContext var1, AnnotatedTypeMetadata var2);
}

SpringBootCondition实现的matches方法依赖于一个抽象方法this.getMatchOutcome(context, metadata),我们在它的子类OnClassCondition中可以找到这个方法的具体实现。

public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {ClassLoader classLoader = context.getClassLoader();ConditionMessage matchMessage = ConditionMessage.empty();// 找出所有ConditionalOnClass注解的属性List onClasses = this.getCandidates(metadata, ConditionalOnClass.class);List onMissingClasses;if(onClasses != null) {// 找出不在类路径中的类onMissingClasses = this.getMatches(onClasses, OnClassCondition.MatchType.MISSING, classLoader);// 如果存在不在类路径中的类,匹配失败if(!onMissingClasses.isEmpty()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class, new Object[0]).didNotFind("required class", "required classes").items(Style.QUOTE, onMissingClasses));}matchMessage = matchMessage.andCondition(ConditionalOnClass.class, new Object[0]).found("required class", "required classes").items(Style.QUOTE, this.getMatches(onClasses, OnClassCondition.MatchType.PRESENT, classLoader));}// 接着找出所有ConditionalOnMissingClass注解的属性// 它与ConditionalOnClass注解的含义正好相反,所以以下逻辑也与上面相反onMissingClasses = this.getCandidates(metadata, ConditionalOnMissingClass.class);if(onMissingClasses != null) {List present = this.getMatches(onMissingClasses, OnClassCondition.MatchType.PRESENT, classLoader);if(!present.isEmpty()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class, new Object[0]).found("unwanted class", "unwanted classes").items(Style.QUOTE, present));}matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class, new Object[0]).didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE, this.getMatches(onMissingClasses, OnClassCondition.MatchType.MISSING, classLoader));}return ConditionOutcome.match(matchMessage);
}
// 获得所有annotationType注解的属性
private List<String> getCandidates(AnnotatedTypeMetadata metadata, Class<?> annotationType) {MultiValueMap attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true);ArrayList candidates = new ArrayList();if(attributes == null) {return Collections.emptyList();} else {this.addAll(candidates, (List)attributes.get("value"));this.addAll(candidates, (List)attributes.get("name"));return candidates;}
}
private void addAll(List<String> list, List<Object> itemsToAdd) {if(itemsToAdd != null) {Iterator var3 = itemsToAdd.iterator();while(var3.hasNext()) {Object item = var3.next();Collections.addAll(list, (String[])((String[])item));}}
}    
// 根据matchType.matches方法来进行匹配
private List<String> getMatches(Collection<String> candidates, OnClassCondition.MatchType matchType, ClassLoader classLoader) {ArrayList matches = new ArrayList(candidates.size());Iterator var5 = candidates.iterator();while(var5.hasNext()) {String candidate = (String)var5.next();if(matchType.matches(candidate, classLoader)) {matches.add(candidate);}}return matches;
}

关于match的具体实现在MatchType中,它是一个枚举类,提供了PRESENT和MISSING两种实现,前者返回类路径中是否存在该类,后者相反。

private static enum MatchType {PRESENT {public boolean matches(String className, ClassLoader classLoader) {return OnClassCondition.MatchType.isPresent(className, classLoader);}},MISSING {public boolean matches(String className, ClassLoader classLoader) {return !OnClassCondition.MatchType.isPresent(className, classLoader);}};private MatchType() {}// 跟我们之前看过的案例一样,都利用了类加载功能来进行判断private static boolean isPresent(String className, ClassLoader classLoader) {if(classLoader == null) {classLoader = ClassUtils.getDefaultClassLoader();}try {forName(className, classLoader);return true;} catch (Throwable var3) {return false;}}private static Class<?> forName(String className, ClassLoader classLoader) throws ClassNotFoundException {return classLoader != null?classLoader.loadClass(className):Class.forName(className);}public abstract boolean matches(String var1, ClassLoader var2);
}

现在终于真相大白,@ConditionalOnClass的含义是指定的类必须存在于类路径下,MongoDataAutoConfiguration类中声明了类路径下必须含有Mongo.class, MongoTemplate.class这两个类,否则该自动配置类不会被加载。

在Spring Boot中到处都有类似的注解,像@ConditionalOnBean(容器中是否有指定的Bean),@ConditionalOnWebApplication(当前工程是否为一个Web工程)等等,它们都只是@Conditional注解的扩展。当你揭开神秘的面纱,去探索本质时,发现其实Spring Boot自动配置的原理就是如此简单,在了解这些知识后,你完全可以自己去实现自定义的自动配置类,然后编写出自定义的starter。

4、总结

SpringBoot 的 自动配置得益于 SpringFramework 强大的支撑,框架早已有很多工具和注解可以自动装配 Bean 。SpringBoot 通过 一个封装,将市面上通用的组件直接写好了配置类。当我们程序去依赖了这些组件的 jar 包后,启动 SpringBoot应用,于是自动加载开始了。

我们也可以定义自己的自动装配组件,依赖之后,Spring直接可以加载我们定义的 starter 。

加载步骤:

1)SpringBoot启动会加载大量的自动配置类
2)我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
3)我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
4)给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这
些属性的值;

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

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

相关文章

.NET Core 3.0稳定版发布

一年一度的 .NET 开发者盛会 .NET Conf 2019 已正式开始了&#xff08;举办时间为 9.23-9.25&#xff09;。大会第一天最重磅也是最激动人心的消息莫过于 .NET Core 3.0 稳定版的发布。.NET 项目管理总监 Scott Hunter 在大会宣布了这则消息&#xff0c;并在主题演讲环节围绕 .…

深入理解 Spring Boot Starters 原理(手写Spring boot Start)

一、Spring Boot Starter诞生原因 Spring Boot Starter是在SpringBoot组件中被提出来的一种概念&#xff0c;stackoverflow上面已经有人概括了这个starter是什么东西&#xff0c;想看完整的回答戳这里 Starter POMs are a set of convenient dependency descriptors that you …

记一次中小公司的研发问题

作者&#xff1a;zollty&#xff0c;资深程序员和架构师&#xff0c;私底下是个爱折腾的技术极客&#xff0c;架构师社区合伙人&#xff01;一、一些不好的现状&#xff0c;及对应的改进方法1、前后端代码绑定在一起&#xff0c;很难维护&#xff0c;前端UI做得太差&#xff0c…

ASP.NET Core 3.0 使用gRPC

一.简介gRPC 是一个由Google开源的&#xff0c;跨语言的&#xff0c;高性能的远程过程调用&#xff08;RPC&#xff09;框架。gRPC使客户端和服务端应用程序可以透明地进行通信&#xff0c;并简化了连接系统的构建。它使用HTTP/2作为通信协议&#xff0c;使用 Protocol Buffers…

Codeforces Round #739 (Div. 3)(AK实况)

Codeforces Round #739 (Div. 3) A. Dislike of Threes 找到第kkk个既不是333的倍数&#xff0c;个位数上也不是333的数&#xff0c;也已预处理然后O(1)O(1)O(1)输出&#xff0c;也可直接forforfor循环暴力。 #include <bits/stdc.h>using namespace std;int main() {/…

利用Helm简化Kubernetes应用部署(2)

目录定义Charts 使用Helm部署Demo Helm常用操作命令 定义Charts回到之前的“charts”目录&#xff0c;我们依次进行解读并进行简单的修改。Chart.yaml配置示例&#xff1a;apiVersion: v1 appVersion: "1.1" description: A demo Helm chart for Kubernetes name:…

linux查看磁盘空间命令

Linux 查看磁盘空间可以使用 df 和 du 命令。 df df 以磁盘分区为单位查看文件系统&#xff0c;可以获取硬盘被占用了多少空间&#xff0c;目前还剩下多少空间等信息。 例如&#xff0c;我们使用df -h命令来查看磁盘信息&#xff0c; -h 选项为根据大小适当显示&#xff1a; …

Visual Studio 2019 16.3 正式发布,支持 .NET Core 3.0

微软正式发布了 Visual Studio 2019 16.3 版本&#xff0c;主要更新内容如下&#xff1a;.NET Core 3.0Visual Studio 版本 16.3 包括对 .NET Core 3.0 的支持。注意&#xff1a;如果使用的是 .NET Core 3.0&#xff0c;则需要使用 Visual Studio 16.3 或更高版本。.NET Core 桌…

【干货】规模化敏捷DevOps四大实践之持续探索CE(中英对照版)

本文翻译来自SAFe DevOps社群帅哥网友贾磊&#xff1a;高级质量经理&敏捷教练 曾就职于外企、国企、大型上市企业等&#xff0c;担任过测试工程师、测试经理、项目经理、敏捷教练、质量总监、高级质量经理等岗位。是一名敏捷变革的爱好者和践行者。爱好网球、羽毛球。正文原…

Spring Cloud——Eureka——架构体系

1、概述 Eureka包括两个端&#xff1a; Eureka Server&#xff1a;注册中心服务端&#xff0c;用于维护和管理注册服务列表。Eureka Client&#xff1a;注册中心客户端&#xff0c;向注册中心注册服务的应用都可以叫做Eureka Client&#xff08;包括Eureka Server本身&#x…

推荐.neter常用优秀开源项目系列之二

.net社区有很多优秀的开源项目&#xff0c;我们今天再推荐12个开源项目&#xff1b;1. Domain-Driven-Design-ExampleDDD 示例 挺不错的。github https://github.com/zkavtaskin/Domain-Driven-Design-Example2.SmartStoreNET开源的电商项目github https://github.com/smartsto…

Zookeeper: Zookeeper架构及FastLeaderElection机制

本文转发自技术世界&#xff0c;原文链接 http://www.jasongj.com/zookeeper/fastleaderelection/ 一、Zookeeper是什么 Zookeeper是一个分布式协调服务&#xff0c;可用于服务发现&#xff0c;分布式锁&#xff0c;分布式领导选举&#xff0c;配置管理等。 这一切的基础&am…

与时俱进 | 博客现已运行在 .NET Core 3.0 及 Azure 上

点击上方蓝字关注“汪宇杰博客”导语9月23日&#xff0c;微软正式发布了 .NET Core 3.0&#xff0c;这个版本具有大量新功能和改进。我也在第一时间将自己的博客网站更新到了 .NET Core 3.0&#xff0c;并且仍然跑在微软智慧云 Azure 国际版的应用服务上。本文总结了我在博客迁…

Zookeeper:基于Zookeeper的分布式锁与领导选举

本文转发自技术世界&#xff0c;原文链接 http://www.jasongj.com/zookeeper/distributedlock/ 1、Zookeeper特点 1.1 Zookeeper节点类型 如上文《Zookeeper架构及FastLeaderElection机制》所述&#xff0c;Zookeeper 提供了一个类似于 Linux 文件系统的树形结构。该树形结构…

Asp.Net Core Mvc Razor之RazorPage

在AspNetCore.Mvc.Razor命名空间中的RazorPage继承RazorPageBase&#xff0c;并定义的属性为&#xff1a;HttpContext Context 表示当前请求执行的HttpContextRazorPageBase定义为抽象类&#xff0c;并继承了接口&#xff1a;IRazorPageIRazorPage接口定义属性如下&#xff1a;…

Spring Cloud——Consul——架构体系

我们知道&#xff0c;Eureka 2.X因遇到问题&#xff0c;已停止研发。Spring Cloud官方建议迁移到Consul或者Zookeeper等其他服务发现中间件。 下面是 Spring Cloud 支持的服务发现软件以及特性对比&#xff1a; 一、Consul 介绍 Consul 是 HashiCorp 公司推出的开源工具&…

ASP.NET Core 3.0 gRPC 双向流

目录ASP.NET Core 3.0 使用gRPCASP.NET Core 3.0 gRPC 双向流ASP.NET Core 3.0 gRPC 认证授权一.前言在前一文 《二. 什么是 gRPC 流gRPC 有四种服务类型&#xff0c;分别是&#xff1a;简单 RPC&#xff08;Unary RPC&#xff09;、服务端流式 RPC &#xff08;Server streami…

开源公司被云厂商“寄生”,咋整?

上周 OSS Capital 召集一些开源公司&#xff0c;组织了一场关于如何面对“云厂商给开源带来的危害”的会议。OSS Capital 是一家风险投资公司&#xff0c;该公司只投开源&#xff0c;其董事会合伙人之一是开源运动的先驱人物 Bruce Perens。网上有一个十分有名的“开源商业化独…

Spring Cloud Config——原理解析

springCloud config项目,用来为分布式的微服务系统中提供集成式外部配置支持,分为客户端和服务端 可以让你把配置放到远程服务器&#xff0c;目前支持本地存储、Git以及Subversion。 spring官方如下介绍: 简而言之: 通过配置服务(Config Server)来为所有的环境和应用提供外部配…

AWS加入.NET Foundation企业赞助商计划

.NET 走向开源&#xff0c;MIT许可协议。 微软为了推动.NET开源社区的发展&#xff0c;2014年联合社区成立了.NET基金会。.NET基金会是一个独立的组织&#xff0c;支持.NET社区和开源&#xff0c;旨在拓宽和加强.NET生态系统和社区。这可以通过多种方式完成&#xff0c;包括项目…