【mini-spring】【更新中】第一章 IOC与Bean源码及思路解析
GitHub链接:mini-spring
本文仅作学习用,所出现【源码】内容皆为mini-spring中源码。
基本知识
了解IOC和Bean,了解SpringMVC。
源码分析
归根到底,这部分实际上是在逐步的分析bean的生命周期。通过一点一点增加功能和使用单元测试来学习bean生命周期的每一部分。如果你理解了bean的生命周期,简单来说:定义(bean以及对应的IOC容器)-实例化-初始化-使用-销毁,那你就可以理解为什么说IOC是Spring的核心功能:IOC将繁杂的生命周期过程逐步拆解,程序员只需要关注每个部分中需要自定义的内容,将自定义的内容写好,spring会将全部的功能拼接在一起,让你在实际开发中省去大量的时间。 非常建议反复查看源码和自己举例理解。
bean的实例化
Spring中的bean的是使用BeanFactory来产生的,也就是使用工厂类来同一创建。因此对于Bean的内容和创建过程,自然而然有以下几点:
- BeanFactory要怎么实现?或者说创建一个Bean需要什么内容?
- 创建的流程是怎样的?
这里要说明的是,应用Java复杂的继承和实现机制,最终使用的 BeanFactory 实际是DefaultListableBeanFactory。项目ioc包下有一个测试类如下:
public class BeanDefinitionAndBeanDefinitionRegistryTest {@Testpublic void testBeanFactory() throws Exception {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();BeanDefinition beanDefinition = new BeanDefinition(HelloService.class);beanFactory.registerBeanDefinition("helloService", beanDefinition);HelloService helloService = (HelloService) beanFactory.getBean("helloService");helloService.sayHello(); // 输出为 hello}
}
// 对应的hello类
class HelloService {public String sayHello() {System.out.println("hello");return "hello";}
}
代码并不复杂,我们分析一下。流程如下
- 创建 BeanFactory:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
这一行代码创建了一个 DefaultListableBeanFactory 对象,用于管理 Bean 的生命周期和依赖注入。
- 创建 BeanDefinition:
BeanDefinition beanDefinition = new BeanDefinition(HelloService.class); // 使用反射
这一行代码创建了一个 BeanDefinition 对象,用于描述 HelloService 类的信息。BeanDefinition 中包含了 Bean 的类名、构造参数、属性值等信息。显而易见,这里使用了反射来获取信息。
- 注册 BeanDefinition:
beanFactory.registerBeanDefinition("helloService", beanDefinition);// 这个注册的函数如下:
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry {private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {beanDefinitionMap.put(beanName, beanDefinition);}// 后续代码暂时不需要
}
这一行代码将 HelloService 的 BeanDefinition 注册到 beanFactory 中,并指定 Bean 的名称为 “helloService”。不难看出,BeanFactroy类内部有一个ConcurrentHashMap,注册的Bean都存储到这里,而注册的过程只是put到map中。
- 获取 Bean:
这里比较重要,可以多次阅读,尤其是多个方法的调用关系。这些调用关系在后面仍然会用到
HelloService helloService = (HelloService) beanFactory.getBean("helloService");
//getBean 调用后可以获取bean的实例
@Override
public Object getBean(String name) throws BeansException {Object sharedInstance = getSingleton(name); // 先调用这里,方法在下面。if (sharedInstance != null) {//如果是FactoryBean,从FactoryBean#getObject中创建bean 后面会讲FactoryBean。return getObjectForBeanInstance(sharedInstance, name);}// 不是的话,则使用反射获取得到BeanDefinitionBeanDefinition beanDefinition = getBeanDefinition(name);Object bean = createBean(name, beanDefinition);return getObjectForBeanInstance(bean, name);
}
// getSingleton 可以自己看代码 源码中是多级缓存的
// 代码首先在多级缓存中查找是否存在这个bean,当然在当前的例子中缓存是什么都不存在的
// 因此最终指向的是return singletonObject的部分,也就是直接返回。
@Override
public Object getSingleton(String beanName) {Object singletonObject = singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();//从三级缓存放进二级缓存earlySingletonObjects.put(beanName, singletonObject);singletonFactories.remove(beanName);}}}return singletonObject;
}
// 获取BeanDefinition,从BeanFactory的Map中获取
@Override
public BeanDefinition getBeanDefinition(String beanName) throws BeansException {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (beanDefinition == null) {throw new BeansException("No bean named '" + beanName + "' is defined");}return beanDefinition;
}
// createBean 如果是代理则返回代理对象,否则调用doCreateBean
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {//如果bean需要代理,则直接返回代理对象Object bean = resolveBeforeInstantiation(beanName, beanDefinition);if (bean != null) {return bean;}return doCreateBean(beanName, beanDefinition);
}
// doCreateBean在后面会详细说,因为包含了实例化和postprocessor的内容,只需要知道是创建了一个Bean并且返回即可
这一行代码通过 beanFactory 的 getBean() 方法获取名称为 “helloService” 的 Bean。getBean() 方法内部会根据 BeanDefinition 创建 Bean 实例,并进行依赖注入。getBean方法会先检查是否已经创建过,即是否为单例。多级缓存会在后面解释
- 调用 Bean 方法:
helloService.sayHello(); // 输出hello
总结一下,通过反射,可以获取类的所有结构信息,根据这个class可以创建BeanDefinition,这一个BeanDefinition存储的就是 HelloService的beanClass。其他属性(如 initMethodName、scope、propertyValues)默认为空,需要手动设置或通过注解解析(当然源码中的scope是默认为true,因为bean都是默认单例)。
然后根据BeanDefinition注册到 BeanFactory,调用 beanFactory.registerBeanDefinition(“helloService”, beanDefinition) 方法,BeanFactory 会保存 <beanName, BeanDefinition> 的映射(如 “helloService” -> beanDefinition)。此时 BeanFactory 只是记录了配置,并未真正创建 HelloService 实例。
完成了上述的操作,就可以批量生成 Bean 实例。每次调用 beanFactory.getBean(“helloService”) 时,首先检查缓存:如果 helloService 已创建且 scope=“singleton”,直接返回实例。这样,我们就完成了Bean的实例化。注意这里是实例化,也就是创建了一个Bean对象。而后续还可以加入初始化的操作,为Bean赋值和添加内容。
从上面的过程和代码,我们可以回答上面的两个问题:Bean的创建需要使用反射获取到类信息,流程则是先获取到Definition这样的描述信息,再将其注册到BeanFactory的Map中。当我们需要创建对应的Bean,则检测是否已经注册,如果有则使用创建好的Bean即可。
当然,对于SpringMVC中,Bean可以是一个类,这很常见,例如controller层调用service层的代码,我们肯定是将service层标识为Bean(用@service),然后在controller层中调用service层的方法或者修改对应的成员变量。因此Bean可以有属性。那我们是怎么给Bean填充属性的?这里其实在BeanDefinition的构造函数中能发现:
public BeanDefinition(Class beanClass) {this(beanClass, null);}public BeanDefinition(Class beanClass, PropertyValues propertyValues) {this.beanClass = beanClass;this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
}
有两种构造函数,首先都需要传入beanClass,这是必要的,因为有BeanClass才能创建BeanDefinition,进而注册和创建Bean。第二种构造方法还需要传入PropertyValues,也就是属性值。这里的propertyValues是一个ArrayList,内部包含的是propertyvalue,是单独的一个类,包含了name和value两个字段。
因此测试代码如下:
@Testpublic void testPopulateBeanWithPropertyValues() throws Exception {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();PropertyValues propertyValues = new PropertyValues();propertyValues.addPropertyValue(new PropertyValue("name", "derek"));propertyValues.addPropertyValue(new PropertyValue("age", 18));BeanDefinition beanDefinition = new BeanDefinition(Person.class, propertyValues);beanFactory.registerBeanDefinition("person", beanDefinition);Person person = (Person) beanFactory.getBean("person");System.out.println(person);assertThat(person.getName()).isEqualTo("derek");assertThat(person.getAge()).isEqualTo(18);}
首先还是创建factory,之后创建propertyValues(注意,这是列表),再添加两个propertyValue类型的对象。之后再获取得到BeanDefinition,并且进行注册。完成注册则可以进行Bean的实例化。这样操作就可以给Bean的属性赋值。
这种填充方式只是添加值,那如果是bean中注入bean呢?例如service层是一个bean,其中的一个方法也被声明为bean,这种情况要怎么处理?
这里需要增加一个BeanReference类,包装一个bean对另一个bean的引用。上面BeanDefinition的构造方法只有两种,因此对于Bean中有Bean的情况,我们还是使用的第二种构造方法,区别在于value的值是什么。如果value的类型是BeanReference,引用beanB,则先去实例化beanB。 由于不想增加代码的复杂度提高理解难度,暂时不支持循环依赖,后面会在高级篇中解决该问题。
有测试代码如下:
@Testpublic void testPopulateBeanWithBean() throws Exception {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();//注册Car实例PropertyValues propertyValuesForCar = new PropertyValues();propertyValuesForCar.addPropertyValue(new PropertyValue("brand", "porsche"));BeanDefinition carBeanDefinition = new BeanDefinition(Car.class, propertyValuesForCar);beanFactory.registerBeanDefinition("car", carBeanDefinition);//注册Person实例PropertyValues propertyValuesForPerson = new PropertyValues();propertyValuesForPerson.addPropertyValue(new PropertyValue("name", "derek"));propertyValuesForPerson.addPropertyValue(new PropertyValue("age", 18));//Person实例依赖Car实例propertyValuesForPerson.addPropertyValue(new PropertyValue("car", new BeanReference("car")));BeanDefinition beanDefinition = new BeanDefinition(Person.class, propertyValuesForPerson);beanFactory.registerBeanDefinition("person", beanDefinition);Person person = (Person) beanFactory.getBean("person");System.out.println(person);assertThat(person.getName()).isEqualTo("derek");assertThat(person.getAge()).isEqualTo(18);Car car = person.getCar();assertThat(car).isNotNull();assertThat(car.getBrand()).isEqualTo("porsche");}
这里的操作就是先构造car的BeanDefinition,再构造person的。而person的部分除了自身的属性值之外,还有一个BeanReference类型的value,这样再声明的BeanDefinition和注册的部分就包含了car。
完成注册之后,就可以使用工厂实例化一个名为person的bean,这个bean本身的属性值,也可以通过person获取到car这个bean。类比SpringMVC的架构,controller既可以直接调用service层的属性值,也可以调用service层中被注册为bean的方法,进而再使用。
资源与资源加载器
springboot中,Resource接口是所有资源类的抽象接口,在mini-spring中是这样的:
有三个实现类,分别为:
- FileSystemResource,文件系统资源的实现类
- ClassPathResource,classpath下资源的实现类
- UrlResource,对java.net.URL进行资源定位的实现类
而ResourceLoader接口,则是对于资源的加载器的抽象接口。在mini-spring中,默认使用DefaultResourceLoader为其实现类。有如下的测试代码。测试代码展示了怎么使用resourceLoader来进行资源的获取。
public class ResourceAndResourceLoaderTest {@Testpublic void testResourceLoader() throws Exception {DefaultResourceLoader resourceLoader = new DefaultResourceLoader();//加载classpath下的资源Resource resource = resourceLoader.getResource("classpath:hello.txt");InputStream inputStream = resource.getInputStream();String content = IoUtil.readUtf8(inputStream);System.out.println(content);assertThat(content).isEqualTo("hello world");//加载文件系统资源resource = resourceLoader.getResource("src/test/resources/hello.txt");assertThat(resource instanceof FileSystemResource).isTrue();inputStream = resource.getInputStream();content = IoUtil.readUtf8(inputStream);System.out.println(content);assertThat(content).isEqualTo("hello world");//加载url资源resource = resourceLoader.getResource("https://www.baidu.com");assertThat(resource instanceof UrlResource).isTrue();inputStream = resource.getInputStream();content = IoUtil.readUtf8(inputStream);System.out.println(content);}
}
而可以使用资源加载器进行资源的获取,就可以从xml中获取bean。虽然现在的springboot更希望用户使用注解来定义bean而不是直接在xml中定义。通过资源加载器,就可以在xml格式配置文件中声明式地定义bean的信息,资源加载器读取xml文件,解析出bean的信息,然后往容器中注册BeanDefinition。
BeanDefinitionReader是读取bean定义信息的抽象接口,XmlBeanDefinitionReader是从xml文件中读取的实现类。BeanDefinitionReader需要有获取资源的能力,且读取bean定义信息后需要往容器中注册BeanDefinition,因此BeanDefinitionReader的抽象实现类AbstractBeanDefinitionReader拥有ResourceLoader和BeanDefinitionRegistry两个属性。
测试代码如下:
public class XmlFileDefineBeanTest {@Testpublic void testXmlFile() throws Exception {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);beanDefinitionReader.loadBeanDefinitions("classpath:spring.xml");Person person = (Person) beanFactory.getBean("person");System.out.println(person);assertThat(person.getName()).isEqualTo("derek");assertThat(person.getCar().getBrand()).isEqualTo("porsche");Car car = (Car) beanFactory.getBean("car");System.out.println(car);assertThat(car.getBrand()).isEqualTo("porsche");}
}
这一段的内容仅为从xml中获取,思路就是从xml中读取资源,然后加载BeanDefinition,因此大致相同。
BeanFactoryPostProcessor和BeanPostProcessor
【BeanFactoryPostProcessor和BeanPostProcessor是spring框架中具有重量级地位的两个接口,理解了这两个接口的作用,基本就理解spring的核心原理了】
BeanFactoryPostProcessor是spring提供的容器扩展机制,允许我们在bean实例化之前修改bean的定义信息即BeanDefinition的信息。源码给出的代码如下。首先还是创建BeanFactory,然后从xml中获取beanDefinitionLoader,并且将资源加载器中的真正的definition进行提取。但和之前不同的是,我们需要在使用Definition进行实例化之前进行修改。
public class BeanFactoryProcessorAndBeanPostProcessorTest {@Testpublic void testBeanFactoryPostProcessor() throws Exception {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);beanDefinitionReader.loadBeanDefinitions("classpath:spring.xml");//在所有BeanDefintion加载完成后,但在bean实例化之前,修改BeanDefinition的属性值CustomBeanFactoryPostProcessor beanFactoryPostProcessor = new CustomBeanFactoryPostProcessor();beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);Person person = (Person) beanFactory.getBean("person");System.out.println(person);//name属性在CustomBeanFactoryPostProcessor中被修改为ivyassertThat(person.getName()).isEqualTo("ivy"); // 最终执行结果的name就是ivy。}
}
// 对应的CustomBeanFactoryPostProcesser,在这里主动修改人名
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("CustomBeanFactoryPostProcessor#postProcessBeanFactory");BeanDefinition personBeanDefiniton = beanFactory.getBeanDefinition("person");PropertyValues propertyValues = personBeanDefiniton.getPropertyValues();//将person的name属性改为ivypropertyValues.addPropertyValue(new PropertyValue("name", "ivy"));}
}
你可能发现这个和给bean添加属性的操作好像类似,事实确实是这样。但是添加属性是在定义Bean的时候,也就是先准备好属性再使用BeanDefinition的构造函数进行初始化(还记得吗?有两个构造函数,一个是只有class信息,另一个还包含了PropertyValues)。但是BeanFactoryPostProcesser则是在BeanDefinition加载后进行的修改。
因此,由于【这种同样类型的操作,在不同时间进行】,我们可以联想到Bean的生命周期,这个项目关于Bean这一块的思路大致出现:最终我们需要复现出的是Bean完整的声明周期,从定义到赋值,再到初始化,最后是销毁。当然这一过程实际远比这四步要复杂。
回到正题,BeanPostProcessor也是spring提供的容器扩展机制,不同于BeanFactoryPostProcessor的是,BeanPostProcessor在bean实例化后修改bean或替换bean。BeanPostProcessor是后面实现AOP的关键。BeanPostProcessor的两个方法分别在bean执行初始化方法(后面实现)之前和之后执行。
public interface BeanPostProcessor {/*** 在bean执行初始化方法之前执行此方法*/Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;/*** 在bean执行初始化方法之后执行此方法*/Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
这里的第二个测试代码如下:
@Test
public void testBeanPostProcessor() throws Exception {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);beanDefinitionReader.loadBeanDefinitions("classpath:spring.xml");//添加bean实例化后的处理器CustomerBeanPostProcessor customerBeanPostProcessor = new CustomerBeanPostProcessor();beanFactory.addBeanPostProcessor(customerBeanPostProcessor);Car car = (Car) beanFactory.getBean("car"); // 打断点!! 这里包含了初始化和实例化的内容System.out.println(car);//brand属性在CustomerBeanPostProcessor中被修改为lamborghiniassertThat(car.getBrand()).isEqualTo("lamborghini");
}
这里可以ctrl+B去看源码,我们查看一下CustomerBeanPostProcessor的代码。其就是分为了两个部分,一个是BeforeInitialization,一个是AfterInitialization。这里的前置处理是将car这个Bean的属性进行修改,后置则单纯是返回这个bean,即不会进行任何的处理。也就是这里提前写好了应该在bean的实例化后,初始化之前进行的前置操作和后置操作。这里的前置和后置是相对于bean的初始化而言。
public class CustomerBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("CustomerBeanPostProcessor#postProcessBeforeInitialization, beanName: " + beanName);//换兰博基尼if ("car".equals(beanName)) {((Car) bean).setBrand("lamborghini");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("CustomerBeanPostProcessor#postProcessAfterInitialization, beanName: " + beanName);return bean;}
}
然后我们针对getBean这个方法查看源码。这个方法包含了实例化和初始化的过程。实例化的过程在上面都已经有涉及(BeanDefinition的内容)。
// getBean中会调用createBean。
@Override
public Object getBean(String name) throws BeansException {Object sharedInstance = getSingleton(name);if (sharedInstance != null) {//如果是FactoryBean,从FactoryBean#getObject中创建beanreturn getObjectForBeanInstance(sharedInstance, name);}BeanDefinition beanDefinition = getBeanDefinition(name);Object bean = createBean(name, beanDefinition);return getObjectForBeanInstance(bean, name);
}
// 继续查看createBean:
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {//如果bean需要代理,则直接返回代理对象Object bean = resolveBeforeInstantiation(beanName, beanDefinition);if (bean != null) {return bean;}return doCreateBean(beanName, beanDefinition);
}
// 继续查看doCreateBean
protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {Object bean;try {bean = createBeanInstance(beanDefinition);//为解决循环依赖问题,将实例化后的bean放进缓存中提前暴露if (beanDefinition.isSingleton()) {Object finalBean = bean;addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {return getEarlyBeanReference(beanName, beanDefinition, finalBean);}});}//实例化bean之后执行boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);if (!continueWithPropertyPopulation) {return bean;}//在设置bean属性之前,允许BeanPostProcessor修改属性值applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);//为bean填充属性applyPropertyValues(beanName, bean, beanDefinition);//执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法bean = initializeBean(beanName, bean, beanDefinition);} catch (Exception e) {throw new BeansException("Instantiation of bean failed", e);}//注册有销毁方法的bean// 省略,此处是销毁部分
从doCreate中可以看到,这里的initialize Bean就是我们想要的初始化,这行代码之前的部分是实例化。
protected Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {if (bean instanceof BeanFactoryAware) {((BeanFactoryAware) bean).setBeanFactory(this);}//执行BeanPostProcessor的前置处理Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);try {invokeInitMethods(beanName, wrappedBean, beanDefinition);} catch (Throwable ex) {throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", ex);}//执行BeanPostProcessor的后置处理wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);return wrappedBean;
}
而initializationBean的部分就和我们之前说的是一样的,先进行一个前置处理,然后是初始化,最后是后置处理。
注意!bean的实例化和初始化是两回事。这个概念很基础但容易混淆:实例化类似于new操作,在这里就是使用beanFactory+BeanFactoryPostProcessor进行的实例化,得到了具体的bean,但是初始化则是填充值,初始化的前后包含了这里说的前置操作和后置操作。
经过上面的学习,从bean的角度看,目前的生命周期是这样的。这一套并不完整,但是可以说明除销毁外的大多数流程。
读者如果比较敏感,应该可有发现上面有一些小问题,即我们是需要手动在代码中声明BeanPostProcessor,能不能将其加入到spring中自主处理呢?应用上下文ApplicationContext,是spring中较之于BeanFactory更为先进的IOC容器,ApplicationContext除了拥有BeanFactory的所有功能外,还支持特殊类型bean如上一节中的BeanFactoryPostProcessor和BeanPostProcessor的自动识别、资源加载、容器事件和监听器、国际化支持、单例bean自动初始化等。
也许有的面试题会问IOC容器有哪些,那除了之前的BeanFactory,还有就是ApplicationContext。 比较重要的功能就是BeanFactoryProcessor和BeanPostProcessor的自动识别,即不需要手动写入而是可以通过配置或者注解的方式来加入。
测试代码如下。跟之前使用BeanFactory相比,就省去了手动添加处理器的过程。
@Test
public void testApplicationContext() throws Exception {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");Person person = applicationContext.getBean("person", Person.class);System.out.println("111" + person);//name属性在CustomBeanFactoryPostProcessor中被修改为ivyassertThat(person.getName()).isEqualTo("ivy");Car car = applicationContext.getBean("car", Car.class);System.out.println(car);//brand属性在CustomerBeanPostProcessor中被修改为lamborghiniassertThat(car.getBrand()).isEqualTo("lamborghini");
}
关于自动装配的代码,主要看的是AbstractApplicationContext#refresh方法。这个方法也很重要,是spring容器启动和刷新的核心逻辑。通过这个方法,spring会在启动的时候自动扫描和装配Bean(除去懒加载的),然后将这些Bean实例化、初始化完成并且放到IOC容器中,等待被调用。
@Override
public void refresh() throws BeansException {//创建BeanFactory,并加载BeanDefinitionrefreshBeanFactory();ConfigurableListableBeanFactory beanFactory = getBeanFactory();//添加ApplicationContextAwareProcessor,让继承自ApplicationContextAware的bean能感知beanbeanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));//在bean实例化之前,执行BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactory);//BeanPostProcessor需要提前与其他bean实例化之前注册registerBeanPostProcessors(beanFactory);//初始化事件发布者initApplicationEventMulticaster();//注册事件监听器registerListeners();//注册类型转换器和提前实例化单例beanfinishBeanFactoryInitialization(beanFactory);//发布容器刷新完成事件finishRefresh();
}
// 这里着重看一下finishBeanFactoryInitialization(beanFactory);
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {//设置类型转换器if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME)) {Object conversionService = beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME);if (conversionService instanceof ConversionService) {beanFactory.setConversionService((ConversionService) conversionService);}}//提前实例化单例beanbeanFactory.preInstantiateSingletons();
}
// preInstantiateSingletons,看注释可以得知是创建一个Bean,创建要满足的条件和我上面写得一样。
@Override
public void preInstantiateSingletons() throws BeansException {beanDefinitionMap.forEach((beanName, beanDefinition) -> {//只有当bean是单例且不为懒加载才会被创建if (beanDefinition.isSingleton() && !beanDefinition.isLazyInit()) {getBean(beanName);}});
}
Bean的初始化和销毁方法
在spring中,定义bean的初始化和销毁方法有三种方法:
- 在xml文件中制定init-method和destroy-method
- 继承自InitializingBean和DisposableBean
- 在方法上加注解PostConstruct和PreDestroy(在springboot中比较常用)
第三种通过BeanPostProcessor实现,在扩展篇中实现。
第一种:在xml中制定init-method和destroy-method
这种在springboot中并不常用,主要使用的还是注解。
首先我们看一下xml文件:
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsd"><bean id="person" class="org.springframework.test.bean.Person" init-method="customInitMethod" destroy-method="customDestroyMethod"><property name="name" value="derek"/><property name="car" ref="car"/></bean><bean id="car" class="org.springframework.test.bean.Car"><property name="brand" value="porsche"/></bean></beans>
然后再对应的person中声明即可。
第二种:继承InitializingBean和DisposableBean
- 初始化方法
这里声明了person的bean,这个bean加入了init和destroy的方法,也就是基于这两个方法进行初始化和销毁。
public class Person implements InitializingBean, DisposableBean {private String name;private int age;private Car car;public void customInitMethod() {System.out.println("I was born in the method named customInitMethod");}public void customDestroyMethod() {System.out.println("I died in the method named customDestroyMethod");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("I was born in the method named afterPropertiesSet");}@Overridepublic void destroy() throws Exception {System.out.println("I died in the method named destroy");}//setter and getter
}
// 测试代码:
public class InitAndDestoryMethodTest {@Testpublic void testInitAndDestroyMethod() throws Exception {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:init-and-destroy-method.xml");applicationContext.registerShutdownHook(); //或者手动关闭 applicationContext.close();}
}
这个person包含了初始化和销毁的方法。最终的输出结果为:
I was born in the method named afterPropertiesSet
I was born in the method named customInitMethod
I died in the method named destroy
I died in the method named customDestroyMethod
也就说执行顺序是先调用的afterPropertiesSet,这个方法重写了 invokeInitMethods方法,这个方法在initializeBean中被调用(还记得doCreate吗?这里调用的initializeBean,后者的作用是调用BeanPostProcessor的前置和后置方法,以及invokeInitMethods,也就是Bean的初始化方法)。所以说明afterPropertiesSet方法是在Bean初始化的时候被调用的。然后才是customInitMethod,也就是说明,Bean的InitMethod方法是在自定义的初始化方法之前执行的。下面的代码其实就是这个意思
protected void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Throwable {// 先执行InitMethod if (bean instanceof InitializingBean) {((InitializingBean) bean).afterPropertiesSet();}// 再执行自定义的初始化方法,以反射的方式来执行String initMethodName = beanDefinition.getInitMethodName();if (StrUtil.isNotEmpty(initMethodName) && !(bean instanceof InitializingBean && "afterPropertiesSet".equals(initMethodName))) {Method initMethod = ClassUtil.getPublicMethod(beanDefinition.getBeanClass(), initMethodName);if (initMethod == null) {throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");}initMethod.invoke(bean);}
}
- 销毁方法
在DefaultSingletonBeanRegistry中增加属性disposableBeans保存拥有销毁方法的bean,拥有销毁方法的bean在AbstractAutowireCapableBeanFactory#registerDisposableBeanIfNecessary中注册到disposableBeans中。
也就是说,还是在refresh中会调用finishBeanFactoryInitialization,后者调用了getBean,最终会调用doCreateBean,也就会注册有销毁方法的Beans,这样就可以在JVM关闭之前通过一个单独的关闭线程将Beans都进行销毁。
这里可以看之前的doCreateBean的完整内容了。之前的内容是实例化和初始化,之后才是销毁。
protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
// 前面是实例化和初始化
//注册有销毁方法的beanregisterDisposableBeanIfNecessary(beanName, bean, beanDefinition);Object exposedObject = bean;if (beanDefinition.isSingleton()) {//如果有代理对象,此处获取代理对象exposedObject = getSingleton(beanName);addSingleton(beanName, exposedObject);}return exposedObject;
}
// 检测是否满足销毁的要求:是单例,且有销毁方法
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {//只有singleton类型bean会执行销毁方法if (beanDefinition.isSingleton()) {if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));}}
}
// 其实也就是把需要销毁的Bean放到DisposableBean中,最后统一销毁。
public void registerDisposableBean(String beanName, DisposableBean bean) {disposableBeans.put(beanName, bean);
}
测试的代码如下。整个的调用流程是先在refresh中一路调用,调用到了doCreateBean,其中包含了将【有销毁方法】的bean注册到disposableBeans中,然后销毁的时候则会钩子函数进行销毁,销毁的对象为注册到disposableBeans中的Bean。
public class InitAndDestoryMethodTest {@Testpublic void testInitAndDestroyMethod() throws Exception {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:init-and-destroy-method.xml");applicationContext.registerShutdownHook(); //或者手动关闭 applicationContext.close();}
}
// 查看registerShutdownHook,创建一个shutdown线程执行doclose。
// 为了确保销毁方法在虚拟机关闭之前执行,向虚拟机中注册一个钩子方法。
// 当 JVM 接收到关闭信号时(例如用户按下 Ctrl+C 或操作系统发送终止信号),这个钩子线程会被执行。
// 这个线程会调用 doClose() 方法,进而发布关闭事件和销毁单例 Bean。
public void registerShutdownHook() {Thread shutdownHook = new Thread() {public void run() {doClose();}};Runtime.getRuntime().addShutdownHook(shutdownHook);}protected void doClose() {//发布容器关闭事件publishEvent(new ContextClosedEvent(this));//执行单例bean的销毁方法destroyBeans();
}protected void destroyBeans() {getBeanFactory().destroySingletons();
}// 销毁disposableBeans中的Beans。
public void destroySingletons() {ArrayList<String> beanNames = new ArrayList<>(disposableBeans.keySet());for (String beanName : beanNames) {DisposableBean disposableBean = disposableBeans.remove(beanName); // removetry {disposableBean.destroy();} catch (Exception e) {throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);}}
}
生命周期如下:
Aware接口
Aware是感知、意识的意思,Aware接口是标记性接口,其实现子类能感知容器相关的对象。常用的Aware接口有BeanFactoryAware和ApplicationContextAware,分别能让其实现者感知所属的BeanFactory和ApplicationContext。
有测试代码如下。这里的helloService是实现了两个接口:
public class AwareInterfaceTest {@Testpublic void test() throws Exception {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");HelloService helloService = applicationContext.getBean("helloService", HelloService.class);assertThat(helloService.getApplicationContext()).isNotNull();assertThat(helloService.getBeanFactory()).isNotNull();}
}
//HelloService类
public class HelloService implements ApplicationContextAware, BeanFactoryAware {private ApplicationContext applicationContext;private BeanFactory beanFactory;// 剩下的代码省略
}
对于BeanFactory,查看aware的部分就是从getBean一路到doCreateBean,在doCreateBean中有initializeBean的前三行就是。因此你可以得知,这部分在生命周期中是Bean的实例化之后,BeanPostProcessor之前进行的。
通过这三行代码,可以让Bean得知其BeanFactory是什么,也就可以获得对应的BeanFactory中其它Bean的内容。
protected Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {if (bean instanceof BeanFactoryAware) { // 当前的bean是否是实现了BeanFactoryAware接口((BeanFactoryAware) bean).setBeanFactory(this); // 让创建当前bean的BeanFactory的引用给这个bean}//执行BeanPostProcessor的前置处理、初始化方法和后置处理。
}
对于ApplicationContext:是在BeanPostProcessor的前置处理中进行的。对于ApplicationContext,会在Spring运行时自动执行refresh() 方法,refresh会包含各项内容,包括实例化和BeanPostProcessor。
protected Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {// Aware//执行BeanPostProcessor的前置处理Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);// 初始化和后置处理
}
// 查看前置处理的代码:
// 传入的第一个参数其实就是我们要创建的Bean
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean; //将传入的Bean复制一份for (BeanPostProcessor processor : getBeanPostProcessors()) {// current是增加了前置处理的bean。Object current = processor.postProcessBeforeInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;
}
// 查看postProcessBeforeInitialization
// 如果是ApplicationContextAware接口的实现,则会将对于ApplicationContext的引用给当前的bean。最后返回bean
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(applicationContext);}return bean;
}
这个有什么用呢?
实现 Aware 接口(如 ApplicationContextAware)可以让一个普通的 bean 获得对 ApplicationContext 的访问权限,从而能够执行一些普通 bean 通常无法直接执行的操作,比如发布事件。这个后面会提到。
因此现在生命周期如下:
bean作用域,增加prototype的支持
测试代码如下。这里注册的xml文件的bean的scope发生了变化,这次不是单例(singleton),而是原型(prototype)。也就是说,每次都会新创建一个bean,而不是依赖于IOC容器创建一个单例。使用的时候也是,不会只在缓存中使用缓存好的单例。如果你对之前的代码比较熟,就会知道refresh阶段是只会进行BeanDefinition的注册和BeanFactoryPostProcessor的处理,不会进行实例化以及实例化之后的步骤,这需要手动进行。
public class PrototypeBeanTest {@Testpublic void testPrototype() throws Exception {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:prototype-bean.xml");Car car1 = applicationContext.getBean("car", Car.class);Car car2 = applicationContext.getBean("car", Car.class);assertThat(car1 != car2).isTrue(); // 可以通过,这两个不一样}
}
// 为了防止忘记,我再写一下
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//设置类型转换器if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME)) {Object conversionService = beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME);if (conversionService instanceof ConversionService) {beanFactory.setConversionService((ConversionService) conversionService);}}//提前实例化单例beanbeanFactory.preInstantiateSingletons();
}
//查看这个preInstantiateSingleton:
@Override
public void preInstantiateSingletons() throws BeansException {beanDefinitionMap.forEach((beanName, beanDefinition) -> {//只有当bean是单例且不为懒加载才会被创建,才会调用getbeanif (beanDefinition.isSingleton() && !beanDefinition.isLazyInit()) {getBean(beanName);}});
}
而怎么判断是否为单例?会在doCreate的地方操作。先判断是否为singleton,如果是则执行addSingleton,加入到单例中。
@Override
publicvoid addSingleton(String beanName, Object singletonObject) {singletonObjects.put(beanName, singletonObject); // 1earlySingletonObjects.remove(beanName); // 2singletonFactories.remove(beanName); // 3
}
现在可以得到bean的生命周期如下:
FactoryBean
容器事件和事件监听器
一个例子
例如有如下的代码:
// Service 层
@Service
public class MyService {private String serviceName;private int serviceVersion;// 1. 构造器public MyService() {System.out.println("1. MyService: 调用无参构造器,准备实例化");}// 2. Setter 方法 (用于依赖注入)@Autowiredpublic void setServiceName(String serviceName) {System.out.println("2. MyService: 通过 setter 注入 serviceName = " + serviceName);this.serviceName = serviceName;}// 3. @PostConstruct 注解的初始化方法@PostConstructpublic void init() {System.out.println("6. MyService: 执行 @PostConstruct 注解的初始化方法 init()");this.serviceVersion = 1;}// 业务方法public String doSomething() {System.out.println("9. MyService: 执行业务方法 doSomething()");return "Service " + serviceName + " (v" + serviceVersion + ") is working!";}// 4. @PreDestroy 注解的销毁方法@PreDestroypublic void cleanup() {System.out.println("12. MyService: 执行 @PreDestroy 注解的销毁方法 cleanup()");}
}// Controller 层
@RestController
@RequestMapping("/api")
public class MyController {private MyService myService; // 依赖 MyService// 5. 构造器注入 (推荐)@Autowiredpublic MyController(MyService myService) {System.out.println("3. MyController: 调用构造器,注入 myService");this.myService = myService;}@GetMapping("/hello")public String sayHello() {System.out.println("8. MyController: 调用 sayHello 方法");// 9. 调用 Service 的方法String result = myService.doSomething();return "Controller received: " + result;}
}
我们结合上面所学来分析一个bean的生命周期:
- 程序员定义 Bean:
你通过添加 @Service, @Controller, @Repository 等注解来标记类,告诉 Spring 你希望将这些类实例化为 Bean。你还可能使用 @Autowired 或 @Resource 等注解来声明依赖关系,或者直接在构造器/Setter 方法上使用 @Autowired 来实现依赖注入。 - Spring Boot 的注册和定义:
这一部分开始和上面所学相关。
当你的 Spring Boot 应用启动时,SpringApplication.run() 方法会被调用。内部,它会创建一个 ApplicationContext,这是 Spring 的核心容器,负责管理 Bean。容器会扫描你指定的包(通过 @SpringBootApplication 或 @ComponentScan 注解定义的 base packages)。在扫描过程中,容器会识别出带有 @Service, @Controller, @Component 等注解的类。
而对于每个被识别的类,容器会:- 创建 BeanDefinition: 这是一个配置元数据对象,描述了如何创建对应的 Bean,包括类名、作用域(默认是单例 singleton)、是否是懒加载、依赖关系、初始化/销毁方法等。
- 注册 BeanDefinition : 此时,Bean 还没有被创建,只是定义好了如何创建它。
- 第一次调用 Bean(例如 Controller 调用 Service):
假设一个 HTTP 请求到达,Spring MVC 框架(也是 Spring Boot 自动配置的一部分)需要处理这个请求。它会找到匹配的 @Controller 或 @RestController 处理方法。在执行这个 Controller 方法之前,Spring 需要确保 Controller 依赖的 Service Bean 是可用的。
- bean的实例化以及初始化:
如果 Service Bean 还没有被创建(对于一个单例 Bean,通常在应用启动时就已经创建好了,除非它是懒加载的),那么当 Controller 被创建(或者第一次需要其依赖的 Service 时),容器就会:- 根据之前注册的 BeanDefinition,找到对应的 Service 类。
- 调用 BeanFactory.getBean(“serviceName”)(或者更精确地说,是 ApplicationContext.getBean(),它内部委托给 BeanFactory)。 这个 getBean() 调用会触发 Bean 的完整创建和初始化流程。这一部分上面也有源码。
- 实例化(调用构造器):填充属性(依赖注入,调用 Setter 方法或注入构造器参数)
- Aware 接口回调 (如果实现):如果 MyService 实现了 BeanNameAware, ApplicationContextAware 等接口,Spring 会在这一步调用 setBeanName(), setApplicationContext() 等方法,让 Bean 获得自身在容器中的名称或容器引用。
- 应用 BeanPostProcessor 的 postProcessBeforeInitialization
- 调用 @PostConstruct 方法 或 InitializingBean.afterPropertiesSet() 或自定义的 init-method
应用 BeanPostProcessor 的 postProcessAfterInitialization - 最后,将完全初始化好的 Service Bean 返回给 Controller。
- 特别说明:对于单例 Bean: 通常在应用启动、容器刷新(也就是refresh方法调用的时候,上面有源码)的过程中,所有单例 Bean 就已经被创建和初始化好了。所以,在 Controller 调用 Service 时,getBean() 很可能只是从容器缓存中获取一个已经存在的 Bean 实例,而不会重新创建。 这就是单例模式的效率优势。想一想mini-spring的代码,是不是在newSingleon的时候是存在多级缓存的?
对于原型(prototype)Bean: 每次调用 getBean() 都会创建一个新的 Bean 实例。