自动配置
1.1 自动配好Tomcat
- 引入tomcat依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.12.RELEASE</version>
<scope>compile</scope>
</dependency>
- 配置Tomcat
1.2 自动配置spring-mvc
- 引入springMVC 全套组件
- 自动配好SPringleMVC常用组件
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.15.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.15.RELEASE</version>
<scope>compile</scope>
</dependency>
- 常用的功能
- dispatchServlet(转发)
- multipartResolver(文件上传)
- ViewResolver(视图解决器)
- characterEncodingFilter(文字过滤器)
- 展示容器内 对应组件的方法
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
// springApplication.run(MainApplication.class,args);
// 1.返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
// 2.查看容器里面的组件
String[] names = run.getBeanDefinitionNames();
for(String name : names){
System.out.println(name);
}
}
}
1.3 默认包结构
-
主程序所在包的所有子包组件都会被默认扫描进来
```
com
± example
± myapplication
± Application.java
|
± customer
| ± Customer.java
| ± CustomerController.java
| ± CustomerService.java
| ± CustomerRepository.java
|
± order
± Order.java
± OrderController.java
± OrderService.java
± OrderRepository.java
``` -
无需以前的包扫瞄
-
如果要自己设置包扫描
@SpringBootApplication(scanBasePackages = "com.atguigu") //在主类上添加
@ComponentScan //在具体类上添加包扫描的注解
注意不能再包SpringBootApplication上添加包扫描注解,因为SpringBootApplication上已经有了componentScan注解了
- *@SpringBootApplication是复合类
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
1.4 自动配置有很多的默认值
- 默认配置都是最终映射到一个类的
- 配置文件的值最终会绑定到每个类上,这个类在容器中创建对象
1.5 按需加载自动配置项
- 非常多的starter
- 引入了那些场景这个场景的自动配置才会开启
- SpringBoot所有自动配置功能都依赖于spring-boot-autoconfigure
-
springboot web项目需要引入spring-boot-starter-web依赖
-
spring-boot-starter-web依赖于spring-boot-starter
-
spring-boot-starter需要spring-boot-autoconfigure完成自动配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.12.RELEASE</version>
<scope>compile</scope>
</dependency>
2. 容器功能
2.1 组件添加
-
@Configuration(告诉IOC本类为配置类)
@Configuration(proxyBeanMethods = false) //告诉SpringBoot 这是一个配置类 == 配置文件 //配置文件能完成的事情 配置类都可以完成 //同时@Configuration创建的组件也是被加载到容器中的 //默认创建的组件都是单实例的 public class MyConfig { @Bean //给容器中添加组件,以方法名作为组件的id,返回类型就是组件的类型,返回值就是组件在容器中的实例 public User user01(){ return new User("张三"); } @Bean("tomcat111") public Pet getPet(){ return new Pet("tomcat","12"); } }
-
在Configuration注解的类所注册的组件都是单例的原因
// Configuration.class @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { @AliasFor( annotation = Component.class ) String value() default ""; boolean proxyBeanMethods() default true; //默认是true就表明了方法会被代理 }
/*
在容器Ioc中创建的对象:MyConfig E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIBd02ce854@13cda7c9
是由容器增强过的代理的对象,当proxyBeanMethods默认为true时
当代理对象调用user01方法的时候,首先会去容器中查找有没有对应组件
1.有,则返回
2.无,则创建
总之,保持组件单实例
*/- proxyBeanMethods方法:代理bean方法 ```java Full(proxyBeanMethods = true) //使用代理的方法,单例,返回相同的对象,应用于组件依赖 Lite(proxyBeanMethods = false) //多例每次返回不同对象,优点:跳过检查容器,springboot启动快
-
-
@Bean,@Component,@Controller,@Service,@Repository
-
@ComponentScan,@Import
@import必须写在包扫描范围中,导入的时一个数组,他导入组件的组件名字,是包名+类名
- @Conditional:条件装配,满足Conditional才装入容器
较为重要的
ConditionalOnBean() //容器中有这个类,才注册组件
ConditionalOnMissingBean //容器中没有这个组件,才注册组件
ConditionalOnMissingClass //容器中没有这个才注册组件
ConditionalOnClass //容器中有这个类才注册组件
ConditionalOnResource //文件中具有某个资源的时候才干什么
ConditionalOnJava //默认某一java版本,才注册组件
ConditionalOnWebApplication //是web应用的时候,才注册组件
ConditionalOnNotWebApplication //不是web应用的时候,才注册组件
ConditionalOnProperty //组件有了有个属性的时候,才注册组件
示例
@Configuration //告诉SpringBoot 这是一个配置类 == 配置文件
//配置文件能完成的事情 配置类都可以完成
//同时@Configuration创建的组件也是被加载到容器中的
//默认创建的组件都是单实例的
public class MyConfig {
@ConditionalOnMissingBean(name = "tomcat")
@Bean //给容器中添加组件,以方法名作为组件的id,返回类型就是组件的类型,返回值就是组件在容器中的实例
public User user01(){
return new User("张三");
}
public Pet getPet(){
return new Pet("tomcat","12");
}
}
2.2 原生配置文件的导入
-
@ImportResource**:写在配置类上**
用来将xml配置文件对象导入到springboot的IOC容器中
@Configuration //告诉SpringBoot 这是一个配置类 == 配置文件 @ImportResource("classpath:beans.xml") public class MyConfig { @Bean //给容器中添加组件,以方法名作为组件的id,返回类型就是组件的类型,返回值就是组件在容器中的实例 public User user01(){ return new User("张三"); } }
2.3 配置绑定
- ConfigurationProperties
@Component //一定要加入组件注释,让组件加入容器之中
@ConfigurationProperties(prefix = "mycar") //设置前缀,会在application.properties去查找
public class Car {
private String brand;
private String price;
public void setBrand(String brand) {
this.brand = brand;
}
public void setPrice(String price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public String getPrice() {
return price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price='" + price + '\'' +
'}';
}
}
- @EnableCofigurationProperties + @ConfigurationProperties
/*
配置文件类
*/
@Configuration
@EnableConfigurationProperties(Car.class) //使文件可以
public class MyConfig {
@Bean //给容器中添加组件,以方法名作为组件的id,返回类型就是组件的类型,返回值就是组件在容器中的实例
public User user01(){
return new User("张三");
}
public Pet getPet(){
return new Pet("tomcat","12");
}
}
/*
需要加入获取对应值的组件信息
*/
@ConfigurationProperties(prefix = "mycar") //设置前缀,会在application.properties去查找
public class Car {
private String brand;
private String price;
public void setBrand(String brand) {
this.brand = brand;
}
public void setPrice(String price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public String getPrice() {
return price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price='" + price + '\'' +
'}';
}
}
方法一主要用于自己编写的类,方法主要用于引入第三方的类的数据绑定
3. 自动配置原理入门
3.1 SpringBootApplication 完整签名(idea:ctrl+n进行搜索)
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan( //三个为其核心注解 或者说SpringbootApplication是他们三个的复合
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication
- @SpringBootConfiguration
/**
* SpringBootConfiguration.class源代码
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //代表当前是一个配置类,那么说明SpringBootApplication依然是个配置类,依然是核心配置类
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
- @ComponentScan
指定扫描那些包,具体可以看Spring注解;
- @EnableAutoConfiguration(重点,指定默认的包规则)
作用:帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot,并创建对应配置类的Bean,并把该Bean实体交给IoC容器进行管理。
/*
* EnableAutoConfiguration源代码
*/
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
- AutoConfigurationPackage自动配置包
/*
* AutoConfigurationPackage源代码
*/
@Import({AutoConfigurationPackages.Registrar.class}) //给容器中导入一个组件
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
使用import导入一个registrar的意义:批量导入一些组件
- 查看AutoConfigurationPackages.Registrar.class,了解registrar注册了什么。
public abstract class AutoConfigurationPackages {
private static final Log logger = LogFactory.getLog(AutoConfigurationPackages.class);
private static final String BEAN = AutoConfigurationPackages.class.getName();
public AutoConfigurationPackages() {
}
...
...
...
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
/*
AnnotationMetadata:表示注解的源信息,又由于Springboot->EnableAutoConfiguration->AutoConfigurationPackage->AutoConfigurationPackages,则展示源信息时,展示的信息是MainApplication的信息,也就是包名
相当于将某一个@SpringBootApplication注解的主配置配所在包取到 得com.ruan.boot,register将某个包里面的组件进行批量注册
*/
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
}
- @Import({AutoConfigurationImportSelector.class})
重点在AutoConfigurationImportSelector.class->selectImports->this.getAutoConfigurationEntry(annotationMetadata),给容器中批量导入组件
/*
* this.getAutoConfigurationEntry(annotationMetadata) 方法实现
*/
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//1.getCandidateConfigurations获取到需要导入到容器中的配置类(组件)
//2.SpringFactoriesLoader.loadFactoryNames,spring的工厂加载器,来加载一些配置类
//利用工厂加载器的private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader)方法,获得所有组件
//3.loadSpringFactories方法会在META-INF/spring.factories位置加载一个文件。
// 扫描当前系统中所有META-INF/spring.factories
// 核心jar包中 spring-boot-autoconfigure-2.3.12.RELEASE.jar 含有127个元素springboot一启动 就需要给容器加载的所有类。
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 AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
3.2 按需配置
由上面知道了,springboot启动时,如何进行自动配置中找到相应其的组件,但是不是所有的组件都是必须加载到组件当中的,存在按需配置的情况(以上的autoconfigure中127个没有全部加载,需要导入相应的场景依赖,即条件装配规则)
换句话说:127个场景的所有自动配置启动的时候全部加载,但是并没有全部装配
3.3 修改默认配置
@Bean
@ConditionalOnBean({MultipartResolver.class}) //容器中有这类组件
@ConditionalOnMissingBean( //容器中没有这个名字
name = {"multipartResolver"}
)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//@Bean标注的方法传入了对象参数,这个参数值就会从容器中中找
// SpringMvc multipartResolver,放置有些用户配置的文件上传解析器不符合规范
return resolver;
}
//给容器中加入了文件上传解析器
4.总结
- SpringBoot先加载所有的自动配置类
- 每个自动配置类按照条件生效
- 生效的配置类都会按照容器中装配很多组件
- 只要让其中有这些组件,相当于这些功能就有了
- 只要用户有自己的配置的,就以用户的优先
xxxxAutoConfiguration—>组件—>xxxxProperties里面哪值---->application.properties