SpringBoot源码笔记分析

1.基础

1.配置SpringBoot热部署

1.引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId>
</dependency>

2.配置IDEA

组合键 Ctrl+shift+alt+’/’ 弹出框

2.spring的配置文件

application.properties

application.yaml

2.1application.properties例子

server.port=8081
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.config.additional-location=
spring.config.location=
spring.config.name=application
@Component //将类作为Bean注入到Spring容器中
@ConfigurationProperties(prefix = "person") //将配置文件以person开头的属性注入到该类中
public class Person {private int id;
private String name;
private List hobby;
}

2.2 application.yaml配置文件

person:id: 1name: lucyhobby: [吃饭,睡觉]family: [father,mother]map: {k1: v1,k2: v2}pet: {type: dog,name: 旺财}

2.3.配置文件属性注入


@Component
public class Student {@Value("${person.id}")private int id;@Value("${person.name}")private String name; }
//
@Component
@ConfigurationProperties(prefix = "person")
public class Person {private int id; public void setId(int id) {this.id = id;}
}

2.源码刨析

2.1 依赖管理

1.为什么导入依赖时不需要指定版本
在spring-boot-starter-parent中的

 <relativePath>../../spring-boot-dependencies</relativePath>

使用 dependencyManagement 进行了版本管理

 <dependencyManagement><dependencyManagement>

2.2 @SpringbootDemoApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited //可以被子类继承
@SpringBootConfiguration //该类为配置类
@EnableAutoConfiguration //启动配置功能
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

2.2.1 @SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented@Configuration //配置类
public @interface SpringBootConfiguration {}

2.2.2 @EnableAutoConfiguration

@AutoConfigurationPackage //自动配置包
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

(1)@AutoConfigurationPackage

将(@SpringBootApplication)所在的包及其子包里面的所有组件扫描到Spring容器中

//spring框架的底层注解,它的作用就是给容器中导入某个组件类,
@Import(AutoConfigurationPackages.Registrar.class)  //  默认将主配置类(@SpringBootApplication)所在的包及其子包里面的所有组件扫描到Spring容器中
public @interface AutoConfigurationPackage {}
//==============Registrar方法========================static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {//取到了当前application启动类所在的包名  com.youxiu.demoregister(registry, new PackageImport(metadata).getPackageName()); }}//============register============public static void register(BeanDefinitionRegistry registry, String... packageNames) {if (registry.containsBeanDefinition(BEAN)) {BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));}// 如果不存在该 BEAN ,则创建一个 Bean ,并进行注册else {GenericBeanDefinition beanDefinition = new GenericBeanDefinition();beanDefinition.setBeanClass(BasePackages.class);beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);// 注册beanDefinitionregistry.registerBeanDefinition(BEAN, beanDefinition);}}//============registerBeanDefinition============public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {Map var4 = this.beanDefinitionMap;synchronized(this.beanDefinitionMap) {//加入this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;this.removeManualSingletonName(beanName);}}

(2)@AutoConfigurationImportSelector.class

帮助SpringBoot容器将所有符合条件的额Configuation配置都加载到Ioc(ApplicationContext)中

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {//选择导入public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}//加载 META-INF/spring-autoconfigure-metadata.properties 的配置文件//1. 加载配置文件META-INF/spring-autoconfigure-metadata.properties,从中获取所有支持自动配置类的条件//作用:SpringBoot使用一个Annotation的处理器来收集一些自动装配的条件,那么这些条件可以在META-INF/spring-autoconfigure-metadata.properties进行配置。// SpringBoot会将收集好的@Configuration进行一次过滤进而剔除不满足条件的配置类// 自动配置的类全名.条件=值 下方第二张图AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());} }

自动配置的类全名.条件=值

 protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {// 1. 判断是否开启注解。如未开启,返回空串if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 2. 获得注解的属性AnnotationAttributes attributes = getAttributes(annotationMetadata);// 3. getCandidateConfigurations()用来获取默认支持的自动配置类名列表// spring Boot在启动的时候,使用内部工具类SpringFactoriesLoader,查找classpath上所有jar包中的META-INF/spring.factories,// 找出其中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的属性定义的工厂类名称,// 将这些值作为自动配置类导入到容器中,自动配置类就生效了List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 3.1 //去除重复的配置类,若我们自己写的starter 可能存在重复的configurations = removeDuplicates(configurations);Set<String> exclusions = getExclusions(annotationMetadata, attributes);// 4.1 校验排除类(exclusions指定的类必须是自动配置类,否则抛出异常)checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);// 5. 对所有候选的自动配置类进行筛选,根据项目pom.xml文件中加入的依赖文件筛选出最终符合当前项目运行环境对应的自动配置类//@ConditionalOnClass : 某个class位于类路径上,才会实例化这个Bean。//@ConditionalOnMissingClass : classpath中不存在该类时起效//@ConditionalOnBean : DI容器中存在该类型Bean时起效//@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效//@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效//@ConditionalOnExpression : SpEL表达式结果为true时//@ConditionalOnProperty : 参数设置或者值一致时起效//@ConditionalOnResource : 指定的文件存在时起效//@ConditionalOnJndi : 指定的JNDI存在时起效//@ConditionalOnJava : 指定的Java版本存在时起效//@ConditionalOnWebApplication : Web应用环境下起效//@ConditionalOnNotWebApplication : 非Web应用环境下起效//总结一下判断是否要加载某个类的两种方式://根据spring-autoconfigure-metadata.properties进行判断。//要判断@Conditional是否满足// 如@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })表示需要在类路径中存在SqlSessionFactory.class、SqlSessionFactoryBean.class这两个类才能完成自动注册。configurations = filter(configurations, autoConfigurationMetadata);// 6. 将自动配置导入事件通知监听器//当AutoConfigurationImportSelector过滤完成后会自动加载类路径下Jar包中META-INF/spring.factories文件中 AutoConfigurationImportListener的实现类,// 并触发fireAutoConfigurationImportEvents事件。fireAutoConfigurationImportEvents(configurations, exclusions);// 7. 创建 AutoConfigurationEntry 对象return new AutoConfigurationEntry(configurations, exclusions);}

2.3. run方法

由SpringApplication对象与 Run两部分组成

SpringApplication.run(DemoApplication.class, args);public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);
}

2.3.1 SpringApplication()


public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;//断言判空Assert.notNull(primarySources, "PrimarySources must not be null");//项目启动类 SpringbootDemoApplication.class设置为属性存储起来this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();//设置初始器  //所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,在Spring上下文被刷新之前进行初始化的操作setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 设置监听器(Listener)setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 初始化 mainApplicationClass 属性:用于推断并设置项目main()方法启动的主程序启动类this.mainApplicationClass = deduceMainApplicationClass();}

2.3.1.1setInitializers初始化

 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = getClassLoader();// Use names and ensure unique to protect against duplicates// 加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));//创建spring工厂实例List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;}@SuppressWarnings("unchecked")private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,ClassLoader classLoader, Object[] args, Set<String> names) {List<T> instances = new ArrayList<>(names.size());for (String name : names) {try {Class<?> instanceClass = ClassUtils.forName(name, classLoader);Assert.isAssignable(type, instanceClass);// 获得构造方法Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);// 创建对象T instance = (T) BeanUtils.instantiateClass(constructor, args);// 加入到表instances.add(instance);}catch (Throwable ex) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);}}return instances;}
//  Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//SpringFactoriesLoader.loadFactoryNames(type, classLoader)
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}//1.获取项目springFactory配置文件路径 // jar:file:/D:/Java_code/apache-maven-3.5.2/resp/org/springframework/spring-beans/5.2.2.RELEASE/spring-beans-5.2.2.RELEASE.jar!/META-INF/spring.factories//jar:file:/D:/Java_code/apache-maven-3.5.2/resp/org/springframework/boot/spring-boot-devtools/2.2.2.RELEASE/spring-boot-devtools-2.2.2.RELEASE.jar!/META-INF/spring.factories//2.urls 转换成 resource//3.遍历resource key value封装到了 MultiValueMap<String, String>try {Enumeration<URL> urls = (classLoader != null ?//加载META-INF/spring.factories//org.springframework.beans.BeanInfoFactory=org.springframework.beans.ExtendedBeanInfoFactoryclassLoader.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);}}

2.3.2Run()

 public ConfigurableApplicationContext run(String... args) {// 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;//异常管理Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();// (1)获取并启动监听器SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {// 创建  ApplicationArguments 对象 初始化默认应用参数类// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//(2)项目运行环境Environment的预配置ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体Banner printedBanner = printBanner(environment);// (3)创建Spring容器context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);// (4)Spring容器前置处理//这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。prepareContext(context, environment, listeners, applicationArguments, printedBanner);// (5):刷新容器refreshContext(context);// (6):Spring容器后置处理afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// (7)发出结束执行的事件通知listeners.started(context);// (8):执行RunnerscallRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}

1.createApplicationContext

protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {// 该类型为SERVLET类型,所以会通过反射装载对应的字节码,case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);}}// 创建 ApplicationContext 对象return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}

2.prepareContext

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {//设置容器环境,包括各种变量context.setEnvironment(environment);//设置上下文的 bean 生成器和资源加载器postProcessApplicationContext(context);//执行容器中的ApplicationContextInitializerapplyInitializers(context);listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beans//注册启动参数bean,这里将容器指定的参数封装成bean,注入容器ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// Load the sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[0]));listeners.contextLoaded(context);}

3.refreshContext

protected void refresh(ApplicationContext applicationContext) {// 断言,判断 applicationContext 是 AbstractApplicationContext 的子类Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);// 启动(刷新) AbstractApplicationContext((AbstractApplicationContext) applicationContext).refresh();}

3.自定义Starter

SpringBoot starter机制
SpringBoot 是由众多starter组成(一系列的自动化配置的start插件)
starter是SpringBoot中重要的一部分,理解为可插拔的插件

1.框架段

1.导入依赖

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>2.2.2.RELEASE</version></dependency>
</dependencies>

2.自定义一个Bean

@EnableConfigurationProperties(SimpleBean.class)
@ConfigurationProperties(prefix = "simplebean")
public class SimpleBean {private int id;private String name;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "SimpleBean{" +"id=" + id +", name='" + name + '\'' +'}';}
}

3.自定义自动配置接口

 //@ConditionalOnClass : 某个class位于类路径上,才会实例化这个Bean。//@ConditionalOnMissingClass : classpath中不存在该类时起效//@ConditionalOnBean : DI容器中存在该类型Bean时起效//@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效//@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效//@ConditionalOnExpression : SpEL表达式结果为true时//@ConditionalOnProperty : 参数设置或者值一致时起效//@ConditionalOnResource : 指定的文件存在时起效//@ConditionalOnJndi : 指定的JNDI存在时起效//@ConditionalOnJava : 指定的Java版本存在时起效//@ConditionalOnWebApplication : Web应用环境下起效//@ConditionalOnNotWebApplication : 非Web应用环境下起效
@Configuration
@ConditionalOnClass
public class MyAutoConfiguration {static {System.out.println("MyAutoConfiguration init....");}@Beanpublic SimpleBean simpleBean() {return new SimpleBean();}}

4.配置接口路径

在resources目录下新建 \META-INF\spring.factories

// 自动配置的类全名.条件=值
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.youxiu.config.MyAutoConfiguration

2.使用端

@RunWith(SpringRunner.class)  //测试启动器,并加载spring boot测试注解
@SpringBootTest
class DemoApplicationTests {@Autowiredprivate SimpleBean simpleBean;@Testpublic void test(){System.out.println(simpleBean);}
}

4.国际化配置

实现了 LocaleResolver 接口,重写 resolveLocale()方法。覆盖掉spring组件的LocaleResolver

@Configuration
public class MyLocaleResovle implements LocaleResolver {// 自定义 区域解析方式@Overridepublic Locale resolveLocale(HttpServletRequest httpServletRequest) {// 获取页面手动传递的语言参数值l  : zh_CN  en_US  ''String l = httpServletRequest.getParameter("l");Locale locale = null;if(!StringUtils.isEmpty(l)){// 如果参数不为空,就根据参数值进行手动语言切换String[] s = l.split("_");locale = new Locale(s[0],s[1]);}else {//Accept-Language: zh-CN ,zh;q=0.9String header = httpServletRequest.getHeader("Accept-Language");String[] split = header.split(",");String[] split1 = split[0].split("-");locale = new Locale(split1[0],split1[1]);}return locale;}@Overridepublic void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {}// 将自定义的MyLocaleResovle重新注册成一个类型为LocaleResolver的bean组件@Beanpublic LocaleResolver localeResolver(){return new MyLocaleResovle();}
}
 <a class="btn btn-sm" th:href="@{/toLoginPage(l='zh_CN')}">中文</a><a class="btn btn-sm" th:href="@{/toLoginPage(l='en_US')}">English</a>
login.tip=please sign in
login.username=username
login.password=password
login.rememberme=remember me
login.button=login

5.springBoot的默认缓存

5.1代码

@EnableCaching 全局注解

@EnableCaching // 开启缓存
@SpringBootApplication
public class Springboot04CacheApplication {public static void main(String[] args) {SpringApplication.run(Springboot04CacheApplication.class, args);} }

给方法添加

    //@Cacheable:  将该方法查询结果comment存放在springboot默认缓存中//cacheNames: 起一个缓存命名空间  对应缓存唯一标识// value: 缓存结果  key:默认在只有一个参数的情况下,key值默认就是方法参数值 如果没有参数或者多个参数的情况:simpleKeyGenerate// 查询方法     //指定在 unless 指定在符合某个情况下不进行缓存@Cacheable(cacheNames = "comment",unless = "#result==null")public Comment findCommentById(Integer id){Optional<Comment> byId = commentRepository.findById(id);if(byId.isPresent()){Comment comment = byId.get();return  comment;}return  null;}

5.2源码分析

SprignBoot默认装配的是SimpleCacheConfiguration .使用CacheManager是ConcurrentMapCacheManager,底层是 ConcurrentMap

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {@AliasFor("cacheNames") //指定缓存的名称 二选一String[] value() default {};
//指定缓存的名称二选一@AliasFor("value")String[] cacheNames() default {};
//缓存数据的keyString key() default "";
//指定缓存数据的key的生成器String keyGenerator() default "";
//缓存管理器String cacheManager() default "";
//指定缓存解析器String cacheResolver() default "";
//指定在符合某条件下,进行数据缓存String condition() default "";
//符合条件下,不进行数据缓存String unless() default "";
// sync 是否使用异步缓存,默认falseboolean sync() default false;
}
public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap(16);}

5.3redis缓存

配置文件

#Redis服务连接配置
#Redis服务地址
spring.redis.host=127.0.0.1
#Redis服务器连接端口
spring.redis.port=6379
#Redis服务器连接密码
spring.redis.password=# 对基于注解的Redis缓存数据统一设置有效期为1分钟,单位毫秒
spring.cache.redis.time-to-live=60000
  @Autowiredprivate RedisTemplate redisTemplate;
// 使用API方式进行缓存:先去缓存中查找,缓存中有,直接返回,没有,查询数据库public Comment findCommentById(Integer id){Object o = redisTemplate.opsForValue().get("comment_" + id);if(o!=null){//查询到了数据,直接返回return (Comment) o;}else {//缓存中没有,从数据库查询Optional<Comment> byId = commentRepository.findById(id);if(byId.isPresent()){Comment comment = byId.get();//将查询结果存到缓存中,同时还可以设置有效期为1天redisTemplate.opsForValue().set("comment_" + id,comment,1, TimeUnit.DAYS);return  comment;}}return  null;}

5.3自定义redis缓存序列化

package com.lagou.config;@Configuration
public class RedisConfig {// 自定义一个RedisTemplate//基于 自定义一个RedisTemplate形式@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);// 创建JSON格式序列化对象,对缓存数据的key和value进行转换Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);// 解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);//设置redisTemplate模板API的序列化方式为jsontemplate.setDefaultSerializer(jackson2JsonRedisSerializer);return template;}//基于注解形式@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {// 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换RedisSerializer<String> strSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jacksonSeial =new Jackson2JsonRedisSerializer(Object.class);// 解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jacksonSeial.setObjectMapper(om);// 定制缓存数据序列化方式及时效RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(1)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(strSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSeial)).disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();return cacheManager;}
}

附录

1.Assert:断言机制:

测试代码或者调试程序时,总会做出一些假设,断言就是用于在代码中捕捉这些假设。当要判断一个方法传入的参数时,我们就可以使用断言。

public void isTest(){Assert.notNull(cart);Assert.notEmpty(cart.getCartItems());Assert.notNull(receiver);Assert.notNull(paymentMethod);Assert.notNull(shippingMethod);}

单词

evaluate 评估
Definition定义
Enabled启动
Candidate候选人 ==》getCandidateConfigurations
Duplicate辅助 ==》removeDuplicates

deduce 推断-==》deduceFromClasspath

primary主要,基本–》primarySources

Environment==》configureEnvironment

SpringBoot源码笔记分析相关推荐

  1. TomcatNginx源码笔记分析

    Tomcat 1.访问执行流程 2.tomcat的执行流程 Tomcat的两个重要身份 1)http服务器 2)Tomcat是⼀个Servlet容器 3.tomcat的容器执行流程 当⽤户请求某个UR ...

  2. SpringBoot源码核心源码讲解

    SpringBoot源码主线分析   我们要分析一个框架的源码不可能通过一篇文章就搞定的,本文我们就来分析下SpringBoot源码中的主线流程.先掌握SpringBoot项目启动的核心操作,然后我们 ...

  3. NJ4X源码阅读分析笔记系列(一)——项目整体分析

    NJ4X源码阅读分析笔记系列(一)--项目整体分析 NJ4X是什么 参见NJ4X的官网:http://www.nj4x.com/ Java and .Net interfaces to support ...

  4. NJ4X源码阅读分析笔记系列(三)—— nj4x-ts深入分析

    NJ4X源码阅读分析笔记系列(三)-- nj4x-ts深入分析 一.系统的工作流程图(模块级) 其工作流程如下(以行情获取为例): 应用端向Application Server发起连接 应用服务器调用 ...

  5. SpringBoot源码分析(二)之自动装配demo

    SpringBoot源码分析(二)之自动装配demo 文章目录 SpringBoot源码分析(二)之自动装配demo 前言 一.创建RedissonTemplate的Maven服务 二.创建测试服务 ...

  6. Springboot源码分析第一弹 - 自动装配实现

    Springboot就不用多了吧,解放Java开发双手的神器. 最显著的特点就是,去配置化,自动装配,自动配置.让开发人员只需要注重业务的开发 今天就来了解一下自动装配的源码是怎么实现的 预先准备 直 ...

  7. Kafka 3.0 源码笔记(3)-Kafka 消费者的核心流程源码分析

    文章目录 前言 1. 消费者负载均衡的实现 2. 源码分析 2.1 KafkaConsumer 的初始化 2.2 KafkaConsumer 的消息拉取 2.2.1 消息拉取的准备及入口 2.2.2 ...

  8. SpringBoot源码分析之内置Servlet容器

    原文链接:http://fangjian0423.github.io/2017/05/22/springboot-embedded-servlet-container/ SpringBoot内置了Se ...

  9. springboot源码分析

    快速开发底层原理 SpringBoot核心理念 能够实现帮助开发者快速的整合第三方框架(Spring.Mybatis.hibernate) 原理:Maven依赖封装整合和自定义starter. 完全去 ...

最新文章

  1. CentOS7系统 yum 安装报错
  2. php 堵塞 消息队列,PHP的并发处理
  3. 机器人军团【动态规划】
  4. java利用htmlparser得到网页html内容
  5. Java线程中断机制-如何中断线程
  6. 从0开始架构一个IOS程序 ——04— UITabBarController和自定义TabBar 搭建主页面
  7. C语言和设计模式(责任链模式)
  8. 游戏需求,猎豹启发!揭秘华为Mate 20 X石墨烯膜散热方案出台始末
  9. 安卓自定义控件之设计自己的提示dialog
  10. 全新UI四方聚合支付系统源码/新增USDT提现/最新更新安全升级修复XSS漏洞补单漏洞
  11. Nakashima Propeller选择Voltaire加速船舶螺旋桨设计
  12. 机械类有哪些好投一些的核心期刊?
  13. 易语言雷电模拟器adb模块制作实现一键模拟器多开
  14. OpenCV(二) —— 颜色通道提取 边界填充 数值计算 图像融合
  15. 【noip模拟题】华尔街的秘密
  16. 华为路由器——BGP路由技术详解
  17. 词语语义的相关关系和相似关系量化
  18. 编程之法-C语言应用开发与工程实践-C语言概述
  19. javaweb课程设计网上书店
  20. Android 剩余可用时长的计算公式

热门文章

  1. html5水调歌头代码,张惠言的五首《水调歌头》
  2. Halcon - 测量 - 轮廓到线的距离
  3. 线程池之ScheduledThreadPoolExecutor线程池源码分析笔记
  4. vue 创建项目的命令
  5. 关于实现Runnable接口的类中有公共属性问题
  6. S5P4418/S5P6818核心板EMMC已由原来的4.5版本升级到5.1版本
  7. Android攻城狮Gallery和ImageSwitcher制作图片浏览器
  8. 什么叫「人的格局」?是否有必要培养大的格局或怎么培养?
  9. Oracle 客户端 使用 expdp/impdp 示例 说明
  10. 博客开通了....激动 呵呵