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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取 2000+ 道 Java 面试题

前言

通过前面的文章了解到在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. * Exclude specific auto-configuration classes such that they will never be applied.
  11. * @return the classes to exclude
  12. */
  13. Class<?**>**[] exclude() default {};
  14. }

发现不存在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. * Explicitly specify the name of the Spring bean definition associated
  8. * with this Configuration class. If left unspecified (the common case),
  9. * a bean name will be automatically generated.
  10. * <**p**>The custom name applies only if the Configuration class is picked up via
  11. * component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}.
  12. * If the Configuration class is registered as a traditional XML bean definition,
  13. * the name/id of the bean element will take precedence.
  14. * @return the specified bean name, if any
  15. * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
  16. */
  17. String value() default “”;
  18. }

发现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. AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
  4. if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
  5. return;
  6. }
  7. ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
  8. abd.setScope(scopeMetadata.getScopeName());
  9. String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
  10. AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
  11. if (qualifiers != null) {
  12. for (Class<? extends Annotation> qualifier : qualifiers) {
  13. if (Primary.class.equals(qualifier)) {
  14. abd.setPrimary(true);
  15. }
  16. else if (Lazy.class.equals(qualifier)) {
  17. abd.setLazyInit(true);
  18. }
  19. else {
  20. abd.addQualifier(new AutowireCandidateQualifier(qualifier));
  21. }
  22. }
  23. }
  24. BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
  25. definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
  26. BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
  27. }

首先来看

[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. if (phase == null) {
  6. if (metadata instanceof AnnotationMetadata &&
  7. ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
  8. return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
  9. }
  10. return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
  11. }
  12. List<**Condition**> conditions = new ArrayList<**Condition**>();
  13. for (String[] conditionClasses : getConditionClasses(metadata)) {
  14. for (String conditionClass : conditionClasses) {
  15. Condition condition = getCondition(conditionClass, this.context.getClassLoader());
  16. conditions.add(condition);
  17. }
  18. }
  19. AnnotationAwareOrderComparator.sort(conditions);
  20. for (Condition condition : conditions) {
  21. ConfigurationPhase requiredPhase = null;
  22. if (condition instanceof ConfigurationCondition) {
  23. requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
  24. }
  25. if (requiredPhase == null || requiredPhase == phase) {
  26. if (!condition.matches(this.context, metadata)) {
  27. return true;
  28. }
  29. }
  30. }
  31. return false;
  32. }

该代码判断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. }

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

[html] view plain copy

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

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

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

[html] view plain copy

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

创建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. * 返回true 生成bean
  9. * 返回false 不生成bean
  10. */
  11. @Override
  12. public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  13. Map<**String, Object>** map = metadata.getAnnotationAttributes(Component.class.getName());
  14. return “MyCondition”.equals(map.get(“value”).toString());
  15. }
  16. }

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. if (metadata.isAnnotated(Primary.class.getName())) {
  9. abd.setPrimary(true);
  10. }
  11. if (metadata.isAnnotated(DependsOn.class.getName())) {
  12. abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray(“value”));
  13. }
  14. if (abd instanceof AbstractBeanDefinition) {
  15. AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
  16. if (metadata.isAnnotated(Role.class.getName())) {
  17. absBd.setRole(attributesFor(metadata, Role.class).getNumber(“value”).intValue());
  18. }
  19. if (metadata.isAnnotated(Description.class.getName())) {
  20. absBd.setDescription(attributesFor(metadata, Description.class).getString(“value”));
  21. }
  22. }
  23. }

处理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已创建完成。


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring boot实战(第九篇)Application创建源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏