寫在前面的話
相關背景及資源:
曹工說Spring Boot源碼(1)-- Bean Definition到底是什么,附spring思維導圖分享
曹工說Spring Boot源碼(2)-- Bean Definition到底是什么,咱們對着接口,逐個方法講解
曹工說Spring Boot源碼(3)-- 手動注冊Bean Definition不比游戲好玩嗎,我們來試一下
曹工說Spring Boot源碼(4)-- 我是怎么自定義ApplicationContext,從json文件讀取bean definition的?
曹工說Spring Boot源碼(5)-- 怎么從properties文件讀取bean
曹工說Spring Boot源碼(6)-- Spring怎么從xml文件里解析bean的
曹工說Spring Boot源碼(7)-- Spring解析xml文件,到底從中得到了什么(上)
曹工說Spring Boot源碼(8)-- Spring解析xml文件,到底從中得到了什么(util命名空間)
曹工說Spring Boot源碼(9)-- Spring解析xml文件,到底從中得到了什么(context命名空間上)
工程結構圖:
概要
本篇已經是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命名空間的context:property-override、context:property-placeholder,這兩個呢,主要是獲得了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 tx:annotation-driventag 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呢,也會放到后面再講,這是個重頭。
如果有幫助,大家點個贊哈。