文章目录
1. 第一个springBoot程序
文件目录一定要跟XXXAppplication
同级。
springboot每个项目打成jar包可以独立运行。
java -jar jar包的名字
2. 原理初探
自动配置
pom.xml
- spring-boot-dependencies 这是核心依赖,在父工程中。
- 我们在引入springboot依赖时不用写版本,就是因为有这些版本仓库
启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 启动器:就是springboot的启动场景
- 比如spring-boot-starter-web,会帮我们自动导入web环境所有的依赖!
- springboot会将所有的功能场景, 变成一个个启动器
- 我们要使用什么功能,就只需找到对应的启动器
主程序
@SpringBootApplication
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
}
-
注解
-
@SpringBootConfiguration :springboot的配置 @Configuration : spring配置 @Component : spring组件 @EnableAutoConfiguration :自动配置 @AutoConfigurationPackage : 自动配置包 @Import({Registrar.class}) 自动配置,包注册 @Import({AutoConfigurationImportSelector.class}) :自动配置导入选择器,
AutoConfigurationImportSelector
这个类中有如下方法getCandidateConfigurations
1. 获取候选的配置
// 获取候选的配置 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct."); return configurations; }
2.这个类又调用了 SpringFactoriesLoader 类的静态方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); //这里它又调用了 loadSpringFactories 方法 return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
3.我们继续点击查看 loadSpringFactories 方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身 MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { //去获取一个资源 "META-INF/spring.factories" Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); //将读取到的资源遍历,封装成为一个Properties 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()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }
4.发现一个多次出现的文件:spring.factories,找到它
-
spring.factories
我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!
结论:
-
SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
-
将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
-
整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
-
它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
-
有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;
SpringApplication.run分析
run方法流程
3. properties配置文件
server.port=8088 # 端口号
server.servlet.context-path=/dingtalkbot # 项目后缀
# 整合redis
spring.redis.host=172.16.21.139
spring.redis.database=7
spring.redis.port=17001
#放开springboot对静态资源的拦截
spring.mvc.static-path-pattern:/** # 项目路径
spring.resources.static-locations:classpath:/** # 资源路径下
4. yml属性注入
实体类 Person.class
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date date;
private Map<String,Object> maps;
private List<Object> list;
private Dog dog;
...
...
application.yml
person:
name: zhulianwei
age: 3
happy: false
birth: 2000/01/01
maps: {k1: v1,k2: v2}
list:
- code
- girl
- music
dog:
name: 旺财
age: 1
5. 一些注解
@ConfigurationProperties 默认从全局配置文件中获取值
@
6. JSR303校验
一种属性注入的数据校验
@Validated // 数据校验
public class Person {
@Email()
private String name;
比如实体类中的**@Email**注解,在给name赋值的时候,只能是email格式,否则报错。
常见参数
@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) string is between min and max included.
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
.......等等
除此以外,我们还可以自定义一些数据校验规则
7. 多环境配置以及配置文件位置
配置文件加载位置
外部加载配置文件的方式十分多,我们选择最常用的即可,在开发的资源文件中进行配置!
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件:
优先级1:项目路径下的config文件夹配置文件优先级2:项目路径下配置文件优先级3:资源路径下的config文件夹配置文件优先级4:资源路径下配置文件
yml的多环境配置
以下配置了三套环境,默认/dev/test,在yml中只需要用’—'来分隔开,用spring profiles来标识选择哪一套配置。
server:
port: 8081
#选择要激活那个环境块
spring:
profiles:
active: test
---
server:
port: 8083
spring:
profiles: dev #配置环境的名称
---
server:
port: 8084
spring:
profiles: test #配置环境的名称
8. 自动装配原理
https://www.cnblogs.com/javaguide/p/springboot-auto-config.html
1 主程序
@SpringBootApplication // ★★★
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
}
2 SpringBootApplication注解
@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 { ...}
3 EnableAutoConfiguration注解
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage // 作用:将main包下的所有组件注册到容器中 ★★★@Import(AutoConfigurationImportSelector.class) // ★★★public @interface EnableAutoConfiguration { // ...}
4 AutoConfigurationImportSelector 类
AutoConfigurationImportSelector
类实现了 ImportSelector
接口,也就实现了这个接口中的 selectImports
方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// <1>.判断自动装配开关是否打开
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//<2>.获取所有需要装配的bean
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); // ★★★
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
这里我们需要重点关注一下getAutoConfigurationEntry()
方法,这个方法主要负责加载自动配置类的。
该方法调用链如下:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
//<1>.
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//<2>.
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//<3>.
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // ★★★
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//<4>.
configurations = getConfigurationClassFilter().filter(configurations); // ★★★ 过滤
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
第一步
判断自动装配开关是否打开。默认spring.boot.enableautoconfiguration=true
,可在 application.properties
或 application.yml
中设置
第二步
用于获取EnableAutoConfiguration
注解中的 exclude
和 excludeName
。
第三步 ★★★
获取需要自动装配的所有配置类
- getCandidateConfigurations
- SpringFactoriesLoader
- loadSpringFactories 读取
META-INF/spring.factories
★★★★★★
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
...
第四步:
spring.factories
中这么多配置,每次启动都要全部加载么?
不是的,这一步有经历了一遍筛选,@ConditionalOnXXX
中的所有条件都满足,该类才会生效。
原理再探究
META-INF/spring.factories其中的HttpEncodingAutoConfiguration类
// 1. 表示这是一个配置类
@Configuration(proxyBeanMethods = false)
// 2. 自动配置属性,ServerProperties的属性可以在配置文件中配置
@EnableConfigurationProperties(ServerProperties.class)
// 3. spring的底层注解,根据不同的条件,来判断当前配置或者类是否生效
@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));
// ......
}
了解@Conditional
了解完自动装配的原理后,我们来关注一个细节问题,自动配置类必须在一定的条件下才能生效;
@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。
- springboot帮我们自动装配
- 会从xxxProperties.class里面取默认值
- 可以在配置文件中修改值
精髓
1、SpringBoot启动会加载大量的自动配置类(spring.factories中的类)
2、我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
3、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
4、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
**xxxxAutoConfiguration:自动配置类;**给容器中添加组件
xxxxProperties:封装配置文件中相关属性;
总结 ★★★★★★
Spring Boot 通过==@EnableAutoConfiguration==开启自动装配,
通过 SpringFactoriesLoader 最终加载META-INF/spring.factories中的自动配置类实现自动装配。
自动配置类要在==@Conditionalonxxx==注解条件满足下才能加载,
想要其条件满足必须引入spring-boot-starter-xxx包实现起步依赖。
开发简单网站
一、静态资源过滤
这四个目录下的静态资源都能直接访问到。静态资源都会从这4个目录中找。
项目目录结构