标签:init 系统 ima 生效 java not sse gis 启动
...
import ...
@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 {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
.......
}
可以看出,@SpringBootApplication注解是:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {
@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),
@Filter(type = FilterType.CUSTOM,classes ={AutoConfigurationExcludeFilter.class})
}
)
这三个注解的组合注解,这些注解具体作用如下所示
package org.springframework.boot;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
/**
可以看到该注解的源码中,除了三个注解标配以外,就是一个@Configuration,就是说该注解的功能和 @Configuration一样,作用是声明这个类是一个配置类,只不过@SpringBootConfiguration声明的是一个核心 配置类,所以为什么我们称springboot应用的主程序类是一个核心配置类
*/
@ComponentScan(
excludeFilters = {
@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),
@Filter(type = FilterType.CUSTOM,classes ={AutoConfigurationExcludeFilter.class})
}
)
//该注解就是指定扫描那些包,将包中的类自动spring框架自动创建对象,作为组件存放到容器中
//上面的注解中的属性定义了两个扫描器,具体是什么可以回顾spring的注解,因为这个注解是spring框架的
package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//上面不关心
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
/*
可以看到该注解中是:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
这两个注解的合成
*/
package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar;
import org.springframework.context.annotation.Import;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
/*
可以看到该注解中只有一个特别注解:
@Import({Registrar.class})
@Import注解表示给容器中导入一个组件,这里导入的是一个Registrar.class
这里可能是@Import的更高级用法,可以自己取看一下,这里不探究
*/
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]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
/*
可以看到Registrar类是AutoConfigurationPackages类的一个静态内部类:
上面源码中有两个方法,主要作用是利用这个类给容器批量注册组件,具体怎么
实现,我们可以给第一个方法中的打上断点一探究竟如下:
*/
断点示意图
然后DEBUG启动类调试,可以看到执行的方法有两个参数:
registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)
其中AnnotationMetadata metadata//翻译过来就是注解的源信息
/*
就是@AutoConfigurationPackage注解标注在哪个类,
而这里是@SpringBootApplication注解标注在哪里就是该注解标注在哪里
这里是标注在主程序类上,如下图
*/
方法中有这样一段语句:
(String[])
(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])
/*
PackageImports(metadata)).getPackageNames()
PackageImports表示要导入的包里的东西
表示将注解的源信息metadata拿进来,获取到注解的包名
这里就是获取主程序类所在的包名以及这个包的各个子包的包名
我们可以看一下包名,复制
new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames()
右键->选择Evaluate Expression->粘贴进去,evaluate,可以看到是com.studymyself
是主程序类所在包名
.toArray(new String[0]这句语句就是将拿到的各个包名存储到字符串数组中
*/
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static final AutoConfigurationImportSelector.AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationImportSelector.AutoConfigurationEntry();
private static final String[] NO_IMPORTS = new String[0];
private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
private AutoConfigurationImportSelector.ConfigurationClassFilter configurationClassFilter;
public AutoConfigurationImportSelector() {
}
//核心方法就是这个
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
......
/*
利用getAutoConfigurationEntry(annotationMetadata);给容器批量导入一些组件
selectImports方法通过获取AnnotationMetadata annotationMetadata注解参数执行
方法getAutoConfigurationEntry(annotationMetadata)
表示自动创建获取所扫描包中的所有组件对象,然后执行
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
将获取的组件转换到数组中进行返回
*/
添加断点进行debug调试,如图
当程序抛到断点处时,一步一步往下执行,直到执行下面第二条语句时:
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
可以查看调试页面,看到configurations这个集合中有130个组件要装配到容器中
怎么获取的,是从getCandidateConfigurations方法中获取的,我们再重新debug一遍,当执行到该方法时,进入该方法中执行
```yaml
#(candidate:(竞选或求职的)候选人,申请人; 投考者; 应试者; 参加考试的人; 被认定适合者; 被认定有某种结局者;)(attribute v. 把…归因于; 认为…是由于; 认为是…所为(或说、写、作);n.属性; 性质; 特征;)
当进入到this.getCandidateConfigurations(annotationMetadata, attributes)方法中执行后(获取候选的配置),可以看到是通过获取spring的工厂类加载器(getSpringFactoriesLoaderFactoryClass())来完成的需要装配的组件类信息的获取,核心语句:
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
进入loadFactoryNames方法中查看,如下
```
就是说
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader)
方法帮我们获取了所有应该装配的组件信息,是默认扫描整个项目的"META-INF/spring.factories"中获取这个文件,加载其中的信息。这主要的依赖加载进来的jar包中的"META-INF/spring.factories",有些jar包的META-INF目录下是没有spring.factories。
但是最核心的是spring-boot-autoconfigure-2.2.5.RELEASE.jar这个jar包中是有spring.factories
我们点开其中的spring.factories,可以看到# Auto Configure注解下面的信息就是我们之前调试时,configurations集合中存储的,是囊括了所有场景的配置类,都是***AutoConfiguration结尾的。
就是说是已经再配置文件中写死了的springboot一启动就加载的所有场景的配置类作为组件,然后这些配置类中又为各自场景所需要的组件进行组件装配配置。
虽然130个场景的自动配置类在启动的时候默认加载,但是由于这些场景的所需包以及自动配置类中的的条件装配注解@Conditional限制了是否向容器中装配这些组件,所以最终会按需进行配置的。所以我们在容器中看不到不需要的类的组件
```java
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
```java
@Bean
@ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
给容器中加入了文件上传解析器;
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先
```java
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
}
##
总结:
- SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置
- - 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。
**xxxxxAutoConfiguration ---> 组件 --->** **xxxxProperties里面拿值 ----> application.properties**
标签:init 系统 ima 生效 java not sse gis 启动
原文地址:https://www.cnblogs.com/rjzhong/p/14892977.html