视频地址
https://www.bilibili.com/video/BV19K4y1L7MT
文档地址
https://www.yuque.com/atguigu/springboot/oovrhy
一.基础入门 hello-world 编写项目 1 2 3 4 5 6 7 8 9 10 11 12 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.7.5</version > </parent > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > </dependencies >
1 2 3 4 5 6 7 8 9 package com.xuedongyun;@SpringBootApplication public class MainApplication { public static void main (String[] args) { SpringApplication.run(MainApplication.class, args); } }
1 2 3 4 5 6 7 8 9 10 package com.xuedongyun.controller;@RestController public class HelloController { @RequestMapping("/hello") public String hello () { return "hello world!" ; } }
打包 1 2 3 4 5 6 7 8 <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build >
mvn clear
, mvn package
java -jar ./xxx.jar
(源码)父项目 1 2 3 4 5 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.7.5</version > </parent >
1 2 3 4 5 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > 2.7.5</version > </parent >
1 2 3 4 5 6 7 <properties > <activemq.version > 5.16.5</activemq.version > <antlr2.version > 2.7.7</antlr2.version > <appengine-sdk.version > 1.9.98</appengine-sdk.version > <artemis.version > 2.19.1</artemis.version > ... </properties >
版本仲裁:
父项目声明了常用依赖的版本号,其他依赖无需写版本号(自动版本仲裁) 1 2 3 <properties > <mysql.version > 8.0.21</mysql.version > </properties >
starter引入某种场景的依赖 官方:spring-boot-starter-* 第三方:*-spring-boot-starter 所有spring-boot-starter-*
最基本的依赖都是spring-boot-starter
自动配置 graph LR
web(spring-boot-starter-web)
mvc(spring-webmvc)
springWeb(spring-web)
boot(spring-boot-starter)
tomcat(spring-boot-starter-tomcat)
web --> mvc
web --> springWeb
web --> boot
web --> tomcat 自动配好Tomcat 自动配好SpringMVC 引入SpringMVC开发全套依赖 配置SpringMVC常用功能 返回IOC容器 1 ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
查看IOC容器中的组件 1 String[] names = context.getBeanDefinitionNames();
发现有相关的组件 1 2 3 4 5 dispatcherServlet characterEncodingFilter viewResolver multipartResolver ...
SpringBoot已经配置好了web开发常见场景
默认包扫描规则 主程序所在包,及其子包,都会被自动扫描
也可以指定基本包
1 2 3 4 5 6 7 @SpringBootApplication(scanBasePackages = "com.xuedongyun") @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan("com.xuedongyun")
各种properties配置 默认配置会映射到MultipartProperties类上 配置文件的值最终会绑定到每个类上,类在容器中创建对象 按需加载自动配置项 存在非常多starter 只会开启引入的starter的自动配置 所有的自动配置功能都在spring-boot-autoconfigure
中 graph LR
web(spring-boot-starter-web)
boot(spring-boot-starter)
config(spring-boot-autoconfigure)
web-->boot-->config 1 2 3 4 5 6 7 8 Maven: org.springframework.boot:spring-boot-autoconfigure:2.7.5 spring-boot-autoconfigure-2.7.5.jar org.springframework.boot.autoconfigure aop AopAutoConfiguration netty NettyAutoConfiguration ...
有了什么场景,对应AutoConfig才能生效
容器功能 组件添加 @Configuration 1 2 3 4 5 6 7 8 9 10 11 package com.xuedongyun.config;@Configuration public class MyConfig { @Bean public User user () { return new User ("xdy" , 12 ); } }
组件是单实例的
配置类本身也是组件
proxyBeanMethods:默认true,是否代理Bean的方法代理的话,重复调用方法从容器中拿,无论多少次都是同一个对象 1 @Configuration(proxyBeanMethods = true)
1 2 MyConfig config = context.getBean(MyConfig.class);User user = config.user();
组件依赖proxyBeanMethods为true的情况下 1 2 3 4 5 @Bean public User user () { Pet pet = pet() return new User ("xdy" , 12 , pet); }
Full模式与Lite模式:
@Bean @Bean @Component @Controller @Service @Repository @Import 1 2 3 4 5 @Import({User.class, Pet.class}) @Configuration public class MyConfig {}
@Import
:向容器中注入对应类型组件,默认组件名是全类名
“com.xuedongyun.User”: User{}
“com.xuedongyun.Pet”: Pet{}
@Conditional 1 2 3 4 5 6 7 8 9 10 11 12 @Conditional @ConditionalOnBean // 容器存在指定Bean @ConditionalOnMissingBean // 容器不存在指定Bean @ConditionalOnClassBean // 容器存在指定类型的对象 @ConditionalOnMissingClassBean // 容器不存在指类型的对象 @ConditionalOnResource // 容器类路径存在指定资源 @ConditionalOnJava // 指定Java版本号 @ConditionalOnWebApplication // 应用是Web应用 @ConditionalOnNotWebApplication // 应用不是Web应用 @ConditionOnSingleCandidate // 指定组件只有一个实例(或只有一个主实例) @ConditionOnProperty // 配置文件配置了某属性 ...
1 2 3 4 5 6 7 8 9 10 @Bean public Pet pet () { return new Pet ("tiny" ); } @Bean @ConditionalOnBean(name = "pet") public User user () { return new User ("xdy" , 12 ); }
1 2 3 4 5 6 @Configuration @ConditionalOnBean({Test.class}) public class MyConfig { }
注意Bean注册有顺序,有兴趣可以深入了解
引入xml配置 1 2 3 4 5 6 7 <?xml version="1.0" encoding="UTF-8" ?> <beans > <bean id ="user" class ="com.atguigu.boot.bean.User" > <property name ="name" value ="zhangsan" > </property > <property name ="age" value ="18" > </property > </bean > </beans >
1 2 @ImportResource("classpath:beans.xml") public class MyConfig {}
配置绑定 @ConfigurationProperties 1 2 mycar.name =BYD mycar.price =100000
1 2 3 4 5 6 7 @Component @ConfigurationProperties(prefix = "mycar") public class Car { private String name; private String price; }
1 2 3 4 5 6 7 8 9 10 11 @RestController public class HelloController { @Autowired Car car; @RequestMapping("/car") public Car car () { return car; } }
@EnableConfigurationProperties 1 2 3 4 5 6 7 @Configuration @EnableConfigurationProperties(Car.class) public class MyConfig { }
1 2 3 4 5 6 @ConfigurationProperties(prefix = "mycar") public class Car { private String name; private String price; }
这样Car就不用加@Component
实际场景:比如Car是一个第三方包中的类
(源码)自动配置原理 @SpringBootApplication
的核心1 2 3 4 5 6 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
1. @SpringBootConfiguration Main程序也是SpringBoot中的配置类,只不过是核心配置类 2. @ComponentScan 指定扫描哪些,Spring注解,可以看看具体用法 意义不算很大 3. @EnableAutoConfiguration 1 2 3 @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage 1 2 @Import(AutoConfigurationPackages.Registrar.class)
为容器导入Registrar
组件(@Import的高级用法 ) 利用Registrar
为容器中导入一系列组件AnnotationMetadata:注解元信息,当前注解(@AutoConfigurationPackage
)的一些信息,标在哪里,属性值是多少等等 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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)); } }
1 new PackageImports (metadata).getPackageNames().toArray(new String [0 ])
第一个参数是BeanDefinitionRegistry对象,用于向IOC容器中注册组件 第二个参数是一个字符串数组,存包的全限定名 该方法扫描指定的包,将其中组件注册到IOC容器中 1 register(registry, new PackageImports (metadata).getPackageNames().toArray(new String [0 ]));
@Import(…) 导入AutoConfigurationImportSelector
类型的Bean 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { ... @Override 返回值:给容器中导入哪些组件 public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } ... }
查看getAutoConfigurationEntry()
方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 protected AutoConfigurationEntry getAutoConfigurationEntry (AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry (configurations, exclusions); }
getCandidateConfigurations
获取所有候选的配置(目前144个)1 2 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
1 2 3 4 5 "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration" "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration" "org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration" "org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration" ...
步入后可以看到,通过SpringFactoriesLoader
工厂加载器,加载一些内容 1 2 3 4 5 6 7 8 9 protected List<String> getCandidateConfigurations (AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = new ArrayList <>( SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()) ); ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add); Assert.notEmpty(configurations, "..." ); return configurations; }
点进去可以看到,通过loadSpringFactories
,继续点进去 1 2 3 4 5 6 7 8 public static List<String> loadFactoryNames (Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null ) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
利用loadSpringFactories
加载得到一个Map<String, List<String>>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 private static Map<String, List<String>> loadSpringFactories (ClassLoader classLoader) { Map<String, List<String>> result = cache.get(classLoader); if (result != null ) { return result; } result = new HashMap <>(); try { Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); 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(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList <>()) .add(factoryImplementationName.trim()); } } } result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException ("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]" , ex); } return result; }
从META-INF/spring.factories
加载文件,默认扫描系统所有位置包含META-INF/spring.factories
的文件 1 Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
引入的所有jar包中,凡是有META-INF/spring.factories
的包都将被扫到
spring-boot-autoconfigure-2.7.5.jar
包也有这个文件
可以看到,配置写死在这里。不同注解,所需要的包,都在其中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 # Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.autoconfigure.integration.IntegrationPropertiesEnvironmentPostProcessor # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.autoconfigure.data.redis.RedisUrlSyntaxFailureAnalyzer,\ org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\ org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\ org.springframework.boot.autoconfigure.jooq.NoDslContextBeanFailureAnalyzer,\ org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBeanCreationFailureAnalyzer,\ org.springframework.boot.autoconfigure.r2dbc.MissingR2dbcPoolDependencyFailureAnalyzer,\ org.springframework.boot.autoconfigure.r2dbc.MultipleConnectionPoolConfigurationsFailureAnalzyer,\ org.springframework.boot.autoconfigure.r2dbc.NoConnectionFactoryBeanFailureAnalyzer,\ org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer # Template availability providers org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider # DataSource initializer detectors org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector=\ org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializerDatabaseInitializerDetector # Depends on database initialization detectors org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\ org.springframework.boot.autoconfigure.batch.JobRepositoryDependsOnDatabaseInitializationDetector,\ org.springframework.boot.autoconfigure.quartz.SchedulerDependsOnDatabaseInitializationDetector,\ org.springframework.boot.autoconfigure.session.JdbcIndexedSessionRepositoryDependsOnDatabaseInitializationDetector
2.7.0以后,不推荐将配置写入工程中了,写在包下的Imports里
按需开启自动配置项 虽然上述场景中,所有自动配置启动时默认加载全部(xxxAutoConfiguration
) 按照每个类的装配规则(@Conditional
),最终会按需配置 例1.AopAutoConfiguration 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @AutoConfiguration @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Advice.class) static class AspectJAutoProxyingConfiguration { } @Configuration(proxyBeanMethods = false) @ConditionalOnMissingClass("org.aspectj.weaver.Advice") @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) static class ClassProxyingConfiguration { } }
例2.CacheAutoConfiguration 1 2 3 4 5 6 7 8 9 10 11 @AutoConfiguration(after = { CouchbaseDataAutoConfiguration.class, HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class }) @ConditionalOnClass(CacheManager.class) @ConditionalOnBean(CacheAspectSupport.class) @ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver") @EnableConfigurationProperties(CacheProperties.class) @Import({ CacheConfigurationImportSelector.class, CacheManagerEntityManagerFactoryDependsOnPostProcessor.class }) public class CacheAutoConfiguration {}
例3.DispatcherServletAutoConfiguration 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) public class DispatcherServletAutoConfiguration { ... @Configuration(proxyBeanMethods = false) @Conditional(DefaultDispatcherServletCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class) protected static class DispatcherServletConfiguration { @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet (WebMvcProperties webMvcProperties) { DispatcherServlet dispatcherServlet = new DispatcherServlet (); dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound()); dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents()); dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails()); return dispatcherServlet; } @Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver (MultipartResolver resolver) { return resolver; } } ... }
例4.HttpEncodingAutoConfiguration 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @AutoConfiguration @EnableConfigurationProperties(ServerProperties.class) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnClass(CharacterEncodingFilter.class) @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true) public class HttpEncodingAutoConfiguration { private final Encoding properties; public HttpEncodingAutoConfiguration (ServerProperties properties) { this .properties = properties.getServlet().getEncoding(); } @Bean @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter () { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter (); filter.setEncoding(this .properties.getCharset().name()); filter.setForceRequestEncoding(this .properties.shouldForce(Encoding.Type.REQUEST)); filter.setForceResponseEncoding(this .properties.shouldForce(Encoding.Type.RESPONSE)); return filter; } ... }
总结 SpringBoot加载所有自动配置类 每个自动配置类按照条件生效很多配置类开启了配置绑定(@EnableConfigurationProperties
) 可以看看HttpEncodingAutoConfiguration
中的this.properties
->ServerProperties
可以自己在application.properties
中更改 生效的配置类,会给容器中装配很多组件若用户自己配置组件,以用户的优先(@ConditionalOnMissingBean
) 最佳实践 SpringBoot应用开发 引入场景依赖 查看自动配置了哪些(选做)自己分析,引入场景对应的自动配置一般生效 application.properties
中写debug=true
,开启自动配置报告,来查看 是否需要修改按照文档修改配置项 自定义加入或替换组件 额外的自定义器xxxCustomizer
(后面讲) Lombok 1 2 3 4 <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency >
idea安装lombok插件(现版本默认安装) 直接使用 1 2 3 4 5 6 7 8 9 @Data @ToString @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode public class Car { private String name; private Double price; }
1 2 3 4 5 6 7 <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-devtools</artifactId > <optional > true</optional > </dependency > </dependencies >
Spring Initalizr Developer ToolsSpring Boot DevTools Lombok Web SQLMyBatis Framework MySQL Driver NoSQLSpring Data Redis (Access+Driver)