1.引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency>2.配置IDEA
组合键 Ctrl+shift+alt+’/’ 弹出框
application.properties
application.yaml
1.为什么导入依赖时不需要指定版本 在spring-boot-starter-parent中的
<relativePath>../../spring-boot-dependencies</relativePath>使用 dependencyManagement 进行了版本管理
<dependencyManagement><dependencyManagement>将(@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); } }帮助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); }由SpringApplication对象与 Run两部分组成
SpringApplication.run(DemoApplication.class, args); public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }SpringBoot starter机制 SpringBoot 是由众多starter组成(一系列的自动化配置的start插件) starter是SpringBoot中重要的一部分,理解为可插拔的插件
在resources目录下新建 \META-INF\spring.factories
// 自动配置的类全名.条件=值 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.youxiu.config.MyAutoConfiguration实现了 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@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; }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); }配置文件
#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; }测试代码或者调试程序时,总会做出一些假设,断言就是用于在代码中捕捉这些假设。当要判断一个方法传入的参数时,我们就可以使用断言。
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