面试题
来自面试官发自灵魂深处的拷问:谈谈你对spring的理解;
一脸懵逼的求职者的内心活动:啥? 具体的问题是什么?现在的面试都不按套路出牌了吗? 抛出一个这么大的问题,你让我怎么回答?
一脸懵逼的求职者的回答:额~ ~ ~ 这个。。。。 额~ ~ ~ 那个。。。。额~ ~ ~ 不知道唉。。。
为什么面试官要问这种问题?
不可否认,现在的大多数的面试出题方式都是这样的,惊人的相似,就是面试官喜欢抛出一个问题,看你能讲多深,考的就是你对这项技术的深度和广度,深度就是你对技术底层了解程度,广度就是这项技术的应用范围,以及扩展方向。这时候一个杠精同学就要发问了:“为什么要知道这些那么底层的东西呢?我只要会用不就行了,总是面试的时候造火箭,实际工作的时候却让我拧螺丝”,话虽说的没错,但你需要考虑的是,这个东西大家都会用,又不只你会,既然大家都会的东西,如果体现出你的价值呢?这就需要考虑到深度了,小红和小明都会用spring,但是小明知道它底层的执行机制和原理,这里需要注意的是,你回答的深度就决定了你薪资和待遇,这是非常重要的,那我们要如何回答这种问题呢?其实很简单,先梳理出大概的脉络,然后一个个地深入讲解;我从今年写的博客都是这样的,由浅入深地细化讲解,并且会给出代码和流程图。
作为一名程序员,如果想要向专家方向发展,就必须懂的编程的思想,算法的底层执行原理,代码只是其次,因为原理就一种,实现方式多种多样,一个接口,让一万个人去写实现,这一万个人的实现方式都不会一样;这就是思想的重要性。
Spring IOC
首先,在此之前,我们就必须先知道什么是ioc,ioc叫做控制反转,也可以称为依赖注入(DI),实际上依赖注入是ioc的另一种说法,
1.谁控制谁?:在以前,对象的创建和销毁都是由用户控制的,用了ioc之后,对象的创建和销毁就都交给容器来控制了,用户就不用管这些,只关注业务需求就好了;
2.什么是反转?:既然叫反转,肯定就有正转,正转其实就是对象去找实例,而反转就反过来了嘛,让实例来找对象;怎么找呢?当然是通过容器啦!
3.谁依赖谁?:在spring项目中,将对象理解为Bean,也可以叫bean对象,这个bean和容器之间有个依赖关系,bean对象的创建是依赖容器的,就好像孩子依赖父母一样,孩子不能自己生出自己,需要父母的合作才能出生,这里的孩子就是bean,父母就是容器;
4.谁注入谁?:通过容器注入了bean对象,而且这个过程是自动化的,也就是说容器会自动找到和bean对象匹配的类型实例注入到对象中;
spring ioc的加载过程
了解完控制反转和依赖注入,接下来我们在看看ioc的加载过程,ioc的整个加载过程如下图,先看看大致的流程,然后再慢慢深入 (其中黄色的框内是注释内容)
1、首先,通过BeanDefinitionReader 读取指定的配置文件生成bean的定义信息,然后到完整的bean定义信息(BeanDefinition对象),注意这里只是存储bean的定义信息,还没有实例化bean对象;就像工厂里面一样,原材料已经准备好了,但是还没有进行生产,原材料就是beanDefinition,生产就是实例化
2、在 BeanDefinition
和 完整BeanDefinition
中间通过一个后置增强器,可以对bean的定义信息进行统一修改,只需要实现 BeanFactoryPostProcessor
接口即可,这个后置增强器是可以有多个的,你只要在不同的类实现多个 BeanFactoryPostProcessor
接口就会执行多次,就像这样:
package com.Spring.Boot.init;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
/**
* 扩展方法--后置增强器(可修改bean的定义信息)
*/
@Component
public class ExtBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// BeanDefinition studentService = beanFactory.getBeanDefinition("studentService");
System.out.println("扩展方法--可进行修改beanDefinition的定义信息");
}
}
复制代码
3、得到完整BeanDefinition
之后就可以进行创建对象了,这整个过程被称为 bean 的生命周期,也就是从实例化到销毁的过程;那么这时候爱学习童鞋就要发问了:“对象创建和销毁有这么麻烦嘛?直接反射实例化一个对象不就行了嘛?为啥还有初始化?”; 首先,这是个好问题,来,我们先把掌声送给这位发问的同学;我想说的是,就算是普通的new一个对象出来,里面也会经过实例化和初始化,接下来我们重点讲bean的生命周期;
Spring Bean的生命周期
粗略来看,bean的生命周期主要分为以下4个步骤
但其实,它的内部蕴含了很多东西,让我们看看细化后的流程图;
怎么样? 是不是看到了很多没见过的东西?好像认识几个,但大多都是没见过的东东,不知道不要紧,接下来我们一个个地讲解
接下来我们要将1、3、4 放到一起讲,是因为它们是在同一个接口里面的,实现InstantiationAwareBeanPostProcessor
接口即可
package com.Spring.Boot.init;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
// 实例化前置
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("postProcessBeforeInstantiation被调用了----在对象实例化之前调用-----beanName:" + beanName);
// 默认什么都不做,返回null
return null;
}
// 实例化后置
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInstantiation被调用了---------beanName:" + beanName);
//默认返回true,什么也不做,继续下一步
return true;
}