精华内容
下载资源
问答
  • Spring Boot面试杀手锏————自动配置原理

    万次阅读 多人点赞 2018-11-07 14:11:15
    引言 不论在工作中,亦或是求职面试...当然,作为Spring Boot的精髓,自动配置原理的工作过程往往只有在“面试”的时候才能用得上,但是如果在工作中你能够深入的理解Spring Boot的自动配置原理,将无往不利。 Spr...

    引言

    不论在工作中,亦或是求职面试,Spring Boot已经成为我们必知必会的技能项。除了某些老旧的政府项目或金融项目持有观望态度外,如今的各行各业都在飞速的拥抱这个已经不是很新的Spring启动框架。

    当然,作为Spring Boot的精髓,自动配置原理的工作过程往往只有在“面试”的时候才能用得上,但是如果在工作中你能够深入的理解Spring Boot的自动配置原理,将无往不利。

    Spring Boot的出现,得益于“习惯优于配置”的理念,没有繁琐的配置、难以集成的内容(大多数流行第三方技术都被集成),这是基于Spring 4.x提供的按条件配置Bean的能力。

    Spring Boot的配置文件

    初识Spring Boot时我们就知道,Spring Boot有一个全局配置文件:application.properties或application.yml。

    我们的各种属性都可以在这个文件中进行配置,最常配置的比如:server.port、logging.level.* 等等,然而我们实际用到的往往只是很少的一部分,那么这些属性是否有据可依呢?答案当然是肯定的,这些属性都可以在官方文档中查找到:

    https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#common-application-properties

    (所以,话又说回来,找资料还得是官方文档,百度出来一大堆,还是稍显业余了一些)

    除了官方文档为我们提供了大量的属性解释,我们也可以使用IDE的相关提示功能,比如IDEA的自动提示,和Eclipse的YEdit插件,都可以很好的对你需要配置的属性进行提示,下图是使用Eclipse的YEdit插件的效果,Eclipse的版本是:STS 4。

     以上,是Spring Boot的配置文件的大致使用方法,其实都是些题外话。

    那么问题来了:这些配置是如何在Spring Boot项目中生效的呢?那么接下来,就需要聚焦本篇博客的主题:自动配置工作原理或者叫实现方式。

    工作原理剖析

    Spring Boot关于自动配置的源码在spring-boot-autoconfigure-x.x.x.x.jar中:

    当然,自动配置原理的相关描述,官方文档貌似是没有提及。不过我们不难猜出,Spring Boot的启动类上有一个@SpringBootApplication注解,这个注解是Spring Boot项目必不可少的注解。那么自动配置原理一定和这个注解有着千丝万缕的联系!

    @EnableAutoConfiguration

     @SpringBootApplication是一个复合注解或派生注解,在@SpringBootApplication中有一个注解@EnableAutoConfiguration,翻译成人话就是开启自动配置,其定义如下:

     而这个注解也是一个派生注解,其中的关键功能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。

    这个spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:

    这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(...)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。

    自动配置生效

    每一个XxxxAutoConfiguration自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:

    @ConditionalOnBean:当容器里有指定的bean的条件下。

    @ConditionalOnMissingBean:当容器里不存在指定bean的条件下。

    @ConditionalOnClass:当类路径下有指定类的条件下。

    @ConditionalOnMissingClass:当类路径下不存在指定类的条件下。

    @ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。

    以ServletWebServerFactoryAutoConfiguration配置类为例,解释一下全局配置文件中的属性如何生效,比如:server.port=8081,是如何生效的(当然不配置也会有默认值,这个默认值来自于org.apache.catalina.startup.Tomcat)。

    在ServletWebServerFactoryAutoConfiguration类上,有一个@EnableConfigurationProperties注解:开启配置属性,而它后面的参数是一个ServerProperties类,这就是习惯优于配置的最终落地点。

    在这个类上,我们看到了一个非常熟悉的注解:@ConfigurationProperties,它的作用就是从配置文件中绑定属性到对应的bean上,而@EnableConfigurationProperties负责导入这个已经绑定了属性的bean到spring容器中(见上面截图)。那么所有其他的和这个类相关的属性都可以在全局配置文件中定义,也就是说,真正“限制”我们可以在全局配置文件中配置哪些属性的类就是这些XxxxProperties类,它与配置文件中定义的prefix关键字开头的一组属性是唯一对应的。

    至此,我们大致可以了解。在全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。

    而诸多的XxxxAutoConfiguration自动配置类,就是Spring容器的JavaConfig形式,作用就是为Spring 容器导入bean,而所有导入的bean所需要的属性都通过xxxxProperties的bean来获得。

    可能到目前为止还是有所疑惑,但面试的时候,其实远远不需要回答的这么具体,你只需要这样回答:

    Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。

    通过一张图标来理解一下这一繁复的流程:

     图片来自于王福强老师的博客:https://afoo.me/posts/2015-07-09-how-spring-boot-works.html 

    总结

    综上是对自动配置原理的讲解。当然,在浏览源码的时候一定要记得不要太过拘泥与代码的实现,而是应该抓住重点脉络。

    一定要记得XxxxProperties类的含义是:封装配置文件中相关属性;XxxxAutoConfiguration类的含义是:自动配置类,目的是给容器中添加组件。

    而其他的主方法启动,则是为了加载这些五花八门的XxxxAutoConfiguration类。

    展开全文
  • Spring Boot自动配置原理浅析

    千次阅读 2020-08-15 11:05:48
    一、什么是Spring Boot的自动配置? Spring Boot的最大的特点就是简化了各种xml配置内容,还记得曾经使用SSM框架时我们在spring-mybatis.xml配置了多少内容吗?数据源、连接池、会话工厂、事务管理···,而现在...

    第一篇:Spring Boot自动配置原理浅析

    第二篇:Spring Boot构造流程浅析

    第三篇:Spring Boot运行流程浅析

    目录

    一、什么是Spring Boot的自动配置?

    二、Spring Boot自动配置核心原理

    三、三大注解

    四、@EnableAutoConfiguration

    五、@Import

    六、@Condition


    一、什么是Spring Boot的自动配置?

    Spring Boot的最大的特点就是简化了各种xml配置内容,还记得曾经使用SSM框架时我们在spring-mybatis.xml配置了多少内容吗?数据源、连接池、会话工厂、事务管理···,而现在Spring Boot告诉你这些都不需要了,一切交给它的自动配置吧!

    所以现在能大概明白什么是Spring Boot的自动配置了吗?简单来说就是用注解来对一些常规的配置做默认配置,简化xml配置内容,使你的项目能够快速运行。

    二、Spring Boot自动配置核心原理

    在分析原理之前先来整体的看一下自动配置的核心原理图,作一个简单的了解。

    简单来说,Spring Boot通过@EnableAutoConfiguration注解开启自动配置,对jar包下的spring.factories文件进行扫描,这个文件中包含了可以进行自动配置的类,当满足@Condition注解指定的条件时,便在依赖的支持下进行实例化,注册到Spring容器中。

    下面我们将浅析Spring Boot自动配置原理的过程。 

    三、三大注解

    在启动类中可以看到@SpringBootApplication注解,它是SpringBoot的核心注解,也是一个组合注解。我们进入这个注解可以看到里面又包含了其它很多注解,但其中@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan三个注解尤为重要。

    @SpringBootConfiguration:继承自Configuration,支持JavaConfig的方式进行配置。

    @EnableAutoConfiguration:本文重点讲解,主要用于开启自动配置。

    @ComponentScan:自动扫描组件,默认扫描该类所在包及其子包下所有带有指定注解的类,将它们自动装配到bean容器中,会被自动装配的注解包括@Controller、@Service、@Component、@Repository等。也可以指定扫描路径。

    四、@EnableAutoConfiguration

    看到这个名字,大家也应该能猜到这个注解的作用,没错,这个注解可以帮助我们自动加载程序所需的默认配置。要想搞清楚自动配置的原理,我们就要拿它开刀。

    继续进入@EnableAutoConfiguration,注意到这两个注解:@AutoConfigurationPackage@Import,这里我们需要关心的是这个@Import注解

     

    五、@Import

    观察@Import(AutoConfigurationImportSelector.class)注解,这里导入了AutoConfigurationImportSelector类。这个类中有一个非常重要的方法——selectImports(),它几乎涵盖了组件自动装配的所有处理逻辑,包括获得候选配置类、配置类去重、排除不需要的配置类、过滤等,最终返回符合条件的自动配置类的全限定名数组。下面源码图中对该方法进行了详尽的注释。

    	@Override
    	public String[] selectImports(AnnotationMetadata annotationMetadata) {
    		//检查自动配置功能是否开启,默认开启
    		if (!isEnabled(annotationMetadata)) {
    			return NO_IMPORTS;
    		}
    		//加载自动配置的元信息
    		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
    				.loadMetadata(this.beanClassLoader);
    		AnnotationAttributes attributes = getAttributes(annotationMetadata);
    		//获取候选配置类
    		List<String> configurations = getCandidateConfigurations(annotationMetadata,
    				attributes);
    		//去掉重复的配置类
    		configurations = removeDuplicates(configurations);
    		//获得注解中被exclude和excludeName排除的类的集合
    		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    		//检查被排除类是否可实例化、是否被自动注册配置所使用,不符合条件则抛出异常
    		checkExcludedClasses(configurations, exclusions);
    		//从候选配置类中去除掉被排除的类
    		configurations.removeAll(exclusions);
    		//过滤
    		configurations = filter(configurations, autoConfigurationMetadata);
    		//将配置类和排除类通过事件传入到监听器中
    		fireAutoConfigurationImportEvents(configurations, exclusions);
    		//最终返回符合条件的自动配置类的全限定名数组
    		return StringUtils.toStringArray(configurations);
    	}

    这里需要关注的的是如何得到候选的配置类,可以看到所有的配置信息通过getCandidateConfigurations()得到,并最终由一个列表保存。我们继续查看getCandidateConfigurations()方法。

    继续进入loadFactoryNames()方法,可以发现一个获取资源的可疑地址:FACTORIES_RESOURCE_LOCATION

    再进入FACTORIES_RESOURCE_LOCATION,发现值为:META-INF/spring.factories

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

    简述以上过程就是:getCandidateConfigurations()方法通过SpringFactoriesLoader加载器加载META-INF/spring.factories文件,首先通过这个文件获取到每个配置类的url,再通过这些url将它们封装成Properties对象,最后解析内容存于Map<String,List<String>>中。

    下面是spring.factories文件的内容格式,根据它我们可以清晰地了解Map<String,List<String>>中都存了些什么。其中Key值为:org.springframework.boot.autoconfigure.EnableAutoConfiguration,Value值为后面的各种XXXAutoConfiguration类。

    最后通过loadFactoryNames传递过来的class名称作为Key从Map中获得该类的配置列表,而这个class名称是什么呢?回到之前的SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader())方法,注意第一个参数,是一个方法,我们进入这个方法,发现返回的是EnableAutoConfiguration.class,这即是我们需要的class名称。

    	/**
    	 * Return the class used by {@link SpringFactoriesLoader} to load configuration
    	 * candidates.
    	 * @return the factory class
    	 */
    	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    		return EnableAutoConfiguration.class;
    	}

    最终,getCandidateConfigurations()方法获取到了候选配置类并存于List中。之所以说是“候选”,是因为它们后续还需要经过一系列的去重、排除、过滤等操作,最终会通过selectImports()方法返回一个自动配置类的全限定名数组。

    六、@Condition

    在上面的步骤中我们得到了一个动配置类的全限定名数组,这些配置类需要在满足@Condition后才能真正的被注册到Spring容器之中。但在Spring Boot项目中我们更多的是看到@Condition注解的衍生注解,如下:

    @ConditionOnBean在容器中有指定Bean的条件下。
    @ConditionalOnMissingBean在容器中没有指定Bean的条件下。
    @ConditionOnClass在classpath类路径下有指定类的条件下。
    @ConditionalOnMissingClass在classpath类路径下没有指定类的条件下。
    @ConditionalOnResource类路径是否有指定的值。
    @ConditionalOnWebApplication在项目是一个Web项目的条件下。
    @ConditionOnProperty在指定的属性有指定的值条件下。

     

    第一篇:Spring Boot自动配置原理浅析

    第二篇:Spring Boot构造流程浅析

    第三篇:Spring Boot运行流程浅析

    展开全文
  • springboot 配置文件以及自动配置

    万次阅读 2018-05-30 19:15:20
    1.springboot 默认配置文件 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SpringBoot使用默认的全局的配置文件,application.properties / ...

    1.springboot 默认配置文件

            SpringBoot使用默认的全局的配置文件,application.properties / application.yml,配置文件名固定。

    2.为什么需要配置文件

            我们知道SpringBoot在底层给我们自动做了一些配置,所以springboot项目不编写配置文件也可以正常运行,但是根据我们的具体开发我们需要修改SpringBoot自动配置的默认值

    3.读取application.yml配置文件

    • Person.java
    @Component
    @ConfigurationProperties(prefix = "person")
    public class Person {
        private String lastName;
        private Integer age;
        private Boolean boss;
        private Date birth;
        private Map<String,Object> maps;
        private List<Object> lists;
        private Dog dog;//此处省略Dog类的bean实现,只有两个简单的属性{name:steven,age:2}
    
        @Override
        public String toString() {
            return "Person [lastName=" + lastName + ", age=" + age + ", boss="
                    + boss + ", birth=" + birth + ", maps=" + maps + ", lists="
                    + lists + ", dog=" + dog + "]";
        }
        //此处省略get set 方法
        ...
    • application.yml
    person:
        lastName: zhansan
        age: 18
        boss: false
        birth: 2017/12/12
        maps: {k1: 12,k2: 12}
        lists:
            - lisi
            - wangwu
        dog:
            name: steven
            age: 2
    
    • Test.java
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class Test{
    
        @Autowired
        Person person;
    
        @Test
        public void contextLoads() {
            System.out.println(person);
        }
    
    }
    
    //Person{lastName='zhansan', age=18, boss=false, birth=Tue Dec 12 00:00:00 CST 2017, 
    //maps={k1=12, k2=12}, lists=[lisi, wangwu], dog=Dog{name='steven', age=2}}
    

            这个简单的demo就完成了,也得到了我们期望的结果,其中最重要的是@ConfigurationProperties,@Component这两个注解。

    • @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
    • @ConfigurationProperties(prefix = "person"):默认从全局配置文件中获取值;
    • prefix = "person":配置文件中哪个(person)下面的所有属性进行一一映射;

    • 只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能; 所有我们为Person类加上@Component注解

    4.自动配置原理

    1. SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration

    SpringBoot启动类
    SpringBootApplication

    2.@EnableAutoConfiguration 作用:利用EnableAutoConfigurationImportSelector给容器中导入一些组件

    EnableAutoConfiguration
        EnableAutoConfigurationImportSelector在1.5中已经弃用,这里我们不深究,看到它的父类AutoConfigurationImportSelector中的selectImports方法
    selectImports
             List configurations = getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置

    3.getCandidateConfigurations()方法调用了SpringFactoriesLoader.loadFactoryNames()方法,下面我们具体来看看这个方法的实现

    getCandidateConfigurations方法
    loadFactoryNames方法
    这里写图片描述
             扫描所有jar包类路径下 META‐INF/spring.factories;
            把扫描到的这些文件的内容包装成properties对象;
            从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中;
            即将 类路径下 META-INF/spring.factories里面配置的所有EnableAutoConfiguration的值加入到了容器中。

    4..META-INF/spring.factories内容

    spring.factories位置

    # Auto Configure
    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,\
    org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
    org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
    org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
    org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
    org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
    org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
    org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
    org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
    org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
    org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
    org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
    org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
    org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
    org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
    org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
    org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
    org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
    org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
    org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
    org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
    org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
    org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
    org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
    org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
    org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
    org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\
    org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
    org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
    org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
    org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
    org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
    org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\
    org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
    org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
    org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
    org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
    org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
    org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

    5.具体分析HttpEncodingAutoConfiguration原理

    @Configuration
    @EnableConfigurationProperties(HttpEncodingProperties.class)
    @ConditionalOnWebApplication
    @ConditionalOnClass(CharacterEncodingFilter.class)
    @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
    public class HttpEncodingAutoConfiguration {
    
        private final HttpEncodingProperties properties;
    
        public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
            this.properties = properties;
        }
    
        @Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取,properties中的个属性是和配置文件绑定的。
        @ConditionalOnMissingBean(CharacterEncodingFilter.class)//容器没有CharacterEncodingFilter组件时生效
        public CharacterEncodingFilter characterEncodingFilter() {
            CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            filter.setEncoding(this.properties.getCharset().name());
            filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
            return filter;
        }
    }
    • @Configuration : 定义配置类。
    • @EnableConfigurationProperties(HttpEncodingProperties.class):启动指定类的ConfigurationProperties功能;
      将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中。
    • `@ConditionalOnWebApplication:判断当前应用是否是web应用,如果是,当前配置类生效
    • @ConditionalOnClass(CharacterEncodingFilter.class)`:判断当前项目有没有这个类,CharacterEncodingFilter:SpringMVC中解决乱码的过滤器。
    • @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)://判断配置文件中是否存在某个配置 spring.http.encoding.enabled;matchIfMissing表示,即使我们配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的;
    • @Conditional注解(Spring注解版),条件注解,通过判断类路径下有没有相应配置的jar包来确定是否加载和自动配置这个类。
    • 所有在配置文件中能配置的属性都是在xxxxProperties类中封装的,配置文件的配置项就可以参照其对应的这个属性类。
    //从配置文件中获取指定的值和bean的属性进行绑定
    @ConfigurationProperties(prefix = "spring.http.encoding")
    public class HttpEncodingProperties {
    
        public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
        ...
    }
    小结:
    1)、SpringBoot启动会加载大量的自动配置类;
    2)、首先检查SpringBoot默认的自动配置类是否满足我们的需求;
    3)、具体查看这个自动配置类中配置了哪些组件;
    4)、给容器中的自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这
    些属性的值;
    5)、xxxxAutoConfigurartion:自动配置类,给容器中添加组件;xxxxProperties:封装配置文件中相关属性;
    

    6.@Conditional派生注解了解

    核心:@Conditional指定的条件成立,才给容器中添加组件,配置类里面的所有内容才生效;即,自动配置类必须在一定的条件下才能生效;

    @ConditionalOnJava:系统的java版本是否符合要求
    @ConditionalOnBean:容器中存在指定Bean;
    @ConditionalOnMissingBean :容器中不存在指定Bean;
    @ConditionalOnExpression :满足SpEL表达式指定
    @ConditionalOnClass :系统中有指定的类
    @ConditionalOnMissingClass: 系统中没有指定的类
    @ConditionalOnSingleCandidate :容器中只有一个指定的Bean,或者这个Bean是首选Bean
    @ConditionalOnProperty:系统中指定的属性是否有指定的值
    @ConditionalOnResource :类路径下是否存在指定资源文件
    @ConditionalOnWebApplication :当前是web环境
    @ConditionalOnNotWebApplication :当前不是web环境

    查看生效的配置类:
    启用 debug=true属性,来查看控制台打印的自动配置报告。

    =========================
    AUTO-CONFIGURATION REPORT
    =========================
    
    
    Positive matches://匹配成功,启用的自动配置类
    -----------------
    
       DispatcherServletAutoConfiguration matched:
          - @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
          - @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition)
    
       DispatcherServletAutoConfiguration.DispatcherServletConfiguration matched:
          - @ConditionalOnClass found required class 'javax.servlet.ServletRegistration'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
          - Default DispatcherServlet did not find dispatcher servlet beans (DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition)
        ...
        ...
        ...
    
    Negative matches://匹配失败,未启用的自动配置类
    -----------------
    
       ActiveMQAutoConfiguration:
          Did not match:
             - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
    
       AopAutoConfiguration:
          Did not match:
             - @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice' (OnClassCondition)
    
        ...
        ...
    
    展开全文
  • 闲谈IPv6-一起玩转IPv6地址自动配置

    万次阅读 多人点赞 2019-03-06 00:15:57
    昨夜梦里惊魂,1997年,安阳市文峰中路老口腔医院门口那个卖冰糖葫芦的老人,他死了。...上周末,我想再写一篇关于IPv6自动配置的文章,但是周日下午疯子开车带我去我和室友暂住了小半年的小屋子里拉行...

    昨夜梦里惊魂,1997年,安阳市文峰中路老口腔医院门口那个卖冰糖葫芦的老人,他死了。1997年我刚上初中,他已经是老人了,我上学路上老是碰到他,却从没有买过他一个冰糖葫芦…现在,他死了,那个卖洗衣膏的人也死了。

    诶,冰糖葫芦诶开口味诶…

    沧海烧成酒,烫胸口,一口口都是愁。狂饮高歌,爽快唱!

    上周末,我想再写一篇关于IPv6自动配置的文章,但是周日下午疯子开车带我去我和室友暂住了小半年的小屋子里拉行李,就没有了时间,也就只能大致理一个思路。在这深夜,我补全剩余的。正好今天晚上盯盘,把这篇文章完成。


    早在2012年,我就开始扯IPv6的自动配置,比方说这篇:
    闲谈IPv6-典型特征的一些技术细节https://blog.csdn.net/dog250/article/details/8169984
    也说明,从那个时候开始,我就已经在写 “IPv6闲谈” 系列文章了。

    我觉得这个要继续下去,而且我也是一个理想味儿十足的现实主义患者,所以,光说不练假把式,于是本文将给大家带来一种不一样的感觉,本文是一个夹杂着形而上说辞的Howto,不伦不类,但可以看透是非。

    我喜欢将所有理论摆平,但是本文中,我将展示一个可以实际操作的过程。

    请阅读。

    IPv6自动配置

    IPv6的地址不好记忆 一直是IPv4卫道士们的一个槽点,然而谁让你去记忆IPv6地址了?

    IPv6庞大到天文数字的地址数量,天生就是为 万物互联 而生的,如若真的每一粒沙子都有一个IPv6地址,试问哪个人可以靠记忆去手敲地址进行配置,就算不靠疲惫的大脑来记忆,如果把这些地址和配置打印在A4的纸上,实际上最大的图书馆都装不下,那么存在计算机存储介质上呢?你试着算算看它们将占据多大的存储空间,以及读取它们需要多久的时间?

    别指望靠人去配置了!随着技术的发展,人是越来越靠不住的,机器才可以。

    于是,IPv6的一个杀手特性引起了人们的注意,即 自动配置!

    来自RFC4862的福音,请先阅读:
    RFC 4862-IPv6 Stateless Address Autoconfigurationhttps://tools.ietf.org/html/rfc4862

    有人说,IPv4不也有DHCP吗?是的,但是它们的本质却是不同的。DHCP需要你去配置 另一个协议的服务 ,即你需要搭建DHCP服务器,你需要DHCP客户端,比如在Windows上,你就要开启dhcp client服务,天啊,这也是个服务!

    换句话说,DHCP是IPv4协议本身之外的东西。而IPv6自动配置却是其内在的东西,它是IPv6协议标准的一部分。一个IPv6终端,只要简单的开机,它就会自动获得IPv6地址。

    广泛点说,IPv4的Zeroconf不也是一种标准吗?zeroconf详见:
    Zero-configuration networkinghttps://en.wikipedia.org/wiki/Zero-configuration_networking
    嗯,看起来是啦,但还是不一定。

    事实上,这种事一直在上演。Anycast这种Trick不也是在IPv4网络被实践了很多年吗?互联网领域内这种没有门槛的算法之外的技术,只有想不到,没有做不到,这不是粒子对撞机。

    XXX古已有之 这种说辞到处可以说,我们早已习惯,但是看实质,那还真的不一样。

    IPv6地址生命周期

    当使能IPv6自动配置时,一个节点在从获得自动配置的地址开始,到地址不再可用的这段时间,成为该地址的一个生命周期,在时间轴上,我将其列如下:

    我们在Linux系统中间隔几秒连续两次查看同一个网卡的同一个自动配置的地址(下图中的自动配置的地址来自于我的常规配置,不必较真儿这是为什么,下面全部讲清楚):
    在这里插入图片描述
    在Prefered和Invalid之间,即Prefered时间已经退到了0,而Invalid时间还有剩余,此时的地址就处在Deprecated状态,该状态的地址可以继续作为目标地址,但是不建议作为源地址,除非它的scope更加合适,详见RFC3484:

    Rule 2: Prefer appropriate scope.
    If Scope(SA) < Scope(SB): If Scope(SA) < Scope(D), then prefer SB
    and otherwise prefer SA. Similarly, if Scope(SB) < Scope(SA): If
    Scope(SB) < Scope(D), then prefer SA and otherwise prefer SB.

    Rule 3: Avoid deprecated addresses.
    The addresses SA and SB have the same scope. If one of the two
    source addresses is “preferred” and one of them is “deprecated” (in
    the RFC 2462 sense), then prefer the one that is “preferred.”

    邻居发现和Anycast

    关于Anycast这个话题,详见:
    闲谈IPv6-Anycast以及在Linux/Win7系统上的Anycast配置https://blog.csdn.net/dog250/article/details/88071601

    如果想要彻底理解IPv6自动配置以及通信的过程,这里有必要联系邻居发现再说一下Anycast。

    IPv6的邻居发现和IPv4的ARP有大不同。

    • IPv4的ARP
      IPv4在发送ARP请求学习目标主机的MAC地址时,收到ARP请求的主机会 顺便学习发送者的MAC地址 ,但是这会引起混乱!特别在防止ARP表项抖动的时候,需要非常复杂的配置或者说Hack!
    • IPv6的邻居发现
      IPv6地址发现将请求和通告严格区分为两个独立的过程。ICMPv6的邻居发现使用override标识位来判断需要不需要覆盖旧的邻居,而不仅仅根据状态时间。

    仔细看RFC4861中的协议格式:
    在这里插入图片描述
    RFC4861中对O位的解释是:

    Override flag. When set, the O-bit indicates that
    the advertisement should override an existing cache
    entry and update the cached link-layer address.
    When it is not set the advertisement will not
    update a cached link-layer address though it will
    update an existing Neighbor Cache entry for which
    no link-layer address is known. It SHOULD NOT be
    set in solicited advertisements for anycast
    addresses and in solicited proxy advertisements.

    It SHOULD be set in other solicited advertisements
    and in unsolicited advertisements.
    注意,邻居请求协议中没有O位!

    明确表明, 针对Anycast地址的邻居请求,不会覆盖已经有的邻居条目 ,这意味着,一个Anycast set中的路由器,谁的通告先到达请求者,请求者就将谁设置为邻居,后面再来的通告不会覆盖已经有的邻居项。

    我们再看看专门对Anycast邻居发现的约束:

    Anycast addresses - Anycast addresses identify one of a set of
    nodes providing an equivalent service, and multiple nodes on
    the same link may be configured to recognize the same anycast
    address. Neighbor Discovery handles anycasts by having nodes
    expect to receive multiple Neighbor Advertisements for the
    same target. All advertisements for anycast addresses are
    tagged as being non-Override advertisements. A non-Override
    advertisement is one that does not update or replace the
    information sent by another advertisement. These
    advertisements are discussed later in the context of Neighbor
    advertisement messages. This invokes specific rules to
    determine which of potentially multiple advertisements should
    be used.

    理解了这个,就可以明白为什么可以把Anycast地址设置位默认网关而不会出现邻居表抖动了。


    我们回顾一下IPv4的ARP。

    ARP其实不算是标准的IP协议,它只能说是IP协议的辅助协议,它位于IP协议之下。比如它通信的时候,不必封装标准IPv4协议头。但是IPv6的邻居发现则不同。

    IPv6的邻居发现封装于ICMPv6报文中,而ICMPv6则必然立于IP协议之上,它是由标准的IPv6协议头封装的,于是,事情就统一了,好一个统一,帅得很!

    也就是说,哪怕发送一个邻居发现报文,即ICMPv6报文,也要封装一个IPv6协议头。但是自动配置之前,主机节点并不知道自己的IP地址,在先有鸡还是先有蛋的困局下,于是链路本地地址就派上了用场。IPv6链路本地地址是一块使能了IPv6协议的网卡与生俱来的!我们知道,IPv6自动配置中,链路本地地址是关键,它不像IPv4时代必须在后来见招拆招般规定一个保留地址段,比如169.254/16用于无地址通信。IPv6扫除了IPv4年代大部分Zeroconf机制存在的必要性!

    既然ICMPv6邻居发现协议也是一个普通的IPv6报文,那么如何解析Anycast地址的MAC呢?

    如果把Anycast地址配置为默认网关,那么在发包到外网时,势必需要解析它的MAC地址,然而Anycast节点是一个Set而不是一台特定的主机,所以,基于 最短度量 ,IPv6采用 先到先得,不覆盖 的原则来解析,即 离请求主机最近的Anycast Set中的路由器肯定最先回复邻居通告 ,然后忽略掉后来的那些更远的通告。这对正向主动的发包过程是很好理解的,但是如果返回路径的包从另一个Anycast Set中的路由器过来呢?

    该路由器R1不是正向数据包出发时经由的那台路由器R0,它要解析主机的MAC地址,于是它发送邻居请求给主机。

    看到这里,我想很多人都会有个疑问,如果路由器R1发送邻居请求,它是Anycast Set的一员,自然也有Anycast地址,那么它的邻居请求被主机H接收到之后,R1的MAC地址会不会冲掉原始的R0邻居项呢?

    Anycast-R0的MAC会不会被Anycast-R1的MAC地址替换?你说呢?

    答案是不会! Why?

    因为关于Anycast,有一个原则,即 Anycast地址不要作为源地址! 因此,在路由器R1发出的针对H的邻居请求的IPv6协议头部中,源地址并不是这个Anycast地址,而是路由器R1的相关网卡的链路本地地址,详情还是要参看RFC3484关于IPv6报文源地址的选择细节,这里只要关注,IPv6邻居发现报文是一个ICMPv6报文,它也是一个普通的IPv6协议封装的报文!

    皮鞋湿而不胖,请接着看。

    临时地址

    IPv6真正标识了网络,这点和IPv4有着本质的不同。

    IPv6路由器管理的是网段,而不是主机。IPv6路由器靠 邻居发现 仅仅管理到满足它 知道这个邻居属于自己的一个网段 这样的程度即可!这点是靠类似IPv6 EUI-64映射机制实现的。

    也就是说,IPv6的主机标识由主机自己来生成和维护! IPv6实现了OSI模型的地址形式,将网络路由器/路由器的寻址和路由器/主机之间的寻址区分了开来:

    • 路由器/路由器寻址:使用路由协议交换转发信息,最长前缀匹配算法寻址
    • 路由器/主机寻址:使用ICMPv6邻居发现协议寻址

    嗯,真正的Internet!

    但这会出现一个问题,且往下看。

    在IPv4网络中,如果一个节点换了IP地址,那基本上没人会猜测到两个IP地址之间的关联,比如我的笔记本电脑在家的时候,它的地址是192.168.1.101,当我把这台电脑带到了一个星巴克,它可能会被分配另一个地址172.16.2.33,没人知道这两个地址标识的是一台设备!

    但是,这在IPv6中却有问题。

    简单起见,我姑且把EUI-64先等同于MAC地址前导16个0组成,比如你的MAC地址是08:00:27:ff:26:e6,那么EUI-64生成的主机标识则是00:00:08:00:27:ff:26:e6,反正你就知道 能通过你的MAC唯一算出你的主机标识 就好了。

    假设我家里的IPv6段是2001:111:222::/64,我的笔记本MAC地址是08:00:27:ff:26:e6,那么我的笔记本电脑的地址就是2001:111:222::00:00:08:00:27:ff:26:e6/64。

    如果我把这台电脑拿到了星巴克,虽然水网段前缀发生了变化,但是由于MAC地址没有变化,因此 新IPv6地址的低64位是不变的!!

    考虑到MAC地址的唯一性以及其和网卡厂商,售卖点等信息的关联性,如果你买这个电脑时恰好用了微信支付或者支付宝,你相当于实名买了这台电脑,那么只要你带着这台电脑并且使用,通过下面的线索就能定位你的隐私:
    接入点搜集的邻居信息–>你的MAC地址–>支付记录以及MAC地址序列号的出货记录–>你是谁。
    或者说,如果有人知道你的MAC地址,他就有可能知道你在什么时间去过什么地方…

    所以说,某些时候,我们不希望使用EUI-64机制来生成主机标识信息,相反我们更希望使用随机一点的值。这就是所谓IPv6临时地址。

    只要你启用了IPv6临时地址,那么当你收到路由器推送下来的前缀信息时,除了根据EUI-64机制生成一个常规的IPv6主机标识并和前缀拼形成一个IPv6地址之外,还会使用随机算法生成一个随机主机标识并和前缀拼接形成一个随机的临时IPv6地址。

    一般而言,这个随机的地址生命周期比较短,比如说一天时间。它一般作为源地址和远程服务器通信,通过你的偏好配置,你可以在通信发生时让协议栈 优选临时地址作为源地址。 详见RFC3484:https://tools.ietf.org/html/rfc3484#section-5 中的rule 7。

    关于临时地址的更多详情,参阅RFC是最标准的做法:
    RFC4941-Privacy Extensions for Stateless Address Autoconfiguration in IPv6https://tools.ietf.org/html/rfc4941

    Howto-Step by Step

    理解了以上IPv6的地址特性,现在进入重头戏。

    这个小节将step-by-step演示IPv6的自动配置是如何玩的。为了简单起见,我的拓扑如下:
    在这里插入图片描述
    下面我要两台机器轮流做路由器和需要被自动配置的节点主机,大家可以同时领略两种不同操作系统配置的不同风味。

    为了系统净化,我特意将两台设备都进行了重置。

    Windows 7作为路由器的配置过程

    首先你要学会netsh的使用,它非常简单和方便。值得一提的是,我没有专门学习netsh,我看完RFC后,配置Windows路由器时,一路help下来的,就成功了。让我感觉这非常人性化,和Cisco命令行很像,秒杀Linux的bash命令行或者iproute2。


    我们首先用它来为 “本地连接 3” 这块网卡添加一个IPv6地址:

    C:\>netsh int ipv6 add addr "本地连接 3"  2007:777:666::555/64
    

    然后效果如下:
    在这里插入图片描述
    就简单add了一个地址,结果出现了这么多地址,应有尽有,我来分别简单解释一下:

    • 手工添加的地址 :配什么就是什么,类似IPv4的手工配置,可作为目标地址用于全局通信
    • EUI-64拼接的地址 :根据RFC3513规范生成的地址,可作为源地址用于全局通信
    • 临时地址 :为了解决RFC4941陈述的问题而随机生成的地址,可作为源地址用于全局通信
    • 本地链路地址 :为实现自动配置而自动生成的Link地址
    • Anycast地址 :依据RFC3513 Reqired Anycast规定而生成的地址

    由于我手工配置的地址是 2007:777:666::555/64 ,那么其网络前缀自然就是 2007:777:666::/64 ,这个也是该Windows机器作为路由器将来要自动配置给其网内主机的地址段。

    说一下最后这个Anycast地址,为什么会生成这个地址,不是说路由器才有这个地址吗?

    是的,当然是只有路由器才有,所以说我已经把Windows机器变成了一台路由器。如何变的呢?非常简单,执行下面的命令即可:

    C:\>netsh int ipv6 set int  "本地连接 3" forwarding=enable
    

    是的,不要去regedit设置注册表了,就着干就行!netsh,不二选择!

    为了让本链路上的主机将自己设置为默认网关,还需要下面的命令:

    C:\>netsh int ipv6 set int  "本地连接 3" advertisedefault=enable
    

    好了,此时此刻,我们的Windows主机已经是一台路由器了,并且按照相关的RFC规范,路由器需要的功能已经应有尽有,最后还差一步,即如何让这个Windows路由器播报自己的前缀从而给发出路由器请求的节点推送网络前缀呢?

    非常简单,只要下面的命令即可:

    C:\>netsh int ipv6 set route 2007:777:666::555/64 "本地连接 3" publish=yes
    

    再次强调,netsh一定要多玩!


    至此,Windows上的配置已经基本完毕。最后看一下“本地连接 3”的总配置:
    在这里插入图片描述

    此时,我的Linux主机的enp0s9网卡要开启了!确认以下的配置是正确的:

    # 接收路由器通告
    net.ipv6.conf.enp0s9.accept_ra = 1
    # 接受通告路由器作为默认网关
    net.ipv6.conf.enp0s9.accept_ra_defrtr = 1
    # 接受前缀通告,这是自动配置之关键
    net.ipv6.conf.enp0s9.accept_ra_pinfo = 1
    

    值得注意的是,上述的配置会在自动配置过程中 被改变

    这主要是由/sbin/NetworkManager搞的。如果一个主机频繁收到不同的路由器通告,很容易出问题,所以最好是自己维护一个状态机,只有在确认当前的自动分配的地址已经超时而Invalid时,才会重新开启接收路由器通告。

    NetworkManager在收到路由器通告后,会将相关网卡的accept_ra等配置参数给禁止掉,事情不容小觑。

    不过我建议,初学者可以先干掉这个NetworkManager,不要被它诡异的行为影响。毕竟这主要是在演示原汁原味的纯正IPv6自动配置,而不是真的要用它。

    好了,在我killall -9 NetworkManager之后,事情明朗了。

    此外,在IPv6中,路由器和主机的角色在配置上是 互斥的 。这点尤其注意。比如,当你将节点配置成路由器时,它将不再接收其它的路由器通告(除非强制配置,但不建议)。

    此时此刻,同时开启抓包!且看:

    [root@localhost ~]# ifconfig enp0s9 up
    

    等待几秒:

    [root@localhost ~]# ip -6 add ls dev enp0s9
    4: enp0s9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qlen 1000
    	# 自动配置的地址,由EUI-64生成并拼接主机标识
        inet6 2007:777:666:0:a00:27ff:fed0:3f5c/64 scope global mngtmpaddr dynamic
        	# 注意这些时间!
           valid_lft 2591963sec preferred_lft 604763sec
        inet6 fe80::a00:27ff:fed0:3f5c/64 scope link
           valid_lft forever preferred_lft forever
    [root@localhost ~]#
    [root@localhost ~]# ip -6 ro ls dev enp0s9
    2007:777:666::/64 proto kernel metric 256 expires 2591890sec
    fe80::/64 proto kernel metric 256
    # 推下来的路由通告中的默认路由指向Windows的本地链路地址!
    default via fe80::5db6:1b75:9d6f:bcbc proto ra metric 1024 expires 1690sec
    

    自动配置完美完成,Linux主机的IPv6地址为:
    2007:777:666:0:a00:27ff:fed0:3f5c/64

    我们来看一下抓包文件,看看发生了什么:
    在这里插入图片描述
    注意那个Router lifetime字段。RFC4861的6.2.3节这样说:

    6.2.3. Router Advertisement Message Content

    A router might want to send Router Advertisements without advertising
    itself as a default router. For instance, a router might advertise
    prefixes for stateless address autoconfiguration while not wishing to
    forward packets. Such a router sets the Router Lifetime field in
    outgoing advertisements to zero.

    我们实际抓包试试看,比如在disable和enable forwarding开关,仅仅保留publish开关时,看看Windows路由器发送的路由器通告有什么区别:
    在这里插入图片描述

    就这么点点不同而已,说明了Windows确实遵循了RFC规范。

    自动配置完毕,来发个包试试看呗。

    Linux主机不是接受Windows路由作为默认网关了吗:

    ::/0  fe80::5db6:1b75:9d6f:bcbc  UGDAe 1024 0     0 enp0s9
    

    下一跳是Windows路由器的本地链路地址。如果这是IPv4,在通信时,通信源地址根据和网关的最长前缀匹配原则,肯定会选择为enp0s9的本地链路地址,但是IPv6却不是这样,它严格按照RFC3484来选择源地址,我们来试试看:

    [root@localhost ~]# telnet 3333:2222:1111::123 80
    Trying 3333:2222:1111::123...
    

    抓包截图:
    在这里插入图片描述
    命中RFC3484第5节的rule 5:

    Rule 5: Prefer outgoing interface.


    现在让我们来总结一下。

    1. 两台机器,一台Windows作为路由器,另一台Linux作为主机,准备开始演示IPv6自动配置。
    2. 将Windows配置成路由器,并且添加一个IPv6全局地址,最终根据相关RFC规范,生成了一堆地址。
    3. 开启Windows路由器的路由器通告前缀选项。Windows路由器配置完成。
    4. 开启Linux主机enp0s3网卡,通过抓包观察自动配置的过程。
    5. Linux主机发起一个TCPv6连接请求,测试默认路由功能,同时验证源地址选择原则。
    6. 关于临时地址的使用,我并没有讲,开启 net.ipv6.conf.enp0s9.use_tempaddr 试试?

    全程配置使用netsh这个非常好用的Windows工具。

    CentOS Linux作为路由器的配置过程

    接下来该对换角色了。

    这次Linux作为路由器,而Windows作为主机。为此,我重置了两台机器的既有配置。切记,一定要关闭Windows的路由器转发以及路由器通告功能,它才能作为普通主机接收自动配置。


    现在开始配置Linux路由器。这个很简单,打开forwarding即可:

    [root@localhost ~]# sysctl -w net.ipv6.conf.all.forwarding=1
    net.ipv6.conf.all.forwarding = 1
    

    开启这个路由转发功能后,Anycast地址自然生成,这个和Windows一样,遵循RFC的规范行事。接下来配置路由器通告。

    在Linux中,这个可以由radvd守护进程完成。如果有人非要抬杠说 既然路由器通告是IPv6自带的一部分,为何不在内核实现,而要专门由一个用户态守护进程支持,这个和DHCP不也一样么? 关于这点,我觉得, 本来就没有什么内核态,用户态之说 由于路由器通告配置比较复杂,拥有很多的参数,而内核态又不被建议被搞复杂,只能做在用户态咯,这都无所谓。如果非要抬杠,给我两天带薪休假时间,我给你做进内核去。


    言归正传,在我们的CentOS上,radvd并不需要编译源码,它比较成熟,直接yum install即可:

    yum install radvd
    

    然后大致看一下manual:

    RADVD(8) RADVD(8)

    NAME
    radvd - router advertisement daemon for IPv6

    按照配置文件的建议,自行大约摸配置即可。或者说,你看一遍radvd.conf的manual就可以玩转了,非常之详细:

    [root@localhost ~]# man radvd.conf
    

    限于篇幅,我也不想复制粘贴,我给出一个我自己的一个配置文件,位于/etc/radvd.conf:

    interface enp0s9
    {
    	AdvSendAdvert on;
    	MinRtrAdvInterval 30;
    	MaxRtrAdvInterval 100; 
    	prefix 2001:dddd:1:0::/64
    	{
    		AdvOnLink on;
    		AdvAutonomous on;
    		AdvRouterAddr off;
    		AdvValidLifetime 120;  # 120秒过期
    		AdvPreferredLifetime 100; # 100秒后不再建议使用
    	};
    	route 2006:222:666::444/128 # 推送这个主机路由
    	{
    	};
    };
    

    下面开始配置过程,我感觉虽然配置上看起来比Windows配置简单,但是并没有Windows的直观。

    先给enp0s9配置一个和radvd通告的前缀一致的IPv6地址:

    [root@localhost ~]# ip -6 a add dev enp0s9 2001:dddd:1:0::123/64
    

    好了,现在开启radvd服务(同时开启tcpdump/wireshark抓包):

    [root@localhost ~]# service radvd start
    Redirecting to /bin/systemctl start radvd.service
    [root@localhost ~]#
    

    OK,这时看看Windows主机接收得如何,show一下address:
    在这里插入图片描述

    完美!该有的地址都有了。再看看默认路由有没有推送下来,必须推下来了的:
    在这里插入图片描述
    对了,还有一条主机路由呢,我在radvd.conf里配置的那个:

    2006:222:666::444/128 via $MEMeme
    

    在这里插入图片描述
    必须有啊!

    我们看看抓包,这一切背后发生了什么?看看Linux的路由器通告就好了:
    在这里插入图片描述

    一切全部浮到了水面上。IPv6的自动配置就是这么玩的。

    Windows VS. Linux

    这次,我竟然用Windows做路由器,拿Linux做靶子。也不奇怪,自从我第一眼看到netsh,我就喜欢它了,它竟然什么东西都能help出来,这一点让我回忆起了我熟悉的Cisco命令行。

    我经常拿netsh和Linux iproute2对对比,我一直倾向于在心理上希望iproute2更胜一筹,但事实上,我觉得netsh玩的更爽!

    所以,在我演示这个IPv6自动配置这么重要的特性的过程中,Windows的netsh让我感觉好舒服。


    好玩吗?我觉得比较好玩,但是游戏结束了。


    后记

    我的感觉,IPv6才是真正的互联互通网络协议,它真的是太方便了!真的是比IPv4方便多了。

    即便是路由器上要手工配置的那个全局地址,那个被写在了DNS节点AAAA里面的地址,它也是有章可循的,它的128比特地址里通过一些比特就知道它所处的路由位置,这些就是天然聚类的收益!

    不过不要指望去靠某个人手工维护这些。

    周末或者下周,我想聊聊关于IPv6编程的一些问题,敬请期待!


    这是一个寂静的雨后之夜,在今天早上,我发了一个朋友圈,关于一个知乎上的问题 为什么东亚人活得这么累 其中有一个回答,我是很赞同的,这个回答是这样的:
    在这里插入图片描述

    然后,这是我的评价:

    我是同意这个地缘学解释的,这也是我的解释。
    我总是拿着家里那个标注地形地貌的地球仪给疯子和小小看,“你们看,小时候老师给我们讲中国地大物博,资源丰富,其实现在看来,那是骗人的。看看美国,欧洲,中南美,大部分都是海拔很低,地形平坦,有丰富植被以及水域覆盖,而我们中国,这种地方仅限于从山东往南,经过浙江,沿海到广东一带,往西止于河南郑州,湖北武汉连线,东北资源可以处于西伯利亚和季风要冲,异常寒冷。这怎么跟欧美比。在地球仪上看,我们和非洲中南部非常类似。。。”

    就是这么简单的道理,资源贫乏,但却令人遗憾地培育了水稻这种作物,因此便在资源匮乏地带养活了海量的人口,造成了一种现象,那就是“不管干什么事,拿人堆就行了”这种劳动密集型观念,而劳动密集观念其实是个“延长劳动时间”等价的,都是线性效应而不是指数效应。唉:-(

    不用扯那些现代经济学术语装逼,再复杂的现象的本质往往道理都是最简单的。动不动就外汇储备什么,扯淡!中国古代人难道就不苦逼吗?不苦逼怎么会有“头悬梁锥子扎屁股,吃得苦中苦成为人上人,愚公移山,精卫填海,铁杵磨针,书中自有BMW”这种苦逼至上价值观?!
    再看看欧洲古代中世纪,落后是落后,但人少,没人干活自然要想一些在中国看来是奇技淫巧的技巧,再往前就是罗马帝国后期了,那时有大量奴隶劳动力,也是拿人堆,但苦逼的是奴隶而不是普通老百姓啊。

    欧洲文明中最苦逼和中国最像的就是罗马打赢迦太基后到屋大维内战结束那一段100多年的时间了,到很快结束了,毕竟高卢,北非,近东环地中海这片资源太丰富了!

    又能怎么样呢?我一直以来都不喜欢知乎,在知乎里面学不到知识,到处都是装逼,抬杠,编那些啰里八嗦的故事,最烦的就是分割线,傻逼一样的存在。好的答案没几个,却让读者浪费了大量的时间,标榜高大上却处处都是垃圾!

    反正都是一样。


    浙江温州皮鞋湿,下雨进水不会胖!

    展开全文
  • SpringBoot自动配置的原理及实现

    万次阅读 多人点赞 2018-11-13 14:22:34
    SpringBoot自动配置的实现原理 SpringBoot的核心就是自动配置自动配置又是基于条件判断来配置Bean。关于自动配置的源码在spring-boot-autoconfigure-2.0.3.RELEASE.jar 回顾配置属性 在通常需要我们在property中...
  • SpringBoot启动会根据条件加载配置
  • spring boot有着丰富的特性,其中的自动配置特性极大的简化了程序开发中的工作(不用写一行XML)。本文我们就来看一下spring boot是如何做到自动配置的。 在开始分析之前,直接编写一个测试案例来体验下在开发项目中...
  • 约定优于配置,这是SpringBoot中的一个很重要特性,此特性让我们可以在几秒中之内完成一个项目的搭建,无需任何配置,本文就通过深入源码的方式来探索下自动配置的实现过程 为什么要自动配置 手动配置很麻烦且容易...
  • Spring Boot自动配置原理

    千次阅读 2018-04-18 22:00:37
    Spring Boot自动配置原理 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。它使用“习惯优于配置”的理念可以让你的项目快速运行部署。使用Spring Boot可以...
  • SpringBoot自定义starter及自动配置

    千次阅读 2019-11-07 09:38:55
    SpringBoot的核心就是自动配置,而支持自动配置的是一个个starter项目。除了官方已有的starter,用户自己也可以根据规则自定义自己的starter项目。 自定义starter条件 自动化配置需满足以下条件: 根据条件检查...
  • Spring Boot面试必问:自动配置原理

    千次阅读 多人点赞 2020-09-04 15:24:07
    Spring Boot自动配置原理 在传统的SSM框架集成中,需要编写大量的XML配置文件,比如集成Mybatis时,需要编写mybatis_config.xml文件,在集成springmvc时,需要编写springmvc.xml文件,这些配置文件十分繁琐,还很...
  • Spring Boot自动配置原理、实战

    万次阅读 2018-11-06 10:57:29
    Spring Boot的自动配置注解是@EnableAutoConfiguration, 从上面的@Import的类可以找到下面自动加载自动配置的映射。 org.springframework.core.io.support.SpringFactoriesLoader.loadFactoryNames(Class&lt...
  • springboot 2.x自动配置原理简单分析

    千次阅读 2019-03-09 20:58:39
    1、什么是自动配置? 在学习SSM开发项目时,我们需要自己引入spring、springMVC以及mybatis的依赖,并且需要使用配置文件或者java config来进行配置,比如配置视图解析器等等组件。但springboot 为我们提供了一种...
  • Springboot 关闭自动配置

    千次阅读 2018-09-19 20:37:27
    springboot通过@SpringBootApplication 下的@EnableAutoConfiguration 实现自动配置,节约了开发者大量时间,但是有可能有些不必要的配置。如果想关闭其中的某一项配置,那应该怎么办呢? 使用@SpringBoot...
  • 【Spring Boot】Spring Boot是如何进行自动配置的?

    千次阅读 多人点赞 2019-06-29 20:07:40
    springboot自动配置 如果你对SpringBoot不甚了解, 或许就会对其Quick & Dirty的做事方式有所顾虑, 是不是AutoConfiguration黑魔法加载了过多没必要的配置啊? 是不是这套框架太简单无法满足需要啊? 不过...
  • 同时SpringBoot的自动配置功能也是真的香,但是使用框架给我们带来方便的同时,也不能忘记了底层的实现,往往一个技术让我们开发更加快捷的同时,也让我们更容易忽略底层的实现,接下来我们就来分析一下,关于...
  • springboot——自动配置

    千次阅读 2018-02-10 14:47:49
    自动配置是什么?什么是自动配置?举例来讲,当你通过@Autowired或@Resource注解,自动注入一个类实例之前,被注入进来的这个类实例需要被spring容器纳管,不然肯定会注入失败。往往我们会在xml通过`bean id="...
  • 狂神说SpringBoot05:自动配置原理

    千次阅读 多人点赞 2020-03-12 12:43:08
    狂神说SpringBoot系列连载课程,通俗易懂,基于SpringBoot2.2.5版本,欢迎各位狂粉转发关注学习。未经作者授权,禁止转载自动配置原理配置文件到底能写什么?怎么写?Spr...
  • 下面将详细说明Spring Boot覆盖自动配置原理、SpringBoot配置Web端口号、数据库连接属性、自定义log4j配置、application.properties自定义键值对、Profile对于生产环境和开发环境的不同配置。1. 覆盖Spring Boot自动...
  • Spring Boot是Spring家族具有划时代意义的一款产品,它发展自Spring Framework却又高于它,这种高于主要表现在其最重要的三大特性,而相较于这三大特性中更为重要的便是Spring Boot的自动配置(AutoConfiguration)...
  • SpringBoot-自定义配置-覆盖自动配置

    千次阅读 2019-10-23 10:00:04
    Spring Boot不是有很牛逼的自动配置吗?而且它的牛叉之一就是自动配置,让工程师从繁琐的,重复的配置中解放出来,为什么还要自定义配置? Maven中的中打开pom.xml增加如下配置: <dependency> <...
  • spring boot自动配置方式整合 spring boot具有许多自动化配置,对于kafka的自动化配置当然也包含在内,基于spring boot自动配置方式整合kafka,需要做以下步骤。 1. 引入kafka的pom依赖包 <!-- ...
  • SpringBoot自动配置原理

    万次阅读 2018-07-07 16:18:36
    SpringBoot可以简化开发的一个主要原因就是采用了默认配置,所谓约定大于配置就是这个意思。在没有自己指定配置的时候使用默认配置的原理大致如下。如有错误,还请指正。 =================== 2019.9.6更新,此篇...
  • Spring Boot(15)——自动配置Validation

    千次阅读 2019-05-18 21:37:34
    自动配置Validation 当应用中的Classpath下存在javax.validation的实现时,Spring Boot的org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration将会自动配置用于validate的...
  • springboot自动配置是如何实现的?

    万次阅读 2019-03-28 17:33:29
    什么是SpringBoot自动配置? ​ springboot的自动配置,指的是springboot会自动将一些配置类的bean注册进ioc容器,我们可以需要的地方使用@autowired或者@resource等注解来使用它。 ​ “自动”的表现形式就是我们...
  • Insight SpringBoot 自动配置DataSource

    千次阅读 2018-12-25 21:45:28
    自动配置DataSource实现为例,分析SpringBoot 的auto-config 机制 自动配置DataSource-向导 SpringBoot 自动配置DataSource,通过 DataSourceAutoConfiguration 实现(约定-XXXAutoConfiguration)。 ...
  • springboot 自动配置 autoConfig 全流程

    千次阅读 2020-03-22 19:26:12
    目录 1.自动配置简介 2.实现方式 3.演示 摘要
  • 一键安装JDK和JRE并自动配置Java环境变量

    万次阅读 热门讨论 2018-05-08 16:27:37
    一键安装JDK和JRE并自动配置Java环境变量 问题描述: 那天装完ctex(CTeX_2.9.2.164),之后在命令行下运行和编译Java文件提示没有Java环境,查看环境变量后发现系统变量 path 被覆盖,上网查询后发现这是ctex的一...
  • 一、springboot的简单运行原理 首先,springboot一定有父项目,就是maven中的pom文件中引入的 <parent> <groupId>org.springframework.boot</groupId> <artifactId>...&l...
  • springboot的自动配置原理/步骤

    万次阅读 多人点赞 2018-09-03 14:39:03
    1、SpringBoot启动的时候加载主配置类(@SpringBootApplication),开启了自动配置功能 @EnableAutoConfiguration。  2、@EnableAutoConfiguration 作用:   利用AutoConfigurationImportSelector给容器中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,764,923
精华内容 1,105,969
关键字:

自动配置