SpringBoot之@SpringBootApplication注解详解
Posted 敲代码的小小酥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot之@SpringBootApplication注解详解相关的知识,希望对你有一定的参考价值。
前言
@SpringBootApplication注解中包含了SpringBoot的包扫描原理、自动装配原理等诸多原理,下面进行该注解的详细研究。
研究上面的原理,主要是研究@SpringBootApplication内部的组成结构,如下图:
下面对@SpringBootConfiguration和@EnableAutoConfiguration进行详解。
@SpringBootConfiguration注解
点进@SpringBootConfiguration内部,看其内部结构:
@Configuration是Spring的一个注解,其修饰的类会加入Spring容器。这就说明SpringBoot的启动类会加入Spring容器。
@EnableAutoConfiguration注解
看其内部结构:
重点看@AutoConfigurationPackage注解和@Import(AutoConfigurationImportSelector.class)注解。
@AutoConfigurationPackage注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
看其@Import进来的类AutoConfigurationPackages.Registrar类:
这是一个内部类,源码如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
我们看registerBeanDefinitions方法中的register方法:
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
通过断点,看packageNames参数的传入值:
可见是在启动类中配置的scanBasePackages属性的包。
程序进入了else条件块儿,执行如下代码:
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
该步骤就是创建BeanDefinition对象,然后将其注册到BeanDefinition注册器中。我们看其注册的BeanDefinition对象的详情:
可以看到,这个BeanDefinition封装的是一个Package对象。所以,BeanDefinition不只能封装类的属性,还可以封装整个包的属性。
这样,就把包下的类,全部注册到了Spring容器中。
@Import(AutoConfigurationImportSelector.class):
看导入的AutoConfigurationImportSelector类:
主要是在这个方法中,加载了jar包下的spring.facotries文件,源码如下:
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
这个方法中,将jar包中spring.factories文件信息加载到了cache缓存中。 这些配置的类,都是加入Spring容器中的。所以,配置的这些类,自动就在Spring容器中有了,无需我们再进行配置,这就是自动配置的原理。
功能自动开启
在Spring项目中,我们使用Aop也好,事务也好,缓存也好,零配置MVC也好,都要手动开启功能,才能用。而在SpringBoot项目中,无需手动配置,直接能用,这是什么原因呢?
我们拿AOP举例,在spring-boot-autoconfigure jar包中的spring.factories文件中,看AOP的配置:
点进去这个类看源码:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
类中的@EnableAspectJAutoProxy注解,开启了AOP功能,这就是自动开启的原理。
以上是关于SpringBoot之@SpringBootApplication注解详解的主要内容,如果未能解决你的问题,请参考以下文章
亲测接口自动化01_Eclipse运行springBootApp