基于上一篇博客的小demo, 我通过debug看了下spring 大概的工作流程,做了点小笔记:
package com.test.main;
import com.test.app.Appconfig;
import com.test.dao.TestDao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
/**
* AnnotationConfigApplicationContext (后面用 registry 代称) 构造器中做了以下事情:
* 1. 父类构造器里初始化了一个 DefaultListableBeanFactory,包含一个beanDefinitionMap 和一个 beanDefinitionNames
* 2. 构造了一个reader
* 2.1 reader 构造器里,注册若干个后置处理器 (详情查看AnnotationConfigUtils.registerAnnotationConfigProcessors())
* 3. 构造了一个scanner
* 4. 把registry构造方法传入的class (e.g.Appconfig.class)作为bean 也注册到 registry 的 DefaultListableBeanFactory中
* 5. refresh()// TODO
*
* 注意:
* #2.1注册后置处理器 和 #4 注册Appconfig 都是
* 通过 registry.registerBeanDefinition(beanName, definition); 放入到registry的 defaultListableBeanFactory中的beanDefinitionMap的
*
* */
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class); //把spring所有的前提环境准备好
TestDao testDao = annotationConfigApplicationContext.getBean(TestDao.class);
testDao.query();
}
}
关键字理解:
1. BeanDefinition
Java 中的class 用类Class 表示 <-----> Spring中的bean 用类BeanDefinition表示
BeanDefinition中存了bean的定义信息。比如: 类名、scope、属性、构造函数参数列表、是否是单例类、是否是懒加载等
BeanDefinition 一定是Class, 但Class 不一定是BeanDefinition
Spring 内的bean来源于:
- spring 内部的bean
- @annotation 添加该注解的类会被Spring 识别为bean
- @bean 添加该注解的方法返回的类会被Spring 识别为bean
- <bean> 在XML中通过bean 标签定义的类会被Spring 识别为bean
2. BeanDefinitionHolder
是一个类似于map的数据结构,key是一个BeanDefinition对象,value是BeanDefinition对应的bean name。用于方便中间传值封装的一个对象,最后key和value 分别会被放到spring bean工厂的bean map中
3. DefaultListableBeanFactory
是整个bean加载的核心,是spring注册bean及加载bean的默认实现。
以下问题通过阅读源码得到了证实:
1. 如果单例类中注入了原型(prototype 类型)的对象,这样元型对象就失去了意义。2种解决办法:
- 添加注解@lookup。 在单例类中新加一个带有该注解的方法。被该标注的方法会被重写,然后根据其返回值的类型,Spring容器调用BeanFactory的getBean()方法来返回一个bean。
package com.test.dao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
@Repository
@Scope("prototype")
public class TestDao2 {
public void query(){
System.out.println("This is TestDao2...");
}
}
package com.test.dao;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Repository;
@Repository
public class TestDao { ;
public void query(){
System.out.println("Hello Spring!");
System.out.println(getTestDao2().hashCode());
}
@Lookup
public TestDao2 getTestDao2(){
return null;
}
}
- 放弃依赖注入,让单例类实现接口ApplicationContextAware,并重写方法setApplicationContext 拿到 ApplicationContext 对象,然后便可以通过ApplicationContext.getBean("beanname")每次拿到一个新对象。
package com.test.dao;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Repository;
;
@Repository
public class TestDao implements ApplicationContextAware {
ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void query(){
System.out.println("Hello Spring!");
System.out.println(applicationContext.getBean("testDao2").hashCode());
}
}
方法2 的原理是通过ApplicationContextAwareProcessor Bean后置处理器实现的。这个后置处理器在Bean初始化之后去检查该bean是否继承了接口ApplicationContextAware,如果继承了就调用bean的setApplicationContext 方法把ApplicationContext 传给该bean