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