SpringBoot基础入门
优缺点
优点:
- 创建独立的Spring应用
- 内嵌web服务器
- 自动starter依赖,简化构建配置
- 自动配置Spring以及第三方功能
- 提供生产级别的监控、健康检查及外部化配置
- 无代码生成、无需编写XML
缺点:
- 迭代快,需要时刻关注变化
- 封装太深,内部原理复杂,不容易精通
入门案例
1. 创建maven工程
2. 引入依赖
1 |
|
3. 创建主程序
1 | /** |
4. 编写业务
1 |
|
5. 测试
直接运行main方法即可
6. 简化配置
编写application.properties
1 | # 修改服务端口号 |
7. 简化部署
在maven配置中添加:
1 | <build> |
mvn clean
mvn package
java -jar HelloWorld-1.0-SNAPSHOT.jar
自动配置原理入门
SpringBoot特点
依赖管理
父项目依赖管理
- 父项目中引入了几乎所有常用依赖,并且自带一个版本
开发导入starter场景启动器
- 有很多spring-boot-starter-:表示某种场景
- 只要引入starter,这个场景的所有常规需要的依赖都会自动引入
- 所有支持的场景
- *-spring-boot-starter:格式通常表示第三方提供的简化开发的场景启动器
无需关注版本号,自动版本仲裁
- 引入依赖默认都可以不写版本
- 引入非版本仲裁的依赖就需要写版本号
可以修改版本号
如果需要使用自己需要的版本,则可以通过配置进行修改
- 查看spring-boot-dependencies里面规定的当前依赖的版本用的key
- 在当前项目的pom.xml中重写所需版本号
1
2
3<properties>
<mysql.version>5.1.43</mysql.version>
</properties>
自动配置
- 自动配好Tomcat
- 引入Tomcat依赖
- 配置Tomcat
- 自动配好SpringMVC
- 引入SpringMVC全套组件
- 自动配好SpringMVC常用组件(功能)
- 自动配好Web常见功能,如字符编码问题
- dispatcherServlet
- characterEncodingFilter
- multipartResolver
- 默认的包结构
- 主程序所在的包及其下面的所有子包里面的组件都会被默认扫描到
- 手动配置则需要:
@SpringBootApplication(scanBasePackages = "com.zephon")
或@ComponentScan("com.zephon")
- 各种配置拥有默认值
- 默认配置最终都是映射到某个类上
- 配置文件的值最终会绑定到某个类上,这个类会在容器中创建对象
- 按需加载所有自动配置项
- 父项目依赖中有很多starter
- 引入哪些场景就会自动配置哪些场景
- 实际上所有自动配置都在
spring-boot-autoconfigure
包中
容器功能
组件(bean)添加
@Configuration
:说明被注解的类是一个配置类,等同于之前的xml配置文件@Beain
:给容器中添加组件,以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例,默认是单例的,即使是通过Configuration中的方法调用的,因为会通过一个代理模式调用属性
proxyBeanMethods
,默认是true
(Full模式),表示获取的Config类实例是被Spring动态代理增强过的代理对象,在获取内部bean时就会保持组件单实例,如果设置为false
(Lite模式)则会直接调用对应的实例,在获取内部bean时就不会保持组件单实例- 通常,如果创建的组件(bean)只是单纯的直接使用,则通常可以使用Lite模式,加载更快
- 如果创建的组件(bean)之间存在依赖关系,对应xml配置中的
ref
,则通常需要使用Full模式,保证其依赖的组件就是窗器中对应的组件
例:
1
2
3
4
5
6
7
public class MyConfig {
// ()中自定义名字
public User user01(){
return new User("zhangsan", 23);
}
}
常用的@Component、@Controller、@Service、@Repository
@ComponentScan、@Import
@Import
:写在一个容器中组件的类(配置类、Controller、Service等)上,用于导入组件类,例:@Import({User.class, DBHelper.class})
表示给容器中自动创建出这两个类型的组件,默认组件名就是全类名
@Conditional:条件装配,表示满足Conditional指定的条件则进行组件注入
ConditionalOnBean
:当存在Bean时才...ConditionalOnMissingBean
:当不存在Bean时才...ConditionalOnClass
:当容器中存在某个类时才...ConditionalOnMissingClass
:当容器中没有某个类时才...ConditionalOnResource
:当项目路径中存在某个资源时才...ConditionalOnJava
:当Java版本号符合条件时才...ConditionalOnWebApplication
:当应用是Web应用时才...ConditionalOnNotWebApplication
:当应用不是Web应用时才...ConditionalOnSingleCondidate
:当容器中是单实例或有主实例(用@Primary标注)才...ConditionalOnProperty
:当配置了某个属性才...例:
1
2
3
4
5
6
7
8
public class MyConfig {
public User user01(){
return new User("zhangsan", 23);
}
}
原生配置文件引入
@ImportResource
:导入原生xml配置文件- 例:
@ImportResource("classpath:beans.xml")
- 例:
配置绑定
目的:读取properties文件中的内容并将其封装到JavaBean中,以供随时使用
原始做法:
1 | public class getProperties { |
@ConfigurationProperties
:@Component
+@ConfigurationProperties
配置文件中:
1
2zhangsan =
20 =配置类:
1
2
3
4
5
6
public class User {
private String name;
private Integer age;
}@EnableConfigurationProperties
+@ConfigurationProperties
@EnableConfigurationProperties(User.class)
:开启User配置绑定功能,并且将这个Car组件自动注册到容器中,用于无法直接在对应类上添加@Component
的情况@ConfigurationProperties
:配置文件
自动配置原理入门
引导加载自动配置类
1 |
|
@SpringBootConfiguration
:相当于一个@Configuration
,表示当前是一个配置类@ComponentScan
:指定包扫描路径@EnableAutoConfiguration
:1
2
3
public EnableAutoConfiguration {@AutoConfigurationPackage
:自动配置包1
2// 向容器中导入组件
public AutoConfigurationPackage {1
2
3
4
5
6
7
8
9
10
11
12
13// Registrar:向容器中导入一系列组件,实际功能:将指定的包(此处是指MainApplication对应的包)下所有的组件导入
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}@Import({AutoConfigurationImportSelector.class})
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 给容器中批量导入一些组件
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}要导入的组件由
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
决定:1
2
3
4
5
6protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}利用工厂加载得到所有组件:
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
42private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
Map<String, List<String>> result = new HashMap();
try {
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}从
META-INF/spring.factories
位置加载一个文件,默认扫描当前系统里所有META-INF/spring.factories
位置的文件其中
spring-boot-autoconfigure-2.7.3.jar
的META-INF/spring-autoconfigure-metadata.properties
定义了所有在SpringBoot启动时要加载的AutoConfiguration配置类
按需开启自动配置项
- 虽然上述定义的所有AutoConfiguration在启动时默认全部加载,但根据条件装配规则,最终会按需配置
定制化修改自动配置
SpringBoot默认会在底层配置好所有的组件,但如果用户自己配置了则以用户的优先
@ConditionalOnMissingBean
:当容器中没有时才配置
SpringBoot先加载所有自动配置类 xxxxxAutoConfiguration
每个自动配置类按条件进行生效,默认都会绑定配置文件指定的值 xxxxProperties里面,xxxProperties又会和配置文件进行绑定
生效的配置类就会向容器中装配很多组件
只要容器中有这些组件,相当于这些功能就有了
只要有用户自己配置的,就以用户的优先
定制化配置
用户自己通过
@Bean
替换底层的组件看对应组件是获取配置文件的哪个值,就去修改
如:
server.servlet.encoding.charset=gbk
最佳实践
- 引入场景依赖
- 查看自动配置了哪些
- 自己分析
- 在配置文件中添加
debug=true
开启自动配置报告
- 是否需要修改
- 参照文档修改配置项
- 自定义加入或替换组件
- 自定义器 xxxxCustomizer
- ......
开发技巧
Lombok
简化JavaBean开发
1. 引入依赖
1 | <dependency> |
2. 安装lombok插件
3. 使用lombok
@Data
:自动生成对应类的getter
和setter
方法@ToString
:自动生成toString()
方法@NoArgsConstructor
:生成无参构造器@AllArgsConstructor
:生成所有参数构造器@EqualsAndHashCode
:生成equals()
和hashcode()
方法@Slf4j
:注入日志类,之后就可以直接用log.info()
等打印日志了
dev-tools
自动重启
1.引入依赖
1 | <dependency> |