springboot加载META-INFspring.factories⽅式
⽬录
springboot 加载 META-INF/spring.factories
⽤户应⽤程序Application
建⽴META-INF/spring.factories⽂件的意义何在
平常我们如何将Bean注⼊到容器当中
springboot 加载 META-INF/spring.factories
⽤户应⽤程序Application
ConfigurableApplicationContext context = SpringApplication.run(NacosSpringBootYamlApplication.class, args);
SpringApplication类
public static ConfigurableApplicationContext run(Class<?> primarySource, args) {
return run(new Class<?>[] { primarySource }, args);
}
// 这⾥Class是数组
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 这⾥就是SpringMvcApplication的实例
this.webApplicationType = WebApplicationType.deduceFromClasspath();// deduce(推断)web类型(servlet、reactive、NoWeb)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 这⾥会处理加载所有的spring.factories⽂件的内容到缓存到*META-INF/spring.factories*中声明的所有ApplicationContextInitializer的实现类并将其实例化 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //到*META-INF/spring.factories*中声明的所有ApplicationListener的实现类并将其实例化
this.mainApplicationClass = deduceMainApplicationClass(); //获得当前执⾏main⽅法的类对象,这⾥就是SpringMvcApplication的实例
}
具体加载该classLoader下的所有spring.factories到缓存
如果缓存已经存在,则直接根据key,返回数据
/** key:是spring.factories的key value:是根据key分组,把同个key的不同value放到list⾥⾯ */
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (classLoader);
if (result != null) { //已经处理过了直接返回
return result;
}
//url: // file:/C:/Users/kongqi/.m2/repository/org/springframework/spring-beans/5.1.9.RELEASE/spring-beans-5.1.9.RELEASE.jar!/META-INF/spring.factories
try { //得到classloader下的所有jar包中的spring.factories的⽂件
Enumeration<URL> urls = (classLoader != null ?
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = Element();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource); // 得到spring.factories的内容
for (Map.Entry<?, ?> entry : Set()) { // key: spring.factories的key value: spring.factories的value
String factoryClassName = ((String) Key()).trim(); // spring.factories的key
for (String factoryName : StringUtilsmaDelimitedListToStringArray((String) Value())) {//value根据逗号,分隔
result.add(factoryClassName, im()); //factoryClassName其实就是spring.factories的key
由于value是List类型 MultiValueMap value有多个
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
流程图
建⽴META-INF/spring.factories⽂件的意义何在
平常我们如何将Bean注⼊到容器当中
@Configuration
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
@Autowired
HelloProperties helloProperties;
@Bean
public HelloService helloService() {
HelloService service = new HelloService();
service.setHelloProperties( helloProperties );
return service;
}
}
⼀般就建⽴配置⽂件使⽤@Configuration,⾥⾯通过@Bean进⾏加载bean
或者使⽤@Compont注解在类上进⾏类的注⼊
注意:
在我们主程序⼊⼝的时候:
@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 {
⾥⾯注解@EnableAutoConfiguration
@ComponentScan注解指扫描@SpringBootApplication注解的⼊⼝程序类所在的basepackage下的
所有带有@Component注解的bean,从⽽注⼊到容器当中。reactive声明类型
但是
如果是加⼊maven坐标依赖的jar包,就是项⽬根⽬录以外的Bean是怎么添加的??
这个时候注解@EnableAutoConfiguration的作⽤就来了
导⼊了AutoConfigurationImportSelector这个类:
这个类⾥⾯有⼀个⽅法
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
@EnableAutoConfiguration注解来注册项⽬包外的bean。⽽spring.factories⽂件,则是⽤来记录项⽬包外需要注册的bean类名为什么需要spring.factories⽂件,
因为我们整个项⽬⾥⾯的⼊⼝⽂件只会扫描整个项⽬⾥⾯下的@Compont @Configuration等注解
但是如果我们是引⽤了其他jar包,⽽其他jar包只有@Bean或者@Compont等注解,是不会扫描到的。
除⾮你引⼊的jar包没有Bean加载到容器当中
所以我们是通过写/META-INF/spring.factories⽂件去进⾏加载的。
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论