曹工說Spring Boot源碼(10)-- Spring解析xml文件,到底從中獲得了什麼(context:annotation-config 解析)

寫在前面的話

相關背景及資源:html

曹工說Spring Boot源碼(1)-- Bean Definition究竟是什麼,附spring思惟導圖分享java

曹工說Spring Boot源碼(2)-- Bean Definition究竟是什麼,我們對着接口,逐個方法講解git

曹工說Spring Boot源碼(3)-- 手動註冊Bean Definition不比遊戲好玩嗎,咱們來試一下面試

曹工說Spring Boot源碼(4)-- 我是怎麼自定義ApplicationContext,從json文件讀取bean definition的?spring

曹工說Spring Boot源碼(5)-- 怎麼從properties文件讀取beanjson

曹工說Spring Boot源碼(6)-- Spring怎麼從xml文件裏解析bean的框架

曹工說Spring Boot源碼(7)-- Spring解析xml文件,到底從中獲得了什麼(上)ide

曹工說Spring Boot源碼(8)-- Spring解析xml文件,到底從中獲得了什麼(util命名空間)spring-boot

曹工說Spring Boot源碼(9)-- Spring解析xml文件,到底從中獲得了什麼(context命名空間上)post

工程代碼地址 思惟導圖地址

工程結構圖:

概要

本篇已是spring源碼第10篇了,爲了讓新同窗也能知道我在講什麼,因此有些東西必須得重複一下。

先給你們看看spring支持的xml配置,我列了個表格以下:

namespace element
util constant、property-path、list、set、map、properties
context property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export、mbean-server
beans import、bean、alias
task annotation-driven、scheduler、scheduled-tasks、executor
cache advice、annotation-driven
aop config、scoped-proxy、aspectj-autoproxy

我題目的意思是,spring在解析每一個不一樣的xml元素時,實際上是有共性的。全部這些元素的解析器,都實現了BeanDefinitionParser。這個接口只有一個方法,做用就是解析元素時,根據元素的配置,來收集beanDefinition,正所謂:條條大道通羅馬,各類xml配置元素,各類註解配置,就是那些大道,羅馬是什麼?

就是beanDefinition

從第一篇到如今,已經第10篇了,咱們還在講bean definition,其實就是由於,只有深入地理解了它,後面才能更方便地理解spring boot,理解configuration註解,理解enable,理解自動裝配。

前面咱們講了util命名空間,spring從中主要得到了幾個工廠bean類型的beanDefinition;也講了context命名空間的 ,這兩個呢,主要是得到了 beanFactoryPostProcessor這樣的有特殊技能的bean的 beandefinition

以上呢,注意,都是beanDefinition,不是bean。拿java舉例,前者是class,後者是instance。

本講,繼續context命名空間。

context:annotation-config

說明

該元素至關重要,xml時代,基本是必不可少。我專門找了個幾年前的項目,如下是截圖:

可是,爲何要配置這個?估計不少人到如今也是一臉懵逼,包括以前的我;配置了以後,有什麼用?仍是一臉懵逼;再問你,爲啥spring boot時代不須要配置這個了呢?

想必,面試這麼隨便問兩下,不少人也答不上吧,這講咱們就來說講它。

先看看xsd裏的說明:

Activates various annotations to be detected in bean classes: Spring's @Required and@Autowired, as well as JSR 250's @PostConstruct, @PreDestroy and @Resource (if available),JAX-WS's @WebServiceRef (if available), EJB3's @EJB (if available), and JPA's@PersistenceContext and @PersistenceUnit (if available). Alternatively, you maychoose to activate the individual BeanPostProcessors for those annotations.Note: This tag does not activate processing of Spring's @Transactional or EJB3's@TransactionAttribute annotation. Consider the use of the tag for that purpose.

我用我剛過線的六級水平翻譯一下:

使bean class中的多種註解能夠被識別:

Spring提供的@Required、@Autowired;

JSR 250提供的@PostConstruct, @PreDestroy,@Resource

JAX-WS 提供的@WebServiceRef

EJB3 提供的 @EJB

JPA 提供的@PersistenceContext and @PersistenceUnit

另外,你也能夠選擇激活單獨的對應這些註解的BeanPostProcessors。

注意,這個註解不能激活 @Transactional的註解的識別,若是要識別這個,請使用 tx:annotation-driven

反正呢,若是你項目裏要用這一堆註解,確定得有對應的代碼來解析這些註解吧,那是什麼代碼來解析呢?

細心的同窗可能看到了,就是BeanPostProcessor。這個註解呢,其實就是註冊一堆的BeanPostProcessor。

用法

咱們在xml中配置了2個類:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context
                 http://www.springframework.org/schema/context/spring-context.xsd">

    <bean class="org.springframework.contextnamespace.TestService"></bean>

    <bean class="org.springframework.contextnamespace.TestController"></bean>
</beans>
@Controller
@Data
public class TestController {
    @Autowired
    private TestService testService;

}
@Service
class TestService {
}

測試代碼:

package org.springframework.contextnamespace;


@Slf4j
public class MainClassForTestAnnotationConfig {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                new String[]{"classpath:context-namespace-test-annotation-config.xml"},false);
        context.refresh();

        Map<String, Object> map = context.getDefaultListableBeanFactory().getAllSingletonObjectMap();
        log.info("singletons:{}", JSONObject.toJSONString(map));

        List<BeanDefinition> list =
                context.getBeanFactory().getBeanDefinitionList();
        MyFastJson.printJsonStringForBeanDefinitionList(list);

        Object testService = context.getBean(TestService.class);
        System.out.println("testService bean:" + testService);

        Object bean = context.getBean(TestController.class);
        System.out.println("testController bean:" + bean);

    }
}

測試程序很簡單,就是getBean來獲取兩個service,你們以爲注入會成功嗎?

咱們看答案吧:

testService bean:org.springframework.contextnamespace.TestService@236e3f4e

testController bean:TestController(testService=null)

能夠看到,沒注入,說明咱們配置了@autowired,可是沒生效。

怎麼才能注入呢?很簡單啊。在xml中配置以下元素便可:

<context:annotation-config></context:annotation-config>

此次再看測試代碼的輸出:

此次能夠看到,已經注入了。

等價用法

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context
                 http://www.springframework.org/schema/context/spring-context.xsd">
    //註釋之,使用<bean>聲明一個AutowiredAnnotationBeanPostProcessor
    <!--<context:annotation-config></context:annotation-config>-->
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

    <bean class="org.springframework.contextnamespace.TestService"></bean>

    <bean class="org.springframework.contextnamespace.TestController"></bean>
</beans>

測試:

元素解析

ContextNamespaceHandler,咱們能夠找到該元素的解析類:

public void init() {
    ...
    registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
    ...
}

這個類很簡單,因此這裏直接一覽全局:

public class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser {

   public BeanDefinition parse(Element element, ParserContext parserContext) {
      Object source = parserContext.extractSource(element);
       
      // 這裏,把支持的註解的解析代碼所有註冊到beanFactory
      // Obtain bean definitions for all relevant BeanPostProcessors.
      Set<BeanDefinitionHolder> processorDefinitions =
            AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
      //檢查有沒有嵌套元素啥的,不用管
      // Register component for the surrounding <context:annotation-config> element.
      CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
      parserContext.pushContainingComponent(compDefinition);

      // Nest the concrete beans in the surrounding component.
      for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
         parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));
      }

      // Finally register the composite component.
      parserContext.popAndRegisterContainingComponent();

      return null;
   }

}

這個解析類,獨具一格,類層次也很簡單,直接就實現了BeanDefinitionParser,不想前面那些類的層次那麼複雜。這個類的重點方法是在:

AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);

這局呢,裏面會註冊各類註解的解析代碼(一些beanPostProcessor)到beanFactory。

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
            BeanDefinitionRegistry registry, Object source) {

        Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
        // 用於支持Configuration註解,註冊了一個beanDefinition,其類別爲BeanPostProcessor,具體bean class爲 ConfigurationClassPostProcessor
        if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

            // 用於支持Autowired註解,註冊了一個beanDefinition,其類別爲 BeanFactoryPostProcessor,具體bean class爲 AutowiredAnnotationBeanPostProcessor
        if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
        }
            // 用於支持Required註解,註冊了一個beanDefinition,其類別爲BeanPostProcessor,具體bean class爲 RequiredAnnotationBeanPostProcessor
        if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
        }
            // 用於支持JSR-250註解,註冊了一個beanDefinition,其類別爲BeanPostProcessor,具體bean class爲 CommonAnnotationBeanPostProcessor
        // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
        if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
        if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            ...支持jpa的相關代碼
        }

        return beanDefs;
    }

彙總一下,就是:

支持解析的註解 beanDefinition中class的類別 beanClass
Configuration BeanFactoryPostProcessor ConfigurationClassPostProcessor
Autowired BeanPostProcessor AutowiredAnnotationBeanPostProcessor
Required BeanPostProcessor RequiredAnnotationBeanPostProcessor
PostConstruct/PreDestroy/
Resource/EJB/WebServiceRef
BeanPostProcessor CommonAnnotationBeanPostProcessor

咱們下面再簡單地列舉一下,這幾個beanClass的繼承結構:

AutowiredAnnotationBeanPostProcessor簡單分析

咱們在前面的例子中,進行了如下注入:

@Controller
@Data
public class TestController {
    @Autowired
    private TestService testService;



}

你們能夠想象下,這個「TestService testService字段,須要注入」,這個元數據會存儲在哪?

BeanDefinition?咱們看看呢:

這個圖,就是我用json輸出的TestController的beanDefinition,這裏面並無出現TestService的聲影。

我來告訴你們,這個數據,實際是在getBean的時候,由AutowiredAnnotationBeanPostProcessor來獲取的,具體的堆棧,你們能夠看看:

具體的方法就是在下邊的buildAutowiringMetadata

private InjectionMetadata findAutowiringMetadata(Class<?> clazz) {
   // Quick check on the concurrent map first, with minimal locking.
   InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
   if (metadata == null) {
      synchronized (this.injectionMetadataCache) {
         metadata = this.injectionMetadataCache.get(clazz);
         if (metadata == null) {
            // 這裏啊,去尋找要自動注入的數據
            metadata = buildAutowiringMetadata(clazz);
            this.injectionMetadataCache.put(clazz, metadata);
         }
      }
   }
   return metadata;
}

咱們能夠再跟一步,進入到這個方法,能夠看到,這裏面反射遍歷了這個class的field、method

private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
   LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
   Class<?> targetClass = clazz;

   do {
      LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
      //遍歷field,看看是否是註解了@Autowired
      for (Field field : targetClass.getDeclaredFields()) {
         //查找field上的@Autowired
         Annotation annotation = findAutowiredAnnotation(field);
         if (annotation != null) {
            if (Modifier.isStatic(field.getModifiers())) {
               if (logger.isWarnEnabled()) {
                  logger.warn("Autowired annotation is not supported on static fields: " + field);
               }
               continue;
            }
            boolean required = determineRequiredStatus(annotation);
            currElements.add(new AutowiredFieldElement(field, required));
         }
      }
      //遍歷方法
      for (Method method : targetClass.getDeclaredMethods()) {
         Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
         // 查看method上的@autowired註解
         Annotation annotation = BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod) ?
               findAutowiredAnnotation(bridgedMethod) : findAutowiredAnnotation(method);
         if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
            if (Modifier.isStatic(method.getModifiers())) {
               if (logger.isWarnEnabled()) {
                  logger.warn("Autowired annotation is not supported on static methods: " + method);
               }
               continue;
            }
            if (method.getParameterTypes().length == 0) {
               if (logger.isWarnEnabled()) {
                  logger.warn("Autowired annotation should be used on methods with actual parameters: " + method);
               }
            }
            boolean required = determineRequiredStatus(annotation);
            PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
            currElements.add(new AutowiredMethodElement(method, required, pd));
         }
      }
      elements.addAll(0, currElements);
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);

   return new InjectionMetadata(clazz, elements);
}

ok,前面的分析,讓咱們知道了,autowired相關的元數據是怎麼被查找到的,下邊,咱們看看,是怎麼實現注入的,下邊這個方法呢,就是在bean建立完了,可是屬性還沒設置時,框架去調用BeanPostProcessor時,致使AutowiredAnnotationBeanPostProcessor的下面方法被調用的。

// 這個方法依然在AutowiredAnnotationBeanPostProcessor裏面
@Override
public PropertyValues postProcessPropertyValues(
      PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {

   InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
   try {
      metadata.inject(bean, beanName, pvs);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
   }
   return pvs;
}

這個方法裏,就是檢查有沒有須要注入的。若有,調用下面方法:

public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
   Collection<InjectedElement> elementsToIterate =
         (this.checkedElements != null ? this.checkedElements : this.injectedElements);
   if (!elementsToIterate.isEmpty()) {
      boolean debug = logger.isDebugEnabled();
      for (InjectedElement element : elementsToIterate) {
         if (debug) {
            logger.debug("Processing injected method of bean '" + beanName + "': " + element);
         }
         //實現注入,其實就是給field賦值
         element.inject(target, beanName, pvs);
      }
   }
}
// 給field設置值
@Override
   protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
      Field field = (Field) this.member;
      try {
         Object value;
         if (this.cached) {
            value = resolvedCachedArgument(beanName, this.cachedFieldValue);
         }
         else {
            DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required);
            Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
            TypeConverter typeConverter = beanFactory.getTypeConverter();
            // 這裏從beanFactory去找能知足要求的bean
            value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
            synchronized (this) {
               if (!this.cached) {
                  if (value != null || this.required) {
                     this.cachedFieldValue = descriptor;
                     //把bean之間的依賴關係,存起來
                     registerDependentBeans(beanName, autowiredBeanNames);
                     if (autowiredBeanNames.size() == 1) {
                        String autowiredBeanName = autowiredBeanNames.iterator().next();
                        if (beanFactory.containsBean(autowiredBeanName)) {
                           if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                              this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName);
                           }
                        }
                     }
                  }
                  else {
                     this.cachedFieldValue = null;
                  }
                  this.cached = true;
               }
            }
         }
         if (value != null) {
            // 這裏就是設置field的value的地方了
            ReflectionUtils.makeAccessible(field);
            field.set(bean, value);
         }
      }
      catch (Throwable ex) {
         throw new BeanCreationException("Could not autowire field: " + field, ex);
      }
   }
}

總結

這節就分析這麼多吧,經過context:annotation-config這個元素,咱們得到了什麼呢?

1個beanFactoryPostProcessor,多個beanPostProcessor

咱們還分析了autowired的實現,其餘的幾個註解,除了@configuration外,都比較相似,就不講了。

@configuration呢,也會放到後面再講,這是個重頭。

若是有幫助,你們點個贊哈。

相關文章
相關標籤/搜索