SpringBoot源码笔记分析

SpringBoot源码笔记分析

1.基础

1.配置SpringBoot热部署

1.引入依赖

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

2.配置IDEA

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5rjnO61s-1599114869749)(D:\拉钩笔记\lagouNode\1Stage\springBoot\pic\IDEASetting.png)]

组合键 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: 1
 name: lucy
 hobby: [吃饭,睡觉]
 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 {
		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            //取到了当前application启动类所在的包名  com.youxiu.demo
			register(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);
            // 注册beanDefinition
			registry.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);
                }
 }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zxAMRDEA-1599114869760)(D:\拉钩笔记\lagouNode\1Stage\springBoot\pic\registerBeanDefinitionMap.png)]

(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());
            }	
		}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oEdxiPce-1599114869762)(D:\拉钩笔记\lagouNode\1Stage\springBoot\pic\loadMetadata.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pattvO6p-1599114869763)(D:\拉钩笔记\lagouNode\1Stage\springBoot\pic\spring.factories.png)]

	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.ExtendedBeanInfoFactory
					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);
		}
	}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d5snaWgM-1599114869765)(D:\拉钩笔记\lagouNode\1Stage\springBoot\pic\loadFactoryName.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pLxPBln2-1599114869766)(D:\拉钩笔记\lagouNode\1Stage\springBoot\pic\loadFactoryV.png)]

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=9000
			ApplicationArguments 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):执行Runners
			callRunners(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);
    //执行容器中的ApplicationContextInitializer
		applyInitializers(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 sources
		Set<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();
	}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fmvQJ7S8-1599114869767)(D:\拉钩笔记\lagouNode\1Stage\springBoot\pic\springApplicationLC.png)]

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;
    }

    @Override
    public 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....");
    }

    @Bean
    public 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 {
    @Autowired
    private SimpleBean simpleBean;

	@Test
	public void test(){

        System.out.println(simpleBean);
    }
}

4.国际化配置

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

@Configuration
public class MyLocaleResovle implements LocaleResolver {

    // 自定义 区域解析方式
    @Override
    public 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.9
            String header = httpServletRequest.getHeader("Accept-Language");
            String[] split = header.split(",");
            String[] split1 = split[0].split("-");
            locale = new Locale(split1[0],split1[1]);
        }
        return locale;
    }
    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {

    }
    // 将自定义的MyLocaleResovle重新注册成一个类型为LocaleResolver的bean组件
    @Bean
    public 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 {};
//缓存数据的key
    String key() default "";
//指定缓存数据的key的生成器
    String keyGenerator() default "";
//缓存管理器
    String cacheManager() default "";
//指定缓存解析器
    String cacheResolver() default "";
//指定在符合某条件下,进行数据缓存
    String condition() default "";
//符合条件下,不进行数据缓存
    String unless() default "";
// sync 是否使用异步缓存,默认false
    boolean 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
  @Autowired
    private 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形式
    @Bean
    public 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的序列化方式为json
        template.setDefaultSerializer(jackson2JsonRedisSerializer);


        return template;
    }

//基于注解形式
    @Bean
    public 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值