spring boot实战(第九篇)Application创建源码分析

前言

通过前面的文章了解到在spring boot的启动时,利用的是编写的Application类,使用了注解@SpringBootApplication,本篇将阐述该Bean的加载过程。

 

[html] view plain copy

  1. @SpringBootApplication  
  2. public class Application {  
  3.     public static void main(String[] args) {  
  4.         SpringApplication app = new SpringApplication(Application.class);   
  5.         app.addListeners(new MyApplicationStartedEventListener());  
  6.         app.run(args);  
  7.     }  
  8. }  

 

 

 

 

 

Application

上篇中讲述了上下文的创建,在run方法中接下来会执行

[html] view plain copy

  1. load(context, sources.toArray(new Object[sources.size()]));  

这个的sources表示的为Application类,在创建SpringApplication时手动传递

[html] view plain copy

  1. SpringApplication app = new SpringApplication(Application.class);  

 

load方法如下:

 

[html] view plain copy

  1. protected void load(ApplicationContext context, Object[] sources) {  
  2.         if (this.log.isDebugEnabled()) {  
  3.             this.log.debug(“Loading source ”  
  4.                     + StringUtils.arrayToCommaDelimitedString(sources));  
  5.         }  
  6.         BeanDefinitionLoader loader = createBeanDefinitionLoader(  
  7.                 getBeanDefinitionRegistry(context), sources);  
  8.         if (this.beanNameGenerator != null) {  
  9.             loader.setBeanNameGenerator(this.beanNameGenerator);  
  10.         }  
  11.         if (this.resourceLoader != null) {  
  12.             loader.setResourceLoader(this.resourceLoader);  
  13.         }  
  14.         if (this.environment != null) {  
  15.             loader.setEnvironment(this.environment);  
  16.         }  
  17.         loader.load();  
  18.     }  

调用loader.load();

[html] view plain copy

  1. private int load(Object source) {  
  2.         Assert.notNull(source, “Source must not be null”);  
  3.         if (source instanceof Class<?>) {  
  4.             return load((Class<?>) source);  
  5.         }  
  6.         if (source instanceof Resource) {  
  7.             return load((Resource) source);  
  8.         }  
  9.         if (source instanceof Package) {  
  10.             return load((Package) source);  
  11.         }  
  12.         if (source instanceof CharSequence) {  
  13.             return load((CharSequence) source);  
  14.         }  
  15.         throw new IllegalArgumentException(“Invalid source type ” + source.getClass());  
  16.     }  

执行load((Class<?>) source)

 

[html] view plain copy

  1. private int load(Class<?> source) {  
  2.         if (isGroovyPresent()) {  
  3.             // Any GroovyLoaders added in beans{} DSL can contribute beans here  
  4.             if (GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {  
  5.                 GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,  
  6.                         GroovyBeanDefinitionSource.class);  
  7.                 load(loader);  
  8.             }  
  9.         }  
  10.         if (isComponent(source)) {  
  11.             this.annotatedReader.register(source);  
  12.             return 1;  
  13.         }  
  14.         return 0;  
  15.     }  

 

isComponent判断Application是否存在注解Compent

 

[html] view plain copy

  1. private boolean isComponent(Class<?> type) {  
  2.         // This has to be a bit of a guess. The only way to be sure that this type is  
  3.         // eligible is to make a bean definition out of it and try to instantiate it.  
  4.         if (AnnotationUtils.findAnnotation(type, Component.class) != null) {  
  5.             return true;  
  6.         }  
  7.         // Nested anonymous classes are not eligible for registration, nor are groovy  
  8.         // closures  
  9.         if (type.getName().matches(“.*\\$_.*closure.*”) || type.isAnonymousClass()  
  10.                 || type.getConstructors() == null || type.getConstructors().length == 0) {  
  11.             return false;  
  12.         }  
  13.         return true;  
  14.     }  

AnnotationUtils.findAnnotation(type, Component.class) 工具类获取执行类对应的注解信息,该工具类在自己编码代码时可用得到

 

由于Application使用注解@SpringBootApplication,其定义如下

 

[html] view plain copy

  1. @Target(ElementType.TYPE)  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. @Documented  
  4. @Inherited  
  5. @Configuration  
  6. @EnableAutoConfiguration  
  7. @ComponentScan  
  8. public @interface SpringBootApplication {  
  9.   
  10.     /**  
  11.      * Exclude specific auto-configuration classes such that they will never be applied.  
  12.      * @return the classes to exclude  
  13.      */  
  14.     Class<?>[] exclude() default {};  
  15.   
  16. }  

发现不存在Compoment注解,是不是表明Application不是一个Component呢?其实不然,来看下@Configuration注解

 

[html] view plain copy

  1. @Target(ElementType.TYPE)  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. @Documented  
  4. @Component  
  5. public @interface Configuration {  
  6.   
  7.     /**  
  8.      * Explicitly specify the name of the Spring bean definition associated  
  9.      * with this Configuration class.  If left unspecified (the common case),  
  10.      * a bean name will be automatically generated.  
  11.      * <p>The custom name applies only if the Configuration class is picked up via  
  12.      * component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}.  
  13.      * If the Configuration class is registered as a traditional XML bean definition,  
  14.      * the name/id of the bean element will take precedence.  
  15.      * @return the specified bean name, if any  
  16.      * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator  
  17.      */  
  18.     String value() default “”;  
  19.   
  20. }  

发现Configuration注解上存在Component注解,表明Application为Component

 

接下来执行this.annotatedReader.register(source);

 

[html] view plain copy

  1. public void register(Class<?>… annotatedClasses) {  
  2.         for (Class<?> annotatedClass : annotatedClasses) {  
  3.             registerBean(annotatedClass);  
  4.         }  
  5.     }  

调用registerBean注册Application对应的bean信息

 

[html] view plain copy

  1. public void registerBean(Class<?> annotatedClass, String name,  
  2.             @SuppressWarnings(“unchecked”) Class<? extends Annotation>… qualifiers) {  
  3.   
  4.         AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);  
  5.         if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {  
  6.             return;  
  7.         }  
  8.   
  9.         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);  
  10.         abd.setScope(scopeMetadata.getScopeName());  
  11.         String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));  
  12.         AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);  
  13.         if (qualifiers != null) {  
  14.             for (Class<? extends Annotation> qualifier : qualifiers) {  
  15.                 if (Primary.class.equals(qualifier)) {  
  16.                     abd.setPrimary(true);  
  17.                 }  
  18.                 else if (Lazy.class.equals(qualifier)) {  
  19.                     abd.setLazyInit(true);  
  20.                 }  
  21.                 else {  
  22.                     abd.addQualifier(new AutowireCandidateQualifier(qualifier));  
  23.                 }  
  24.             }  
  25.         }  
  26.   
  27.         BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);  
  28.         definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);  
  29.         BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);  
  30.     }  

首先来看

 

[html] view plain copy

  1. if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {  
  2.             return;  
  3.         }  

判断是否需要跳过,其代码如下:

 

[html] view plain copy

  1. public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {  
  2.         if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {  
  3.             return false;  
  4.         }  
  5.   
  6.         if (phase == null) {  
  7.             if (metadata instanceof AnnotationMetadata &&  
  8.                     ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {  
  9.                 return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);  
  10.             }  
  11.             return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);  
  12.         }  
  13.   
  14.         List<Condition> conditions = new ArrayList<Condition>();  
  15.         for (String[] conditionClasses : getConditionClasses(metadata)) {  
  16.             for (String conditionClass : conditionClasses) {  
  17.                 Condition condition = getCondition(conditionClass, this.context.getClassLoader());  
  18.                 conditions.add(condition);  
  19.             }  
  20.         }  
  21.   
  22.         AnnotationAwareOrderComparator.sort(conditions);  
  23.   
  24.         for (Condition condition : conditions) {  
  25.             ConfigurationPhase requiredPhase = null;  
  26.             if (condition instanceof ConfigurationCondition) {  
  27.                 requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();  
  28.             }  
  29.             if (requiredPhase == null || requiredPhase == phase) {  
  30.                 if (!condition.matches(this.context, metadata)) {  
  31.                     return true;  
  32.                 }  
  33.             }  
  34.         }  
  35.   
  36.         return false;  
  37.     }  

该代码判断Application上是否存在Conditional注解,如果不满足Conditional对应条件则该bean不被创建;

 

Conditional注解

代码分析到这里可以先看看Conditional注解的使用,其定义为:

[html] view plain copy

  1. @Retention(RetentionPolicy.RUNTIME)  
  2. @Target({ElementType.TYPE, ElementType.METHOD})  
  3. public @interface Conditional {  
  4.     /**  
  5.      * All {@link Condition}s that must {@linkplain Condition#matches match}  
  6.      * in order for the component to be registered.  
  7.      */  
  8.     Class<? extends Condition>[] value();  
  9.   
  10. }  

从源码可以看出,首先判断Application上是否存在Conditional,如果存在,则获取Conditional注解中的value数组值,对应的Class必须实现Condition接口:

 

[html] view plain copy

  1. public interface Condition {   
  2.     boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);  
  3.   
  4. }  

如果matches返回true 表明该bean需要被创建,否则表明该bean不需要被创建。

 

明白了该注解的用法后,来一个实际案例

[html] view plain copy

  1. package com.lkl.springboot.condition;  
  2.   
  3. import org.springframework.context.annotation.Conditional;  
  4. import org.springframework.stereotype.Component;  
  5.   
  6. @Component(“MyCondition”)  
  7. @Conditional(MyCondition.class)  
  8. public class ConditionBean {  
  9. }  

创建ConditionBean,使用注解@Conditional(MyCondition.class)调用MyCondition类

 

[html] view plain copy

  1. /**  
  2.  * 自定义condition  修改返回值,查看bean是否创建  
  3.  *   
  4.  * @author liaokailin  
  5.  */  
  6. public class MyCondition implements Condition {  
  7.   
  8.     /**  
  9.      * 返回true 生成bean  
  10.      * 返回false 不生成bean   
  11.      */  
  12.     @Override  
  13.     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {  
  14.         Map<String, Object> map = metadata.getAnnotationAttributes(Component.class.getName());  
  15.         return “MyCondition”.equals(map.get(“value”).toString());  
  16.     }  
  17.   
  18. }  

MyCondition实现接口Condition,在matches方法中获取bean上注解Component信息,如果bean名称等于MyCondition返回true,否则返回false,bean不会被创建。

 

回到前面Application的分析,Application上不存在Conditional,因此shouldSkip返回false,代码继续执行

 

[html] view plain copy

  1. ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);  

处理Scope注解信息,默认是单例bean

执行

[html] view plain copy

  1. AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);  

处理一些常见注解信息

 

[html] view plain copy

  1. static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {  
  2.         if (metadata.isAnnotated(Lazy.class.getName())) {  
  3.             abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean(“value”));  
  4.         }  
  5.         else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) {  
  6.             abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean(“value”));  
  7.         }  
  8.   
  9.         if (metadata.isAnnotated(Primary.class.getName())) {  
  10.             abd.setPrimary(true);  
  11.         }  
  12.         if (metadata.isAnnotated(DependsOn.class.getName())) {  
  13.             abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray(“value”));  
  14.         }  
  15.   
  16.         if (abd instanceof AbstractBeanDefinition) {  
  17.             AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;  
  18.             if (metadata.isAnnotated(Role.class.getName())) {  
  19.                 absBd.setRole(attributesFor(metadata, Role.class).getNumber(“value”).intValue());  
  20.             }  
  21.             if (metadata.isAnnotated(Description.class.getName())) {  
  22.                 absBd.setDescription(attributesFor(metadata, Description.class).getString(“value”));  
  23.             }  
  24.         }  
  25.     }  

处理Lazy、Primary、DependsOn、Role、Description等注解

 

最后调用

[html] view plain copy

  1. BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);  

注册bean信息,在注册bean信息之前通过

[html] view plain copy

  1. String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));  

获取bean名称

bean的注册调用为

[html] view plain copy

  1. registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());  

该代码在上篇中已有说明。

 

此时Application对应bean已创建完成。

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/zl1zl2zl3/article/details/79180617
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞