• 01springboot自动装配原理


    Springboot自动装配原理详解

    传统配置

    1)传统ssm整合redis的时候 需要在xml的配置文件中 进行大量的配置Bean

    第一步:加入依赖

    <dependency>
    	<groupId>org.springframework.data</groupId>
    	<artifactId>spring-data-redis</artifactId>
    	<version>2.0.9.RELEASE</version>
    </dependency>
    
    <dependency>
    	<groupId>redis.clients</groupId>
    	<artifactId>jedis</artifactId>
    	<version>2.9.0</version>
    </dependency>
    

    第二步: bean的配置

    beans.xml

        //配置连接池
        <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <property name="minIdle" value="10"></property>
            <property name="maxTotal" value="20"></property>
        </bean>
        
        //配置连接工厂
        <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
            <property name="hostName" value="47.104.128.12"></property>
            <property name="password" value="123456"></property>
            <property name="database" value="0"></property>
            <property name="poolConfig" ref="poolConfig"></property>
        </bean>
    
    
        //配置 redisTemplate 模版类
        <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
            <property name="connectionFactory"  ref="jedisConnectionFactory"/>
            <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!!  -->
            <property name="keySerializer">
                <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
            </property>
            <property name="valueSerializer">
                <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
            </property>
            <property name="hashKeySerializer">
                <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
            </property>
            <property name="hashValueSerializer">
             <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
            </property>
        </bean>
    

    第三步:导入配置

    **@ImportResource(locations = "classpath:beans.xml") 可以导入xml的配置文件

    **

    @SpringBootApplication
    @ImportResource(locations = "classpath:beans.xml")
    @RestController
    public class OpenAutoconfigPrincipleApplication {
    
    	@Autowired
    	private RedisTemplate redisTemplate;
    
    	@RequestMapping("/testRedis")
    	public String testRedis() {
    		redisTemplate.opsForValue().set("smlz","smlz");
    		return "OK";
    	}
    }
    

    2)综上所述 我们发现,若整合redis的时候通过传统的整合,进行了大量的配置,那么我们来看下通过springboot自动装配整合的对比

    Springboot自动装配

    导入依赖:

    <!--redis-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- lettuce pool 缓存连接池 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    

    修改yml配置文件

    spring:
      #redis配置
      redis:
        host: ${redis.host}
        port: ${redis.port}
        password: ${redis.password}
        database: ${redis.database}
        timeout: 50000
        expireTime: 3600000 #单位秒
        lettuce:
          pool:
            max-idle: 50 # 连接池中的最大空闲连接 默认 8
            min-idle: 0 # 连接池中的最小空闲连接 默认 0
            max-active: 100 # 连接池最大连接数(使用负值表示没有限制) 默认 8
            max-wait: 1000 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
    

    直接使用

    下述代码可以不要配置,为了解决保存使用jdk的序列方式才配置的

    	@Bean
    	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)  {
    		RedisTemplate<Object, Object> template = new RedisTemplate<>();
    		template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
    		template.setConnectionFactory(redisConnectionFactory);
    		return template;
    	}
    

    3)传统整合和springboot自动装配 优劣势分析

    一目了然,不需要分析了吧

    Springboot自动装配原理前置技术

    @Import

    没有被spring管理的实体类

    public class UserService {
    
        public void testImport() {
            System.out.println("我是通过@Import导入进来的UserService");
        }
    
        public void testImportSelector() {
            System.out.println("我是通过@Import和ImportSelector导入进来的UserService");
        }
    
        public void testDeferredImportSelector() {
            System.out.println("我是通过@Import和DeferredImportSelector导入进来的UserService");
        }
    
        public void testImportBeanDefinitionRegistrar() {
            System.out.println("我是通过@Import和ImportBeanDefinitionRegistrar导入进来的UserService");
        }
    
    }
    

    @Import注解导入

    @Configuration
    @Import(UserService.class)
    public class SpringConfig {
    
    }
    

    直接使用没有被spring管理的实体类UserService:

    @RestController
    public class TestController {
    
    	//自动注入 userService
    	@Autowired
    	private UserService userService;
    
    	@RequestMapping("testImport")
    	public String testImport() {
    		userService.testImport();
    		return "testImportOk";
    	}	
    
    }
    

    ImportSelector

    @Import导入ImportSelector的实现类

    配置类

    @Configuration
    @Import(value = {TestImportSelector.class})
    public class SpringConfig {
    
    }
    

    TestImportSelector

    public class TestImportSelector implements ImportSelector {
    
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            return new String[]{"com.yoocar.service.UserService"};
        }
    
    }
    

    直接使用没有被spring管理的实体类UserService:

    @RestController
    public class TestController {
    
       //自动注入 userService
       @Autowired
       private UserService userService;
    
       @RequestMapping("testImportSelector")
       public String testImportSelector() {
          userService.testImportSelector();
          return "testImportOk";
       }
    
    }
    

    DeferredImportSelector

    @Order(100)
    public class TestDeferredImportSelector implements DeferredImportSelector {
    
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            return new String[]{"com.yoocar.service.UserService"};
        }
    
    }
    

    该接口有2个特点

    1. 继承该接口的ImportSelector会在最后执行
    2. 如果定义了1一个以上的DeferredImportSelector则使用Order接口来进行排序

    通过@Import导入ImportBeanDefinitionRegistrar 从而进来导入组件

    ImportBeanDefinitionRegistrar

    public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
            //定义一个BeanDefinition
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(UserService.class);
            //把自定义的bean定义导入到容器中
            beanDefinitionRegistry.registerBeanDefinition("userService", rootBeanDefinition);
        }
    
    }
    

    核心代码:

    @Conditional和Condition接口

    spring底层条件装配的原理@Conditional

    标注在类上面,表示该类下面的所有@Bean都会启用配置,也可以标注在方法上面,只是对该方法启用配置。示例如下

    应用要求:比如我有二个组件

    而TestLog是依赖TestAspect的,只有容器中有存在TestAspect组件才会加载TestLog

    public class TestLog {
    }
    
    public class TestAspect {
    }
    
    

    ①:自定义条件组件条件

    public class TestConditional implements Condition {
    
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            //容器中包含Aspect组件才返回Ture
            if(conditionContext.getBeanFactory().containsBean("testAspect")){
                return true;
            }else{
                return false;
            }
        }
    
    }
        
        -------------------------------------该情况下会加载二个组件-------------------------------------------------
    
        @Bean
        public TestAspect testAspect() {
            System.out.println("Aspect组件自动装配到容器中");
            return new TestAspect();
        }
    
    
        @Bean
        @Conditional(value = TestConditional.class)
        public TestLog testLog() {
            System.out.println("Log组件自动装配到容器中");
            return new TestLog();
        }
        
        -------------------------------------二个组件都不会被加载----------------------------------------
        //@Bean
        public TestAspect testAspect() {
            System.out.println("Aspect组件自动装配到容器中");
            return new TestAspect();
        }
    
    
        @Bean
        @Conditional(value = TestConditional.class)
        public TestLog testLog() {
            System.out.println("Log组件自动装配到容器中");
            return new TestLog();
        }
        
        
    

    一些衍生的注解

    • @ConditionalOnClass(RedisOperations.class) (Spring工程中引用了Redis的包 才会构建这个bean)
    • @ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
    • @ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
    • @ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
    • @ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
    • @ConditionalOnNotWebApplication(不是web应用)

    Springboot自动装配原理

    @SpringbootApplication

    自动装配原理分析 从@SpringbootApplication入手分析

    image

    那我们仔细分析

    AutoConfigurationImportSelector

    public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
          ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    
       private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
    
       private static final String[] NO_IMPORTS = {};
    
       private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
    
       private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
    
       private ConfigurableListableBeanFactory beanFactory;
    
       private Environment environment;
    
       private ClassLoader beanClassLoader;
    
       private ResourceLoader resourceLoader;
    
       private ConfigurationClassFilter configurationClassFilter;
    
       @Override
       public String[] selectImports(AnnotationMetadata annotationMetadata) {
          if (!isEnabled(annotationMetadata)) {
             return NO_IMPORTS;
          }
          //关键代码
          AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
          return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
       }
    
      
       protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
          if (!isEnabled(annotationMetadata)) {
             return EMPTY_ENTRY;
          }
          AnnotationAttributes attributes = getAttributes(annotationMetadata);
          //关键代码,去META-INF/spring.factories文件中 查询自动装配的信息
          List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
          //去除重复的配置类,可能存在重复的
          configurations = removeDuplicates(configurations);
          //去除exclusion的
          Set<String> exclusions = getExclusions(annotationMetadata, attributes);
          checkExcludedClasses(configurations, exclusions);
          configurations.removeAll(exclusions);
          //这里就是最后返回出去的configurations
          configurations = getConfigurationClassFilter().filter(configurations);
          //根据maven 导入的启动器过滤出 需要导入的配置类
          fireAutoConfigurationImportEvents(configurations, exclusions);
          return new AutoConfigurationEntry(configurations, exclusions);
       }
    
    }
    

    getCandidateConfigurations

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
      //查询自动装配的信息
       List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
             getBeanClassLoader());
       Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
             + "are using a custom packaging, make sure that file is correct.");
       return configurations;
    }
    
    
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    	String factoryTypeName = factoryType.getName();
    	//查询自动装配的信息
    	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    
    loadSpringFactories
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
       MultiValueMap<String, String> result = cache.get(classLoader);
       if (result != null) {
          return result;
       }
    
       try {
          //查询自动装配的信息
          Enumeration<URL> urls = (classLoader != null ?
                classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
          result = new LinkedMultiValueMap<>();
          while (urls.hasMoreElements()) {
             URL url = urls.nextElement();
             UrlResource resource = new UrlResource(url);
             Properties properties = PropertiesLoaderUtils.loadProperties(resource);
             for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                   result.add(factoryTypeName, factoryImplementationName.trim());
                }
             }
          }
          cache.put(classLoader, result);
          return result;
       }
       catch (IOException ex) {
          throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
       }
    }
    

    可以看下FACTORIES_RESOURCE_LOCATION

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

    image

    RedisAutoConfiguration分析

    导入组件RedisTemplate

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(RedisOperations.class)
    @EnableConfigurationProperties(RedisProperties.class)
    @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
    public class RedisAutoConfiguration {
    
        //导入RedisTemplate
    	@Bean
    	@ConditionalOnMissingBean(name = "redisTemplate")
    	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
    			throws UnknownHostException {
    		RedisTemplate<Object, Object> template = new RedisTemplate<>();
    		template.setConnectionFactory(redisConnectionFactory);
    		return template;
    	}
    
        //导入StringRedisTemplate
    	@Bean
    	@ConditionalOnMissingBean
    	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
    			throws UnknownHostException {
    		StringRedisTemplate template = new StringRedisTemplate();
    		template.setConnectionFactory(redisConnectionFactory);
    		return template;
    	}
    
    }
    
    

    @Configuration(proxyBeanMethods = false)

    proxyBeanMethods = true 或不写,是Full模式

    proxyBeanMethods = false 是lite模式

    不带@Configuration的类叫Lite配置类,详见spring源码解析

    根据注释proxyBeanMethods是为了让使用@Bean注解的方法被代理而实现bean的生命周期的行为。
    1.设置为true,那么直接调用方法获取bean,不会创建新的bean,而是会走bean的生命周期的行为。
    2.设置为false, 那么直接调用方法获取bean,会创建新的bean,且不会走bean的生命周期的行为。

    @ConditionalOnClass(RedisOperations.class)

    这里的关键就是它,在Spring工程中引用了Redis的jar包 才会构建这个bean

    RedisOperations是spring提供的Redis的操作接口,在此不多介绍

    @EnableConfigurationProperties(RedisProperties.class)

    @EnableConfigurationProperties
    /**
     * Enable support for {@link ConfigurationProperties @ConfigurationProperties} annotated
     * beans. {@code @ConfigurationProperties} beans can be registered in the standard way
     * (for example using {@link Bean @Bean} methods) or, for convenience, can be specified
     * directly on this annotation.
     *
     * @author Dave Syer
     * @since 1.0.0
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(EnableConfigurationPropertiesRegistrar.class)
    public @interface EnableConfigurationProperties {
    
       /**
        * The bean name of the configuration properties validator.
        * @since 2.2.0
        */
       String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
    
       /**
        * Convenient way to quickly register
        * {@link ConfigurationProperties @ConfigurationProperties} annotated beans with
        * Spring. Standard Spring Beans will also be scanned regardless of this value.
        * @return {@code @ConfigurationProperties} annotated beans to register
        */
       Class<?>[] value() default {};
    
    }
    

    @EnableConfigurationProperties注解的作用是:使使用 @ConfigurationProperties 注解的类生效

    如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties 配置文件转化的bean。说白了 @EnableConfigurationProperties 相当于把使用 @ConfigurationProperties 的类进行了一次注入。

    EnableConfigurationPropertiesRegistrar
    class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
    
       @Override
       public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
          registerInfrastructureBeans(registry);
          //注册ConfigurationPropertiesBeanRegistrar
          ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
          getTypes(metadata).forEach(beanRegistrar::register);
       }
    
       private Set<Class<?>> getTypes(AnnotationMetadata metadata) {
          return metadata.getAnnotations().stream(EnableConfigurationProperties.class)
                .flatMap((annotation) -> Arrays.stream(annotation.getClassArray(MergedAnnotation.VALUE)))
                .filter((type) -> void.class != type).collect(Collectors.toSet());
       }
    
       @SuppressWarnings("deprecation")
       static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {
          ConfigurationPropertiesBindingPostProcessor.register(registry);
          BoundConfigurationProperties.register(registry);
          ConfigurationBeanFactoryMetadata.register(registry);
       }
    
    }
    
    ConfigurationPropertiesBeanRegistrar

    ConfigurationPropertiesBeanRegistrar用于将@ConfigurationProperties标注的bean注册为一个bean定义,源码如下:

    final class ConfigurationPropertiesBeanRegistrar {
    
       //bean定义注册器
       private final BeanDefinitionRegistry registry;
    
       //IOC容器工厂类
       private final BeanFactory beanFactory;
    
       ConfigurationPropertiesBeanRegistrar(BeanDefinitionRegistry registry) {
          this.registry = registry;
          this.beanFactory = (BeanFactory) this.registry;
       }
    
       //将@ConfigurationProperties注解标注的bean注册到IOC容器
       void register(Class<?> type) {
          MergedAnnotation<ConfigurationProperties> annotation = MergedAnnotations
                .from(type, SearchStrategy.TYPE_HIERARCHY).get(ConfigurationProperties.class);
          //核心代码
          register(type, annotation);
       }
    
       void register(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
          String name = getName(type, annotation);
          if (!containsBeanDefinition(name)) {
             //核心代码
             registerBeanDefinition(name, type, annotation);
          }
       }
    
       private String getName(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
          String prefix = annotation.isPresent() ? annotation.getString("prefix") : "";
          return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName());
       }
    
       private boolean containsBeanDefinition(String name) {
          return containsBeanDefinition(this.beanFactory, name);
       }
    
       private boolean containsBeanDefinition(BeanFactory beanFactory, String name) {
          if (beanFactory instanceof ListableBeanFactory
                && ((ListableBeanFactory) beanFactory).containsBeanDefinition(name)) {
             return true;
          }
          if (beanFactory instanceof HierarchicalBeanFactory) {
             return containsBeanDefinition(((HierarchicalBeanFactory) beanFactory).getParentBeanFactory(), name);
          }
          return false;
       }
    
       private void registerBeanDefinition(String beanName, Class<?> type,
             MergedAnnotation<ConfigurationProperties> annotation) {
          Assert.state(annotation.isPresent(), () -> "No " + ConfigurationProperties.class.getSimpleName()
                + " annotation found on  '" + type.getName() + "'.");
          //核心代码
          this.registry.registerBeanDefinition(beanName, createBeanDefinition(beanName, type));
       }
    
       //根据beanName和class实例创建BeanDefinition
       private BeanDefinition createBeanDefinition(String beanName, Class<?> type) {
          if (BindMethod.forType(type) == BindMethod.VALUE_OBJECT) {
             return new ConfigurationPropertiesValueObjectBeanDefinition(this.beanFactory, beanName, type);
          }
          GenericBeanDefinition definition = new GenericBeanDefinition();
          definition.setBeanClass(type);
          return definition;
       }
    
    }
    
    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;
    
       /**
        * Whether to enable SSL support.
        */
       private boolean ssl;
    
       /**
        * Connection timeout.
        */
       private Duration timeout;
    
       /**
        * Client name to be set on connections with CLIENT SETNAME.
        */
       private String clientName;
    
       private Sentinel sentinel;
    
       private Cluster cluster;
    
       private final Jedis jedis = new Jedis();
    
       private final Lettuce lettuce = new Lettuce();
    
       public int getDatabase() {
          return this.database;
       }
    
       public void setDatabase(int database) {
          this.database = database;
       }
    
       public String getUrl() {
          return this.url;
       }
    
       public void setUrl(String url) {
          this.url = url;
       }
    
       public String getHost() {
          return this.host;
       }
    
       public void setHost(String host) {
          this.host = host;
       }
    
       public String getPassword() {
          return this.password;
       }
    
       public void setPassword(String password) {
          this.password = password;
       }
    
       public int getPort() {
          return this.port;
       }
    
       public void setPort(int port) {
          this.port = port;
       }
    
       public boolean isSsl() {
          return this.ssl;
       }
    
       public void setSsl(boolean ssl) {
          this.ssl = ssl;
       }
    
       public void setTimeout(Duration timeout) {
          this.timeout = timeout;
       }
    
       public Duration getTimeout() {
          return this.timeout;
       }
    
       public String getClientName() {
          return this.clientName;
       }
    
       public void setClientName(String clientName) {
          this.clientName = clientName;
       }
    
       public Sentinel getSentinel() {
          return this.sentinel;
       }
    
       public void setSentinel(Sentinel sentinel) {
          this.sentinel = sentinel;
       }
    
       public Cluster getCluster() {
          return this.cluster;
       }
    
       public void setCluster(Cluster cluster) {
          this.cluster = cluster;
       }
    
       public Jedis getJedis() {
          return this.jedis;
       }
    
       public Lettuce getLettuce() {
          return this.lettuce;
       }
    
       /**
        * Pool properties.
        */
       public static class Pool {
    
          /**
           * Maximum number of "idle" connections in the pool. Use a negative value to
           * indicate an unlimited number of idle connections.
           */
          private int maxIdle = 8;
    
          /**
           * Target for the minimum number of idle connections to maintain in the pool. This
           * setting only has an effect if both it and time between eviction runs are
           * positive.
           */
          private int minIdle = 0;
    
          /**
           * Maximum number of connections that can be allocated by the pool at a given
           * time. Use a negative value for no limit.
           */
          private int maxActive = 8;
    
          /**
           * Maximum amount of time a connection allocation should block before throwing an
           * exception when the pool is exhausted. Use a negative value to block
           * indefinitely.
           */
          private Duration maxWait = Duration.ofMillis(-1);
    
          /**
           * Time between runs of the idle object evictor thread. When positive, the idle
           * object evictor thread starts, otherwise no idle object eviction is performed.
           */
          private Duration timeBetweenEvictionRuns;
    
          public int getMaxIdle() {
             return this.maxIdle;
          }
    
          public void setMaxIdle(int maxIdle) {
             this.maxIdle = maxIdle;
          }
    
          public int getMinIdle() {
             return this.minIdle;
          }
    
          public void setMinIdle(int minIdle) {
             this.minIdle = minIdle;
          }
    
          public int getMaxActive() {
             return this.maxActive;
          }
    
          public void setMaxActive(int maxActive) {
             this.maxActive = maxActive;
          }
    
          public Duration getMaxWait() {
             return this.maxWait;
          }
    
          public void setMaxWait(Duration maxWait) {
             this.maxWait = maxWait;
          }
    
          public Duration getTimeBetweenEvictionRuns() {
             return this.timeBetweenEvictionRuns;
          }
    
          public void setTimeBetweenEvictionRuns(Duration timeBetweenEvictionRuns) {
             this.timeBetweenEvictionRuns = timeBetweenEvictionRuns;
          }
    
       }
    
       /**
        * Cluster properties.
        */
       public static class Cluster {
    
          /**
           * Comma-separated list of "host:port" pairs to bootstrap from. This represents an
           * "initial" list of cluster nodes and is required to have at least one entry.
           */
          private List<String> nodes;
    
          /**
           * Maximum number of redirects to follow when executing commands across the
           * cluster.
           */
          private Integer maxRedirects;
    
          public List<String> getNodes() {
             return this.nodes;
          }
    
          public void setNodes(List<String> nodes) {
             this.nodes = nodes;
          }
    
          public Integer getMaxRedirects() {
             return this.maxRedirects;
          }
    
          public void setMaxRedirects(Integer maxRedirects) {
             this.maxRedirects = maxRedirects;
          }
    
       }
    
       /**
        * Redis sentinel properties.
        */
       public static class Sentinel {
    
          /**
           * Name of the Redis server.
           */
          private String master;
    
          /**
           * Comma-separated list of "host:port" pairs.
           */
          private List<String> nodes;
    
          /**
           * Password for authenticating with sentinel(s).
           */
          private String password;
    
          public String getMaster() {
             return this.master;
          }
    
          public void setMaster(String master) {
             this.master = master;
          }
    
          public List<String> getNodes() {
             return this.nodes;
          }
    
          public void setNodes(List<String> nodes) {
             this.nodes = nodes;
          }
    
          public String getPassword() {
             return this.password;
          }
    
          public void setPassword(String password) {
             this.password = password;
          }
    
       }
    
       /**
        * Jedis client properties.
        */
       public static class Jedis {
    
          /**
           * Jedis pool configuration.
           */
          private Pool pool;
    
          public Pool getPool() {
             return this.pool;
          }
    
          public void setPool(Pool pool) {
             this.pool = pool;
          }
    
       }
    
       /**
        * Lettuce client properties.
        */
       public static class Lettuce {
    
          /**
           * Shutdown timeout.
           */
          private Duration shutdownTimeout = Duration.ofMillis(100);
    
          /**
           * Lettuce pool configuration.
           */
          private Pool pool;
    
          private final Cluster cluster = new Cluster();
    
          public Duration getShutdownTimeout() {
             return this.shutdownTimeout;
          }
    
          public void setShutdownTimeout(Duration shutdownTimeout) {
             this.shutdownTimeout = shutdownTimeout;
          }
    
          public Pool getPool() {
             return this.pool;
          }
    
          public void setPool(Pool pool) {
             this.pool = pool;
          }
    
          public Cluster getCluster() {
             return this.cluster;
          }
    
          public static class Cluster {
    
             private final Refresh refresh = new Refresh();
    
             public Refresh getRefresh() {
                return this.refresh;
             }
    
             public static class Refresh {
    
                /**
                 * Cluster topology refresh period.
                 */
                private Duration period;
    
                /**
                 * Whether adaptive topology refreshing using all available refresh
                 * triggers should be used.
                 */
                private boolean adaptive;
    
                public Duration getPeriod() {
                   return this.period;
                }
    
                public void setPeriod(Duration period) {
                   this.period = period;
                }
    
                public boolean isAdaptive() {
                   return this.adaptive;
                }
    
                public void setAdaptive(boolean adaptive) {
                   this.adaptive = adaptive;
                }
    
             }
    
          }
    
       }
    
    }
    

    @Import({ LettuceConnectionConfiguration.class})

    LettuceConnectionConfiguration
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(RedisClient.class)
    class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
    
       LettuceConnectionConfiguration(RedisProperties properties,
             ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
             ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
          super(properties, sentinelConfigurationProvider, clusterConfigurationProvider);
       }
    
       @Bean(destroyMethod = "shutdown")
       @ConditionalOnMissingBean(ClientResources.class)
       DefaultClientResources lettuceClientResources() {
          return DefaultClientResources.create();
       }
    
    //这里LettuceConnectionFactory就是RedisConnectionFactory的实现类,在注入RedisTemplate的时候需要
       @Bean
       @ConditionalOnMissingBean(RedisConnectionFactory.class)
       LettuceConnectionFactory redisConnectionFactory(
             ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
             ClientResources clientResources) throws UnknownHostException {
          LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources,
                getProperties().getLettuce().getPool());
          return createLettuceConnectionFactory(clientConfig);
       }
    
       private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
          if (getSentinelConfig() != null) {
             return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration);
          }
          if (getClusterConfiguration() != null) {
             return new LettuceConnectionFactory(getClusterConfiguration(), clientConfiguration);
          }
          return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
       }
    
       private LettuceClientConfiguration getLettuceClientConfiguration(
             ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
             ClientResources clientResources, Pool pool) {
          LettuceClientConfigurationBuilder builder = createBuilder(pool);
          applyProperties(builder);
          if (StringUtils.hasText(getProperties().getUrl())) {
             customizeConfigurationFromUrl(builder);
          }
          builder.clientOptions(initializeClientOptionsBuilder().timeoutOptions(TimeoutOptions.enabled()).build());
          builder.clientResources(clientResources);
          builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
          return builder.build();
       }
    
       private LettuceClientConfigurationBuilder createBuilder(Pool pool) {
          if (pool == null) {
             return LettuceClientConfiguration.builder();
          }
          return new PoolBuilderFactory().createBuilder(pool);
       }
    
       private LettuceClientConfigurationBuilder applyProperties(
             LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
          if (getProperties().isSsl()) {
             builder.useSsl();
          }
          if (getProperties().getTimeout() != null) {
             builder.commandTimeout(getProperties().getTimeout());
          }
          if (getProperties().getLettuce() != null) {
             RedisProperties.Lettuce lettuce = getProperties().getLettuce();
             if (lettuce.getShutdownTimeout() != null && !lettuce.getShutdownTimeout().isZero()) {
                builder.shutdownTimeout(getProperties().getLettuce().getShutdownTimeout());
             }
          }
          if (StringUtils.hasText(getProperties().getClientName())) {
             builder.clientName(getProperties().getClientName());
          }
          return builder;
       }
    
       private ClientOptions.Builder initializeClientOptionsBuilder() {
          if (getProperties().getCluster() != null) {
             ClusterClientOptions.Builder builder = ClusterClientOptions.builder();
             Refresh refreshProperties = getProperties().getLettuce().getCluster().getRefresh();
             Builder refreshBuilder = ClusterTopologyRefreshOptions.builder();
             if (refreshProperties.getPeriod() != null) {
                refreshBuilder.enablePeriodicRefresh(refreshProperties.getPeriod());
             }
             if (refreshProperties.isAdaptive()) {
                refreshBuilder.enableAllAdaptiveRefreshTriggers();
             }
             return builder.topologyRefreshOptions(refreshBuilder.build());
          }
          return ClientOptions.builder();
       }
    
       private void customizeConfigurationFromUrl(LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
          ConnectionInfo connectionInfo = parseUrl(getProperties().getUrl());
          if (connectionInfo.isUseSsl()) {
             builder.useSsl();
          }
       }
    
       
       private static class PoolBuilderFactory {
    
          LettuceClientConfigurationBuilder createBuilder(Pool properties) {
             return LettucePoolingClientConfiguration.builder().poolConfig(getPoolConfig(properties));
          }
    
          private GenericObjectPoolConfig<?> getPoolConfig(Pool properties) {
             GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();
             config.setMaxTotal(properties.getMaxActive());
             config.setMaxIdle(properties.getMaxIdle());
             config.setMinIdle(properties.getMinIdle());
             if (properties.getTimeBetweenEvictionRuns() != null) {
                config.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRuns().toMillis());
             }
             if (properties.getMaxWait() != null) {
                config.setMaxWaitMillis(properties.getMaxWait().toMillis());
             }
             return config;
          }
    
       }
    
    }
    

    到这里为止,springboot是如何自动装配redis组件的,已经讲的很清楚了

  • 相关阅读:
    vue 传参动态
    a href="tel" 拨打电话
    vue中rem的转换
    请求接口的封装
    http request 请求拦截器,有token值则配置上token值
    node溢出
    vue菜单切换
    vue的table切换
    vue页面初始化
    [论文笔记] Legacy Application Migration to the Cloud: Practicability and Methodology (SERVICES, 2012)
  • 原文地址:https://www.cnblogs.com/lusaisai/p/15983132.html
Copyright © 2020-2023  润新知