spring总是有一种神奇的魔力,让人想要去探究他到底是做了什么.
提前搭建好工程
- 不要选择Spring Initializr, 那是使用SpringBoot的,不利于学习使用.
一路下一步, 填写项目名称, finish.
可以看到,在项目目录下的lib中,IDEA会帮我们把包导好.
直接开始代码
启动类放置的位置是按照SpringBoot的习惯,实际放在哪里都可以.
// Hello类中的代码
public class Hello {
private String message = "";
public String getMessage(){
return message;
}
public void setMessage(String message){
this.message = message;
}
public Hello(){}
public Hello(String message){
this.message = message;
}
}
// HelloConfig类中的代码
@Configuration
@ComponentScan(basePackages = "com.echi.demo")
public class HelloConfig {
@Bean
public Hello hello(){
return new Hello("hello world");
}
}
// 主启动类的代码
public class MainClass {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(HelloConfig.class);
Hello hello = (Hello)applicationContext.getBean("hello");
System.out.println(hello.getMessage());
}
}
因为是学习,所以没有加入SpringMvc中关于父子容器,路径拦截的配置, 简简单单创建一个容器将bean放进去再取出来就好了.
开始调试
因为是要看看spring是获取bean的,所以直接将断点打在getBean()这行.
从下面将spring中的代码贴上来
spring代码好看的一点就是,名字都很长, 可以做到见名知意
bean没有标注@Lazy
// 类名 AbstractApplicationContext
@Override
public Object getBean(String name) throws BeansException {
// 看看这个beanFactory是不是还活着
// 如果已经死亡抛出一个IllegalStateException
assertBeanFactoryActive();
// 根据名称去获取bean
return getBeanFactory().getBean(name);
}
// 类名 AbstractBeanFactory
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// 这句和返回值没关系可以不看, 是处理bean的名称的.
// bean可以拥有别名,也可能是由factoryBean导致的名称干扰
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 这句已经有注释, 是 直接去单例缓存池中获取bean.
// 因为我们没有配置懒加载,所以这边会获取到bean
// 返回值就是我们在config中配置的Hello对象
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 省略部分是一些log
...
// 此处是做一些判断,因为bean已经取出来了.但是取出来的有可能是factoryBean
// 如果是factoryBean,是不能直接用的,还要通过factoryBean来获取bean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 这部分代码是没取到时的处理,稍后再展开
...
}
// Check if required type matches the type of the actual bean instance.
// 做最后的检查
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
// 检查代码省略
...
}
// 已经获取到bean, 也已经做过检查, 确认没问题了 将bean返回
return (T) bean;
}
在上面这段代码可以看到,成功获取到bean.
但是只有get()的动作,那什么时候放进去的呢?
既然在getBean()的时候没有看到在哪里放进去,那断点就往前打一句
// 类名 AnnotationConfigApplicationContext
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
// 做一些初始化的工作
// 在这里初始化了
// - beanFactory
// - AnnotatedBeanDefinitionReader
// - ClassPathBeanDefinitionScanner
this();
register(annotatedClasses);
// 主要内容在这个方法内
refresh();
}
// 类名 AbstractApplicationContext
// 这个方法里全是调用,调用链很深,就不深入进去了.
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 省略部分代码
...
try {
...
// 在这一个方法内,将Hello类的BeanDefinition装入beanDefinitionMap中.
invokeBeanFactoryPostProcessors(beanFactory);
...
// 在这个方法内将Hello类装入到单例缓存池中.
finishBeanFactoryInitialization(beanFactory);
...
}
catch (BeansException ex) {
// 省略不必要的代码
...
}
finally {
...
}
}
}
// 类名 AbstractApplicationContext
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 省略代码
...
// 调用了beanFactory的方法将类装载进单例缓存池
beanFactory.preInstantiateSingletons();
}
// 类名 DefaultListableBeanFactory
@Override
public void preInstantiateSingletons() throws BeansException {
...
// 获取bean定义的名称
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// 循环去挨个实例化
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 如果是factoryBean, 有factoryBean的实例化方式
if (isFactoryBean(beanName)) {
// 使用factoryBean的方式创建bean
...
}
// 如果是单纯的bean,直接走上面getBean时省略的步骤.
// 在getBean()中会创建bean
else {
getBean(beanName);
}
}
}
for (String beanName : beanNames) {
// 调用bean初始化后的回调
...
}
}
仔细看一下getBean中创建bean的方式
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// 因此这里讨论的是使用该方法创建bean,所以单例缓存池中应该是没有缓存该bean的.
Object sharedInstance = getSingleton(beanName); // 返回null
if (sharedInstance != null && args == null) {
// 不讨论此处
...
}
else {
// 在单例缓存池中没有找到bean, 进入该判断分支
// 判断是否有依赖循环的情况
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 看看有没有父容器,是不是在父容器中已经有该bean
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 没有父容器, 此处不讨论
...
}
if (!typeCheckOnly) {
// 将该bean标记为已创建, 防止多线程竞争时创建多次. 内部使用的是双重检查
markBeanAsCreated(beanName);
}
try {
// 开始创建bean的流程
// 先获取beanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 检查该bean有没有依赖其他bean, 如果有需要将依赖的bean先解决
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// 解决依赖bean
...
}
// 正式开始创建bean
// 如果是单例bean,进入该分支
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
// 再内部就是一堆很深的调用,不再深入.
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 创建prototype的bean进入该分支
// prototype就是每次获取都是一个全新的bean
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
// 如果不是单例也不是原型模式, 说明是其他的 request ,session和global session
// 这几种scope都是和web请求相关的
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
...
}
return (T) bean;
}
bean标注@Lazy的情况
@Lazy表示懒加载,只有在使用的情况才会去加载.
在上面的例子中,bean的创建是在ApplicationContext applicationContext = new AnnotationConfigApplicationContext(HelloConfig.class);
这一行执行时创建的,在getBean()时直接可以取到.
加了@Lazy注解之后,bean的创建的在getBean()的时候去创建并放入单例缓存池中的.