曹工说Spring Boot源码(11)-- context:component-scan,你真的会用吗(这次来说说它的奇技淫巧)


写在前面的话

相关背景及资源:

工程代码地址 思维导图地址

工程结构图:

概要

本篇已经是spring源码第11篇,最近都在讲解:spring解析xml文件,到底获得了什么?获得了什么呢,感兴趣的可以挑选感兴趣的看;目前呢,已经讲到了context命名空间,接下来准备讲解component-scan,但是吧,这个真的是一个重量级的嘉宾,且不说原理,光是用法,就够我们感受感受啥叫主角了。

常规用法

我们在package:org.springframework.contextnamespace.componentscantest下存放了以下几个文件:

MainClassForTestComponentScan.java 测试类,包含main方法,不是bean

PersonTestController.java 使用了@Controller注解,里面使用@Autowired自动注入了PersonService

PersonService.java 使用了@Service注解

下边看下代码:

//定义一个bean
package org.springframework.contextnamespace.componentscantest;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Slf4j
@Data
@Controller
public class PersonTestController {

    @Autowired
    private PersonService personService;
}

// 再一个bean
package org.springframework.contextnamespace.componentscantest;

import org.springframework.stereotype.Service;

@Service
public class PersonService {
    private String personname;
}

//测试代码
package org.springframework.contextnamespace.componentscantest;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.MyFastJson;

import java.util.List;
import java.util.Map;

@Slf4j
public class MainClassForTestComponentScan {

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


        List list =
                context.getBeanFactory().getBeanDefinitionList();
        // 我自己的工具类,使用json输出bean definition
        MyFastJson.printJsonStringForBeanDefinitionList(list);
        
        Object bean = context.getBean(PersonTestController.class);
        System.out.println("PersonController bean:" + bean);

    }
}

xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>

    
    

输出:

PersonController bean:PersonTestController(personService=org.springframework.contextnamespace.componentscantest.PersonService@3e11f9e9)

可以看到,注入成功。

我代码里,其实还输出了全部的beanDefinition,我简单整理了一下,一共包含了如下几个:

beanDefinition中的beanClass
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
org.springframework.contextnamespace.componentscantest.PersonService 我们自己的业务bean
org.springframework.contextnamespace.componentscantest.PersonTestController 业务bean
org.springframework.context.annotation.ConfigurationClassPostProcessor
org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor

看来,一个简单的注解,背后却默默做了很多骚操作啊,除了自己的业务bean外,还有5个框架自带的bean,类型呢,从命名可以看出,都是些什么PostProcessor,有兴趣的,可以翻到我前一篇,里面讲解了AutowiredAnnotationBeanPostProcessor

阅读理解

我们从spring-context.xsd文件可以找到这个元素的官方说明。

Scans the classpath for annotated components that will be auto-registered as
Spring beans. By default, the Spring-provided @Component, @Repository,
@Service, and @Controller stereotypes will be detected.


Note: This tag implies the effects of the 'annotation-config' tag, activating @Required,
@Autowired, @PostConstruct, @PreDestroy, @Resource, @PersistenceContext and @PersistenceUnit
annotations in the component classes, which is usually desired for autodetected components
(without external configuration). Turn off the 'annotation-config' attribute to deactivate
this default behavior, for example in order to use custom BeanPostProcessor definitions
for handling those annotations.


Note: You may use placeholders in package paths, but only resolved against system
properties (analogous to resource paths). A component scan results in new bean definition
being registered; Spring's PropertyPlaceholderConfigurer will apply to those bean
definitions just like to regular bean definitions, but it won't apply to the component
scan settings themselves.

See Javadoc for org.springframework.context.annotation.ComponentScan for information
on code-based alternatives to bootstrapping component-scanning.

我用我的425分压线4级翻译了一下:

扫描类路径下的注解组件,它们将会被主动注册为spring bean。默认情况下,可以识别以下注解:
@Component, @Repository,@Service, and @Controller。

注意:这个元素隐含了https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-xml-demo/src/main/java/org/springframework/contextnamespace/componentscan

最后这个自定义注解的内容,小马哥的spring boot编程思想里也提到了,在161页,我手边没有电子版本,所以抱歉了。

总结

component-scan,用了这么些年,看来真的只是用,里面的原理还是一知半解,经过上面的分析,我也自己系统梳理了一遍。大家看看有啥问题的,欢迎指出来,一起进步。

相关