Spring Boot基础

# @SpringBootApplication

  • # @SpringBootConfiguration

    • @Configuration
  • # @EnableAutoConfiguration

  • # @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

# 自动配置

# 依赖管理与版本仲裁

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.8.RELEASE</version>
</parent>
  
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.8.RELEASE</version>
</parent>

如需更换依赖版本号,在当前项目中通过<properties></properties>标签指定即可(maven就近优先原则)

# 自定义组件扫描与场景启动器配置

# @EnableAutoConfiguration

  • # @AutoConfigurationPackage
    • @Import(AutoConfigurationPackages.Registrar.class)

      默认将主程序类所在包及其子包内所有组件扫描进Spring容器;如需扩大包扫描层级范围,需通过@SpringBootApplication(scanBasePackages="com.xxx")进行指定

      // AutoConfigurationPackages.class
      
      /**
      * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing configuration.
      */
      static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
      
      	@Override
      	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      		register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
      	}
      
      	@Override
      	public Set<Object> determineImports(AnnotationMetadata metadata) {
      		return Collections.singleton(new PackageImports(metadata));
      	}
      
      }
      
  • # @Import(EnableAutoConfigurationImportSelector.class)
    public class AutoConfigurationImportSelector implements DeferredImportSelector {
     	@Override
    	public String[] selectImports(AnnotationMetadata metadata) {
      	List<String> Configurations = getCandidateConfigurations(metadata, attributes); // 获取所有可能加入容器的EnableAutoConfiguration
    	}
    
    	// ...
    
    	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        if(!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
        } else {
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                List<String> Configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass()/*此处的类加载器为AppClassLoader*/, gerBeanClassLoader());
                configurations = this.removeDuplicates(configurations);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = this.filter(configurations, autoConfigurationMetadata);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return StringUtils.toStringArray(configurations);
            }
    	} 
    }
    

    # 自动配置核心流程

    // SpringFactoriesLoader.class
    
    public static list<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
      // ...
      Enumeration ex = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); // 从类路径(spring-boot-autoconfigure-xxx-RELEASE)下META-INFO/spring.factories文件中获取org.springframework.boot.autoconfigure.EnableAutoConfiguration所对应的组件(配置类)key值,最终通过反射对配置类中的组件进行实例化
    }
    

# 启动流程

# SpringBoot 1.x
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
  return new SpringApplication(sources).run(args);
}
// SpringApplication.class

public SpringApplication(Object... sources) {
  initialize(sources);
}

// ...

private void initialize(Object[] sources) {
  // SpringApplication.getSpringFactoriesInstances(...):
  // 从类路径(spring-boot-autoconfigure-xxx-RELEASE)下META-INFO/spring.factories文件中获取所有的ApplicationContextInitializer和所有的ApplicationListener;两者都不需要手动加入容器,而只需在classpath:/META-INF/spring.factories中进行配置(ApplicationRunner需手动加入容器)
  // 定位main()方法
}

# 运行流程

# SpringBoot 1.x
// SpringApplication.class

public ConfigurableApplicationContext run(String... args) {
  Stopwatch stopwatch = new Stopwatch();
  stopwatch.start();
  ConfigurableApplicationContext context = null;
  // ...
  SpringApplicationRunListeners listeners = getRunListeners(args);
  listeners.starting(); // 回调所有SpringApplicationRunListener监听器的starting()方法
  
  try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 封装命令行参数
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 1. getOrCreateEnvironment() 2. configureEnvironment(environment, args) 3. listeners.environmentPrepared(environment)
    Banner banner = printBanner(environment);
    
    context = createApplicationContext();
  }
  // ...
  prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 1. context.setEnvironment(environment) 2. postProcessApplicationContext(context) 向容器注册附加组件 3. applyInitializers(context) 依次执行所有ApplicationContextInitializer的initialize(ConfigurableApplicationContext)方法 3. listeners.contextPrepared(context) 4. context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments) 5. load(context, sources.toArray(new Object[sources.size()])) 向ConfigurableApplicationContext加载sources(主类信息) 6. listeners.contextLoaded(context);
  
  refreshContext(context); // 自动扫描、创建、加载容器内所有组件(包括内嵌Servlet容器)
  
  afterRefresh(context, applicationArguments); // 获取容器中所有的ApplicationRunner及CommandLineRunner,并传入args进行回调
  listeners.finished(context, null);
  stopwatch.stop();
  // ...
  return context;
}

# 自定义Starters

xxx-starter只用于pom文件中所需依赖的导入,而前者引入的xxx-starter-autoconfigurer真正负责依赖注入过程

# xxx-starter-autoconfigurer
<dependency>
	<groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>
public class AbcService(...) {
  private AbcProperties properties;
  
  public AbcProperties getAbcProperties() {
   	return properties;
}
  
  // IoC
  public void setAbcProperties(AbcProperties properties) {
    this.properties = properties;
  }
  
  public AbcService() {
    // ...
}
  
// ...
}
@ConfigurationProperties(abc = "abc")
public class AbcProperties {
  private String abc;
  
  // ...
}
@Configuration
@ConditionalOnXXX
@EnableConfigurationProperties(AbcProperties.class)
public class AbcAutoConfiguration {
@Autowired
  private AbcProperties properties;

  @Bean
  public AbcService abcService() {
    AbcService abcService = new AbcService();
    abcService.setAbcProperties(properties);
    return abcService;
  }
}
# classpath:META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xxx.AbcAutoConfiguration

# 自定义配置

# 自定义配置类并与配置文件绑定
  @ConfigurationProperties(prefix = "xxx.yyy")
@Data
@Component
public class XXXConfiguration {
	// 需在配置文件中使用的属性
}
# 使用自定义配置文件所配置属性
// @EnableConfigurationProperties(XXXConfiguration.class) // 因XXXConfiguration已加入容器,故无需再次声明
@Configuration
public class AAAConfiguration {
  @Bean
  public SomeClass someClass(XXXConfiguration config) {
    // 使用config属性(即配置文件中所配置的值)进行进一步属性注入
  }
}

# Web

# WebMvcAutoConfiguration

@Configuration(proxyBeanMethods = false)
// @Configuration // 1.x
@ConditionalOnWebApplication(type = Type.SERVLET)
// @ConditionalOnWebApplication // 1.x
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
// @AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class}) // 1.x
public class WebMvcAutoConfiguration {
  // ...
}

# SpringBootSpring MVC的自动配置

  • # 自动配置了ContentNegotiatingViewResolver以及BeanNameViewResolver
// ContentNegotiatingViewResolver.class
 
@Override
protected void initServletContext(ServletContext context) {
Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncesters(getApplicationContext(), ViewResolver.class).values(); // 获取容器中所有的ViewResolver(并组合)
// ...
}
  • # 静态资源相关配置
    # 前后端耦合架构下静态资源映射规则(SpringBoot 1.x)
    // ResourceProperties.java
    @ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
    public class ResourceProperties implements ResourceLoaderAware {
    // 设置与静态资源相关的参数,以便在配置文件中进行配置
    }
    
    # 静态资源默认映射路径
    // WebMvcAutoConfiguration.java
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
      // ...
      registry.addResourceHandler("/webjars/**")
          			.addResourceLocation("classpath:/META-INF/resources/webjars/"); // 配置webjars静态资源映射
      // ...
      registry.addResourceHandler(staticPathPattern) // 默认为"/**"
          			.addResourceLocation(this.resourceProperties.getStaticLocations) // (在配置文件中指定静态资源路径后)在此配置路径映射:spring.resources.static.locations:"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"
    }
    
    # 欢迎页面默认映射路径
    • 遍历所有静态资源路径,映射第一个index.html

    • # HiddenHttpMethodFilter

      支持RESTful请求

    • # FormatterConverter

      @ConditionalOnProperty(prefix="spring.mvc", name="xxx")

      // WebMvcAutoConfiguration.java
      @Override
      public void addFormatters(FormatterRegistry registry) {
      	ApplicationConversionService.addBeans(registry, this.beanFactory); // 容器中各类GenericConverter/Converter/Formatter/Printer/Parser在ApplicationConversionService中自动配置
      }
      
      • DateFormatter

        SpringBoot 2.x已废弃,==日期格式化工具配置可参考FormattingConversionService==

        • HttpMessageConverters

          处理响应体字符编码;如需自定义消息转换器,可手动在容器中加入HttpMessageConverters (==SpringBoot 2.x中如何手动配置==)

          SpringBoot 2.x中,不再直接配置messageConverters,改为配置messageConvertersProvider

          // WebMvcAutoConfiguration.java
          @Override
          public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
          	this.messageConvertersProvider.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
          }
          
  • # MessageCodesResolver

    定义HTTP状态码生成规则

  • # ConfigurableWebBindingInitializer

    将(非@RequestBody)请求参数封装为POJO

# 自定义Spring MVC配置类

SpringBoot 2.x新增

// 配置视图映射
@Configuration
public class CustomMvcConfiguration extends WebMvcConfigurationSupport {
  @Override
  protected void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/xxx").addViewName("yyy");
  }
    
  @Override
  protected void addResourceHandlers(ViewControllerRegistry registry) {
    // 添加自定义静态资源映射
  }
    
  @Override
  protected void addInterceptors(ViewControllerRegistry registry) {
    // 添加自定义拦截器(需要排除静态资源)
  }
}
# EnableWebMvcConfiguration(内部类WebMvcAutoConfigurationAdapter@Import依赖类)
// DelegatingWebMvcConfiguration.class (EnableWebMvcConfiguration父类,WebMvcConfigurationSupport子类)

private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
  this.configurers.addWebMvcConfigurers(configurers); // 调用容器中所有WebMvcConfigurer实现类的addViewControllers(ViewControllerRegistry)方法
}
}

若在配置类上标注@EnableWebMvc,则将全面接管Spring MVC的自动配置

# ErrorMvcAutoConfiguration

  • DefaultErrorAttributes

    ModelAndView对象(或响应体)传递异常信息:

    public Map<String, Object> getErrorAttributes(RequestAttributes attr, boolean includesStackTrace) {
      Map<String, Object> errorAttributes = new LinkedHashMap<>();
      errorAttributes.put("timestamp", new Date());
      addStatus(errorAttributes, requestAttributes);
      addErrorDetails(errorAttributes, requestAttributes, includesStackTrace);
      addPath(errorAttributes, requestAttributes);
      return errorAttributes;
    }
    

    SpringBoot 2.x中该方法参数为WebRequest的子类WebRequest

  • ErrorPageCustomizer

    void registerErrorPages(ErrorPageRegistry); // 给容器添加错误页面请求映射(默认为/error)
    
  • BasicErrorController

处理ErrorPageCustomizer注册的错误页面请求

  • DefaultErrorViewResolver

    ModelAndView resolveErrorView(HttpServletRequest, HttpStatus, Model); // 解析HTTP状态码和model数据,返回ModelAndView对象
    
    • 给浏览器响应页面:

      调用DefaultErrorViewResolver返回ModelAndView对象或默认的DefaultErrorViewSpelView默认错误页面,对应/error视图)

    • 给其他客户端响应JSON数据:

# 定制错误页面
  • 项目依赖模板引擎:classpath:/templates/error/4xx.html
  • 项目不依赖模板引擎:classpath:/static/error/4xx.html
# 定制错误响应
  • (错误信息完全由前端框架处理)自定义@ExceptionHandler方法,返回JSON数据
  • (错误信息由SpringBoot自行处理)自定义@ExceptionHandler方法,转发至/error请求(或自定义错误页面请求)
  • (如需给浏览器返回后端渲染的错误页面)在request域中自定义"javax.servlet.error.status_code"属性,赋值为错误状态码,即可通过DefaultErrorViewResolver.resolveErrorView(...)方法定位自定义错误页面
    • (扩展JSON属性)重写DefaultErrorAttributes.getErrorAttributes(RequestAttributes)方法

# 嵌入式Servlet容器

  • 配置文件(如配置文件中的server.xxx属性)

    @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
    public class ServerProperties implements EmbeddedServletContainerCustomizer, EnvironmentAware, Ordered {
      // ...
    }
    
    // SpringBoot 2.x中对应WebServerProperties.class
    
  • 配置类(EmbeddedServletContainerCustomizer,或SpringBoot 2.x中的WebServerFactoryCustomizer<ConfigurableWebServerFactory>

# 嵌入式Servlet容器自动配置原理

# SpringBoot 1.x
# EmbeddedServletContainerAutoConfiguration
//@...
@Import(BeanPostProcessorRegistrar.class) // 向容器中导入EmbeddedServletContainerCustomizerBeanPostProcessor,遍历执行容器中所有EmbeddedServletContainerCustomizer.customize(...)方法
public class EmbeddedServletContainerAutoConfiguration {
// 根据容器中Servlet容器工厂类型,初始化特定的XXXEmbeddedServletContainerFactory工厂
}

配置内嵌Servlet容器的基本环境,并最终启动该容器

实现EmbeddedServletContainerFactory接口,在EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer...)方法中对内嵌Servlet容器的基本环境进行配置

# SpringBoot 2.x
# ServletWebServerFactoryAutoConfiguration

或实现ServletWebServerFactory接口,重写WebServer getWebServer(ServletContextInitializer...)方法

# 嵌入式Servlet容器启动原理

  1. SpringApplication.class

    public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
      return new SpringApplication(sources).run(args);
    }
    
    // ...
    
    public ConfigurableApplicationContext run(String... args) {
      // ...
      ConfigurableApplicationContext context = createApplicationContext();
      // ...
      refreshContext(context);
      // ...
    }
    
    // ...
    
    protected ConfigurableApplicationContext createApplicationContext() {
      // 如果需要创建Web容器,则初始化AnnotationConfigEmbeddedWebApplicationContext,否则初始化AnnotationConfigApplicationContext
    }
    
    // ...
    
    private void refreshContext(ConfigurableApplicationContext context) {
      refresh(context); // 调用AbstractApplicationContext.refresh()方法,在其中调用子容器的onRefresh()方法
      // ...
    }
    
  2. EmbeddedWebApplicationContext.class

    @Override
    protected void onRefresh() {
      super.onRefresh();
      try {
        createEmbeddedServletContainer();
      }
      // ...
    }
    
    // ...
    
    private void createEmbeddedServletContainer() {
      // ...
      EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); // 通过内嵌Servlet容器工厂,并借助EmbeddedServletContainerCustomizerBeanPostProcessor后置处理器遍历容器中所有EmbeddedServletContainerCustomizer容器定制器对创建的Servlet容器进行环境配置
      this.embeddedServletContainer containerFactory.getEmbeddedServletContainer(getSelfInitializer()); // 获取Servlet容器
    }
    

# 日志

默认采用Logback作为日志实现

org.springframework.boot:spring-boot-starter-logging

  • ch.qos.logback:logback-classic
    • org.slf4j:slf4j-api
    • ch.qos.logback:logback-core
  • org.apache.logging.log4j:log4j-to-slf4j // 将Log4j替换为Slf4j
    • org.slf4j:slf4j-api
    • org.apache.logging.log4j:log4j-api
  • org.slf4j:jul-to-slf4j // 将JUL替换为Slf4j
    • org.slf4j:slf4j-api
    • SLF4JBrindgeHandler已存在

org.springframework:spring-core

  • spring-jcl (内置jcl-slf4j桥接?)

可在类路径下自定义日志配置文件(如logback-spring.xml),并实现基于不同开发环境的动态日志格式

<springProfile name="staging">
	<!-- <pattern></pattern> -->
</springProfile>

# 数据源

# DataSourceAutoConfiguration

  • DataSourceConfiguration

    自定义数据源类型

@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
  
  @Bean
  public DataSource dataSource(DataSourceProperties properties) {
    return properties.initializeDataSourceBuilder().build(); // 利用反射创建spring.datasource.type对应类型的数据源,并绑定相关属性
  }
}
  • DataSourceInitializer

    监听容器启动,运行特定的SQL脚本

    # SpringBoot 2.x 
    spring:
    	datasource:
    		# ...
    		initialization-mode: always
    		schema:
     			- xxx.sql
    

# 缓存

# Cacheable(声明式缓存,默认缓存key为cacheNames + key

@Cacheable(cacheNames = {"abc"}, key = "#root.methodName + '[' + #id + ']'", condition = "#a0 > 0", unless = "#result == null")
public SomePOJO get(Integer id) {
  // ...
}
  • value/cacheNames属性指定缓存组件名(可将公共组件名单独以@CacheConfig注解方式配置在类上)
  • key属性指定缓存key(默认为方法参数值)
  • 属性值可使用SpEl表达式(如#root.method.name/#root.targetClass/#root.args[0]),若只使用字符串需加上单引号进行区分
  • sync属性指定缓存操作逻辑是否使用synchronized同步代码块
# CacheAutoConfiguration
// ...
@Import(CacheConfigurationImportSelector.class) // 获取所有可能加入容器的缓存配置类
public class CacheAutoConfiguration {
  // ...
}
# SimpleCacheConfiguration

容器启动时默认的缓存配置类为SimpleCacheConfiguration,采用ConcurrentMapCacheManager作为缓存管理器,后者使用ConcurrentHashMap进行存储

@Override
public Cache getCache(String name) {
  Cache cache = this.cacheMap.get(name);
  if (cache == null && this.dynamic) {
    synchronized (this.cacheMap) {
      cache = this.cacheMap.get(name); // 二次获取(?)
      if (cache == null) {
        cache = createCocurrentMapCache(name);
        this.cacheMap.put(name, cache);
      }
    }
  }
  return cache;
}
# @Cacheable方法执行缓存存取流程
  1. CacheManager.getCache(cacheId)

  2. CacheAspectSupport.generateKey(result)借助SimpleKeyGenerator.generate(...)生成缓存key

    可在配置类中注入KeyGenerator实现类,重写Object generate(Object target, Method method, Object... params)方法,配置自定义key生成器

  3. CacheAspectSupport.findInCaches(context, key) -> Cache.lookup(key) 利用生成的缓存key检查缓存

  • (上述步骤没有缓存命中)
    1. 执行目标方法,返回目标数据
    2. Cache.put(key, value)
  • (上述步骤有缓存命中)直接返回该Cache对象
# CachePut

用于实现缓存双写模式解决分布式缓存一致性问题

  1. 执行目标方法,返回目标数据
  2. Cache.put(key, value)
@CachePut(value = "abc", key = "#root.methodName + '[' + #result.id + ']'") // @CachePut在目标方法执行后调用,故可在SpEl表达式中使用#result 
public SomePOJO update(SomePOJO pojo) {
  // ...
}
# CacheEvict

用于实现缓存失效模式解决分布式缓存一致性问题

@CacheEvict(value = "abc", key = "#root.methodName + '[' + #id + ']'"/*, allEntries = true*/) // allEntries属性指定是否清除该缓存组件中所有缓存key(默认为否),beforeInvocation属性指定缓存清除操作是否在方法执行之前进行(默认为否)
public void delete(Integer id) {
  // ...
}

# RedisAutoConfiguration

// RedisAutoConfiguration.class

@Configuration
protected static class RedisConfiguration {
  
  @Bean
  @ConditionalOnMissingBean(name = "redisTemplate")
  public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) throws UnknownHostException {
    RedisTemplate<Object, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);
    return template;
  }
  
  // ...
}
# 自定义JSON序列化器
  • 配置RedisTemplate所使用的序列化器(编程式缓存)

    // SpringBoot 1.x
    
    @Configuration
    public class CustomRedisConfiguration {
      @Bean
      public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) throws UnknownHostException {
        RedisTemplate<Object, SomePOJO> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
        return template;
      }
    }
    
  • 自定义CacheManager(声明式缓存)

    @EnableConfigurationProperties(CacheProperties.class) // 从配置文件中读取spring.cache配置
    @EnableCaching
    @Configuration
    public class CustomRedisCacheConfiguration {
      // 入参自动从容器中获取
      @Bean
      RedisCacheConfiguration redisCacheConfiguration(CacheProperties properties) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer()));
      }
      
      if (properties.getTimeToLive != null) {
        config = config.entryTtl(properties.getTimeToLive);
      }
      if (properties.getKeyPrefix != null) {
        // 缓存key前缀,默认为cacheNames,一般使用默认值
        config = config.prefixKeysWith(properties.getKeyPrefix);
      }
      if (!properties.isCacheNullValues()) {
        // 缓存空值,预防缓存穿透
        config = config.disableCachingNullValues();
      }
      if (!properties.isUseKeyPrefix()) {
        config = config.disableKeyPrefix();
      }
    }
    
    # RedisCacheConfiguration

    采用RedisCacheManager作为缓存管理器,后者使用RedisCache进行缓存读写

    // 默认读写操作不加锁
    protected Object lookup(Object key) {
      byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));
      // ...
    }
    
    public void put(Object key, Object value) {
      // ...
      // cacheWriter.put(...)
    }
    
    // 只有读操作有加锁版本,通过Cacheable注解的sync属性开启
    public synchronized <T> T get(Object key, Callable<T> valueLoader) {
      ValueWrapper result = get(key);
      if (result != null) {
        return (T) result.get();
      }
      valueFromLoader(key, valueLoader);
      put(key, value);
      return value;
    }
    

# AMQP

# RabbitAutoConfiguration

// RabbitAutoConfiguration.class

@Configuration
@ConditionalOnMissingBean(ConnectionFactory.class)
protected static class RedisConfiguration {
  
  @Bean
  public ConnectionFactory connectionFactory(RabbitProperties config) throws Exception {
    RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean();
    
    // 设置factory各种连接属性
    
    CachingConnectionFactory connectionFactory = CachingConnectionFactory(factory.getObject());
    
    // 设置connectionFactory各种连接属性
    
    return connectionFactory;
  }
  
  // ...
  
  @Bean
  @ConditionalOnSingleCandidate(ConnectionFactory.class)
  @ConditionalOnMissingBean(RabbitTemplate.class)
  public RabbitTemplate rabiitTemplate(ConnectionFactory connectionFactory) {
    RabbitTemplate template = new RabbitTemplate(connectionFactory);
    
    // ...
    return template;
  }
  
  // AmqpAdmin
  @Bean
  @ConditionalOnSingleCandidate(ConnectionFactory.class)
  @ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true)
  @ConditionalOnMissingBean(AmqpAdmin.class)
  public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
    return new AmqpAdmin(connectionFactory);
  }
}
# 自定义JSON序列化器
@Configuration
public class CustomRabbitConfiguration {
  @Bean
  public MessageConverter messageConverter() {
    return new Jackson2JsonMessageConverter();
  }
}

# A