2.1 核心运行原理
springboot通过@EnableAutoConfiguration注解开启自动配置,加载spring.factories中注册的各种Autoconfiguration自动配置类,当某个AutoConfiguration类满足其注解@Conditional指定的生效条件时,则实例化该AutoConfiguration类中定义的Bean,并把这些Bean注册到Spring容器中,就可以完成依赖框架的自动配置。
2.2 @EnableAutoConfiguration
2.2.1 入口类和@SpringBootApplication
入口类
以下为springboot应用的常规入口类,形式上非常简单,类上面仅使用了@SpringBootApplication注解,在main方法中,指定入口类作为SpringApplication#run方法的运行参数,就可以启动springboot应用
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
@SpringBootApplication
该注解是springboot应用的核心注解,它里面组合了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
@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 {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
我们刚学springboot的时候,就被“告知”:入口类所在的包及其子包中的组件将会被扫描。这也是要将入口类放在顶级package下的原因(如果放在比较低的层级,那么上级包的类或同级所在包的类无法扫描到),那么为什么呢?因为@SpringBootApplication注解上有个@ComponentScan注解,默认情况下,我们并没有指定任何扫描的基础包,而在ComponentScanAnnotationParser处理这个@ComponentScan注解的时候,获取到如果没有设置任何扫描的信息,那么就获取注解所标注的类所在的包作为扫描的基础包,因而基础包及其下面定义的组件都会被扫描到。我们可以,@SpringBootApplication(scanBasePackages = “com.zzhua.test”)桥接到@ComponentScan注解中的值,那么就只会com.将zzhua.test作为基础包扫描。
2.2.2 @EnableAutoConfiguration注解解析
@EnableAutoConfiguration
该注解是由@SpringBootApplication注解组合而引入的,用于开启自动配置,它会根据类路径上是否有引入指定的包中的类,尝试猜测并配置项目中可能需要的bean。所以也可以使用该注解提供的exclude等方法,排除某些自动配置。
@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 {};
}
2.3 AutoConfigurationImportSelector
该类在@EnableAutoConfiguration注解中被@Import(AutoConfigurationImportSelector.class)组合而引入
2.3.1 @Import
该注解用于向spring容器中导入组件,在源码中很多@EnableXXX也是基于该注解实现的
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}
2.3.2 ImportSelector&DeferedImportSelector接口
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
如果ImportSelector的实现类,还实现了EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware接口,那么这些接口的回调都会在selectImports方法执行之前被回调,这样就可以拿到相应的对象了。
区别
@DeferedImportSelector会在所有的@Configuration类加载完成之后再加载返回的配置类,而ImportSelector在加载完@Configuration类之前先去加载返回的配置类,并且DeferedImportSelector的加载顺序可以通过@Order注解或实现Order接口来指定,并且它还可以了新的getImportGroup()来跨DeferedImportSelector实现自定义@Configuration的加载顺序。
2.3.3 AutoConfigurationImportSelector功能概述
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
AutoConfigurationMetadataLoader#loadMetadata
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
: ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while (urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
return new PropertiesAutoConfigurationMetadata(properties);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
# 多个值,使用逗号分隔(分隔的作用在后面解析中处理)
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate
加载元数据配置,通过加载类路径下,指定路径:META-INF/spring-autoconfigure-metadata.properties文件。加载此数据是为了后续过滤自动配置使用。SpringBoot使用一个Annotation的处理器来收集自动加载的条件,这些条件就可以配置在这个文件当中。Springboot会将收集好的自动配置类,根据此配置信息,使用加载的过滤器(默认配置了3种:OnBeanCondition、OnClassCondition、OnWebApplicationCondition,它们都继承了FilteringSpringBootCondition),再进行一次过滤(跟在自动配置类上添加这些@ConditionalOnClass作用是一样的)。目的就是为了减少@Configuratin类的数量,从而减少初始化bean的耗时。
getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
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 = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
isEnabled(AnnotationMetadata metadata)
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
getAttributes(AnnotationMetadata metadata)
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
String name = getAnnotationClass().getName();
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
+ " annotated with " + ClassUtils.getShortName(name) + "?");
return attributes;
}
protected Class<?> getAnnotationClass() {
return EnableAutoConfiguration.class;
}
getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
removeDuplicates(List list)
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes)
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
excluded.addAll(asList(attributes, "exclude"));
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
private List<String> getExcludeAutoConfigurationsProperty() {
if (getEnvironment() instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(getEnvironment());
return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
.orElse(Collections.emptyList());
}
String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}
checkExcludedClasses(List configurations, Set exclusions)
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
for (String exclusion : exclusions) {
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
if (!invalidExcludes.isEmpty()) {
handleInvalidExcludes(invalidExcludes);
}
}
filter(List configurations, AutoConfigurationMetadata autoConfigurationMetadata)
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return new ArrayList<>(result);
}
FilteringSpringBootCondition
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}
OnClassCondition
@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {
@Override
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
if (Runtime.getRuntime().availableProcessors() > 1) {
return resolveOutcomesThreaded(autoConfigurationClasses, autoConfigurationMetadata);
}
else {
OutcomesResolver outcomesResolver = new StandardOutcomesResolver(autoConfigurationClasses, 0,
autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader());
return outcomesResolver.resolveOutcomes();
}
}
}
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end,
AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
for (int i = start; i < end; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");
if (candidates != null) {
outcomes[i - start] = getOutcome(candidates);
}
}
}
return outcomes;
}
private ConditionOutcome getOutcome(String candidates) {
try {
if (!candidates.contains(",")) {
return getOutcome(candidates, this.beanClassLoader);
}
for (String candidate : StringUtils.commaDelimitedListToStringArray(candidates)) {
ConditionOutcome outcome = getOutcome(candidate, this.beanClassLoader);
if (outcome != null) {
return outcome;
}
}
}
catch (Exception ex) {
}
return null;
}
private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
if (ClassNameFilter.MISSING.matches(className, classLoader)) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class").items(Style.QUOTE, className));
}
return null;
}
fireAutoConfigurationImportEvents(List configurations, Set exclusions)
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
2.4 @Conditional注解
2.4.1 @Conditional
spring4.0中定义的注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
Condition接口
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
ConditionContext
public interface ConditionContext {
BeanDefinitionRegistry getRegistry();
@Nullable
ConfigurableListableBeanFactory getBeanFactory();
Environment getEnvironment();
ResourceLoader getResourceLoader();
@Nullable
ClassLoader getClassLoader();
}
AnnotatedTypeMetadata
可以获取所标注的 类 或者 方法 上的所有注解信息,而无须加载对应的类(asm实现)
public interface AnnotatedTypeMetadata {
MergedAnnotations getAnnotations();
boolean isAnnotated(String annotationName);
Map<String, Object> getAnnotationAttributes(String annotationName);
Map<String, Object> getAnnotationAttributes(String annotationName,
boolean classValuesAsString);
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
}
例如:
@Component("s1")
@Service("s2")
public class Student {
static {
System.out.println("static executing...");
}
public Student() {
System.out.println("student cons...");
}
}
public class TestApp {
public static void main(String[] args) throws IOException {
SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
MetadataReader stuMetadataReader = metadataReaderFactory.getMetadataReader("com.zzhua.test.Student");
boolean annotated = stuMetadataReader.getAnnotationMetadata().isAnnotated(Component.class.getName());
System.out.println(annotated);
Map<String, Object> annotationAttributes = stuMetadataReader.getAnnotationMetadata().getAnnotationAttributes(Component.class.getName());
System.out.println(annotationAttributes);
MultiValueMap<String, Object> allAnnotationAttributes = stuMetadataReader.getAnnotationMetadata().getAllAnnotationAttributes(Component.class.getName());
System.out.println(allAnnotationAttributes);
}
}
2.4.2 @Conditional衍生注解
由@Conditional注解衍生而来的注解,这些注解上都标注了@Conditinal注解,并且指定了Condition接口的实现类,当满足指定的条件时,被该注解标注的类才会生效。
@ConditionalOnBean 当容器中由指定bean时
@ConditionalOnMissingBean 当在容器中没有指定bean时
@ConditionalOnClass 当在类路径下有指定类时
@ConditionalOnMissingClass 当在类路径下没有有指定类时
@ConditionalOnProperty 当指定的属性有指定的值时
@ConditionalOnExpression 当spel表达式为true时
@ConditionalOnWebApplication 当在web容器时
...
以@ConditionalOnMissingBean 为例
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {
Class<?>[] value() default {};
String[] type() default {};
Class<?>[] ignored() default {};
String[] ignoredType() default {};
Class<? extends Annotation>[] annotation() default {};
String[] name() default {};
SearchStrategy search() default SearchStrategy.ALL;
Class<?>[] parameterizedContainer() default {};
}
OnBeanCondition
@Order(Ordered.LOWEST_PRECEDENCE)
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
@Override
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
}
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage matchMessage = ConditionMessage.empty();
MergedAnnotations annotations = metadata.getAnnotations();
if (annotations.isPresent(ConditionalOnBean.class)) {
}
if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
}
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
ConditionalOnMissingBean.class);
MatchResult matchResult = getMatchingBeans(context, spec);
if (matchResult.isAnyMatched()) {
String reason = createOnMissingBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
}
return ConditionOutcome.match(matchMessage);
}
}
|