Springboot是可以自动装配的,是怎么进行实现的呢,Springboot启动类为啥都放在根目录下呢,大概有几种注入Bean的方式呢
- springboot启动类为啥放根目录下,因为默认扫描当前包下的类,可以把对应的Bean注入spring容器,如果挪到指定包下,需要单独配置需要扫描哪些包
SpringBootApplication注解
java
@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{}
可以看到SpringBootApplication注解是个复合注解,先关注下EnableAutoConfiguration 这个注解,springboot通过这个注解实现了把类注入到spring容器,然后交给spring管理的bean就可以控制创建,依赖注入等
java
@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 {};
}
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}
// 实现了ImportBeanDefinitionRegistrar,通过这个注入到了BeanDefinition
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
AutoConfigurationPackage� 注解就负责扫描包下面的类,把对应注解上的包名解析之后给了BeanDefinnitionn,后面创建的对象的时候进行创建
springboot-starter的原理
springboot的starter非常方便,引入一个starter,对应的bean就交给spring管理了,这个都是怎么进心整合,怎么注入spring容器的呢 分析下@Import({AutoConfigurationImportSelector.class}) 可以看到在引入这个Selecter
挑一下AutoConfigurationImportSelector关键方法
java
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 看这里怎么
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
// 关键方法,在load对应的spring.factories
// 返回的map对应的配置的指定的接口对应的一到多个实现类
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
// 查找指定位置的内容
Enumeration 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()) {
Entry<?, ?> entry = (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);
}
}
}
总结
springboot的自动装配有@EnableAutoConfuration注解来实现的,注解为一个复合注解,里面存在@AutoConfurationPackage 这里是在扫描指定的包下的,不指定就是启动类所在的目录下的,里面也是在@Import 一个实现了BeanDefinitionRegistrar 的类来实现把对应的包交给其进行后续扫描创建BeanDefinition,这里在扫描自己写的代码
然后还有一个是在@Import(AutoConfigurationImportSelector.class) 这里的这个就是在处理整合框架,引入包的问题,springboot的扩展是通过springboot自己定义的spi实现的,在resources 目录下的spring,factories 里面记录对应接口的实现类,来让springboot去扫描,符合一定条件的,就进行了加载,这里就是主要加载的引入的包的下面类交给spring管理,跟上面的分工合作,一个处理自己的,一个处理外来的,都交给spring管理,这样bean就都进行装载了