美文网首页Spring Boot 核心技术Springboot整合
[原创]SpringBoot的自动配置原理与自定义SpringB

[原创]SpringBoot的自动配置原理与自定义SpringB

作者: 垃圾简书_吃枣药丸 | 来源:发表于2019-11-02 12:17 被阅读0次

    以SpringBootAdmin的实现方式作为参考

    # 实现的效果:

    • 添加相关的依赖,再在启动类上加上@EnableXXX就可以使用相关的功能了。

    # 原理-SpringBootAdmin为例

    • 在启动类上标记注解@EnableAdminServer
    @EnableAdminServer
    @SpringBootApplication
    public class AdminApplication {
        public static void main(String[] args) {
            SpringApplication.run(AdminApplication.class, args);
        }
    }
    
    • 点进去发现只是导入了一个类AdminServerMarkerConfiguration.class
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AdminServerMarkerConfiguration.class)
    public @interface EnableAdminServer {
    
    }
    
    • 点进这个类AdminServerMarkerConfiguration发现只是向容器中添加了一个bean Marker
    public class AdminServerMarkerConfiguration {
        @Bean
        public Marker adminServerMarker() {
            return new Marker();
        }
    
        public static class Marker {
        }
    }
    

    • 所以这一部分的总结就是:添加@EnableAdminServer注解,向容器中添加一个类型为 Marker的bean

    • 接下来找到AdminServerAutoConfiguration这个类,一般默认SpringBoot的自动配置类都是以XXXAutoConfiguration命名,所以很容易找到,例如RedisAutoConfiguration...
    @Configuration
    @ConditionalOnBean(AdminServerMarkerConfiguration.Marker.class)
    @EnableConfigurationProperties(AdminServerProperties.class)
    @Import({AdminServerWebConfiguration.class})
    public class AdminServerAutoConfiguration {
     @Bean
        @ConditionalOnMissingBean
        public InstanceRegistry instanceRegistry(InstanceRepository instanceRepository,
                                                 InstanceIdGenerator instanceIdGenerator) {
            return new InstanceRegistry(instanceRepository, instanceIdGenerator);
        }
    
        @Bean
        @ConditionalOnMissingBean
        public InstanceIdGenerator instanceIdGenerator() {
            return new HashingInstanceUrlIdGenerator();
        }
    ......
    }
    
    • 解读这个类的注解可发现以下几点:
      1. @Configuration 这是一个配置类,可以看到这个类里面通过@Bean定义了很多bean
      2. @ConditionalOnBean(AdminServerMarkerConfiguration.Marker.class) [重点],判断容器中有这个bean,这就是上一部分,在启动类上添加@EnableAdminServer的作用,当容器中有Marker这个bean时,这个Configuration才生效,才会向容器中添加这个类中定义的bean
      3. @EnableConfigurationProperties(AdminServerProperties.class) 启动配置类AdminServerProperties,将配置类与配置文件绑定
            @ConfigurationProperties("spring.boot.admin")
            public class AdminServerProperties {
              ...
            }
      
      1. @Import({AdminServerWebConfiguration.class}) 导入另外一个配置类,向容器中添加其他bean

    接下来的问题是:如何让Spring在启动的时候扫面到这个配置类呢,通过分析@EnableAutoConfiguration注解的实现可得知:将需要自动配置的类的全路径配置在META-INF/spring.factories,Spring会在启动的时候扫描类路径下所有的META-INF/spring.factories文件

    • 如SpringBootAdmin的配置:
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration,\
      de.codecentric.boot.admin.server.config.AdminServerNotifierAutoConfiguration,\
      de.codecentric.boot.admin.server.config.AdminServerHazelcastAutoConfiguration,\
      de.codecentric.boot.admin.se  rver.config.AdminServerCloudFoundryAutoConfiguration
    
    image.png

    完成~

    # 另外一个例子: 项目中Redis的自动配置

    • SpringBoot项目中使用Redis更加简单,添加Redis依赖即可使用
     <dependency>
                     <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-data-redis</artifactId>
     </dependency>
    

    SpringBoot是如何实现添加了redis依赖就可以使用Redis,RedisTemplate的呢?

    原理:
    • 永远第一步:找到Redis的自动配置类RedisAutoConfiguration
    @Configuration
    @ConditionalOnClass(RedisOperations.class)
    @EnableConfigurationProperties(RedisProperties.class)
    @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
    public class RedisAutoConfiguration {
    
        @Bean
        @ConditionalOnMissingBean(name = "redisTemplate")
        public RedisTemplate<Object, Object> redisTemplate(
                RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
            RedisTemplate<Object, Object> template = new RedisTemplate<>();
            template.setConnectionFactory(redisConnectionFactory);
            return template;
        }
    
        @Bean
        @ConditionalOnMissingBean
        public StringRedisTemplate stringRedisTemplate(
                RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
            StringRedisTemplate template = new StringRedisTemplate();
            template.setConnectionFactory(redisConnectionFactory);
            return template;
        }
    
    }
    
    • @Configuration
    • @ConditionalOnClass(RedisOperations.class) 当类路径下有RedisOperations这个类时,那么很明显,这个类肯定是在添加redis依赖的时候导进来的,现在看一下这个类的路径。
      image.png
      确实是通过redis的起步依赖导进来的,这就是为什么添加了redis的起步依赖,redis就可以直接使用了
    • @EnableConfigurationProperties(RedisProperties.class) 激活配置类RedisProperties
    @ConfigurationProperties(prefix = "spring.redis")
    public class RedisProperties {
    /**
         * Database index used by the connection factory.
         */
        private int database = 0;
    
        /**
         * Connection URL. Overrides host, port, and password. User is ignored. Example:
         * redis://user:password@example.com:6379
         */
        private String url;
    
        /**
         * Redis server host.
         */
        private String host = "localhost";
    
        /**
         * Login password of the redis server.
         */
        private String password;
    
        /**
         * Redis server port.
         */
        private int port = 6379;
            ......
      }
    
    • @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) 导入LettuceConnectionConfigurationJedisConnectionConfiguration,这两个都是redis的客户端。
      那么启用哪一个呢
    • LettuceConnectionConfiguration
    @Configuration
    @ConditionalOnClass(RedisClient.class)
    class LettuceConnectionConfiguration extends RedisConnectionConfiguration {}
    
    • JedisConnectionConfiguration
    @Configuration
    @ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
    class JedisConnectionConfiguration extends RedisConnectionConfiguration {}
    
    • 可以看到当类路径下有响应的客户端时,就启用相应的自动配置类
    image.png
    • 而SpringBoot默认使用的是lettuce


      image.png

    相关文章

      网友评论

        本文标题:[原创]SpringBoot的自动配置原理与自定义SpringB

        本文链接:https://www.haomeiwen.com/subject/xbmcbctx.html