引言
在以前我们都是这样启动Spring:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
这段代码是通过ClassPathXmlApplicationContext创建一个bean容器,之后扫描spring.xml文件,在xml文件中可以自己定义bean。之后容器调用getBean()方法得到userSerivce对象,调用其中的test()方法。那么大家想过Spring怎么创建容器的?bean又是怎么样注入到容器中的,Spring对于各种类型的bean是怎么处理的? 在现在的Spring MVC和SpringBoot是这样启动的:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
可以看到AnnotationConfigApplicationContext的用法和 ClassPathXmlApplicationContext是非常类似的,只不过需要传入的是一个class,而不是一个xml文件。而AppConfig.class一样是定义了各种bean。以下给出一段xml和class的文件实例,他们发挥的作用是一样的。
@ComponentScan ("com.pxoolcm")
public class AppConfig {
@Bean
public UserService userService (){ return new UserService(); } }
<context:component-scan base-package="com.pxoolcm"/>
<bean id="userService" class="com.pxoolcm.service.UserService"/>
其实SpringMVC和SpringBoot之间都是创建了ApplicationContext的,SpringMVC创建了XmlWebApplicationContext,后者则是创建了AnnotationConfigApplicationContext
Bean的创建过程
1.通过构造方法创建对象
spring会通过构造方法来实例化得到一个对象,如果有多个构造方法,spring会进行选择,这叫做推断构造方法。
2.判断是否有@Autowired注解
得到一个对象后,Spring会判断该对象中是否存在被@Autowired注解了的属性,把这些属性找出来并由Spring进行赋值(依赖注入)
3.判断是否实现了各种接口
依赖注入后,Spring会判断该对象是否实现了BeanNameAware接口、 BeanClassLoaderAware接口、BeanFactoryAware接口,如果实现了,就表示当前 对象必须实现该接口中所定义的setBeanName()、setBeanClassLoader()、 setBeanFactory()方法,那Spring就会调用这些方法并传入相应的参数(Aware回调)
4.判断(初始化前)
接下来就是进行初始化前的工作,在这之前,先介绍什么叫做初始化 *初始化 如果在一个类中有一个成员变量,这个成员变量的属性是一个类,那么就是说明还需要准备里面这个类,那么spring就需要知道这个类。并且进行这个类的实例化,这就叫做初始化。
接下来继续讨论判断:当我们需要spring知道里面这个类的时候,就需要进行这个类的实例化,那么怎么让spring知道对这个变量进行初始化呢。答案是加上@PostConstruct注解,如果有这个注解,spring在初始化阶段会到IoC容器中找这个Bean,进行初始化,如果没有这个Bean,那么就会报错。*
5.判断(初始化)
做完初始化前的工作,就要进行初始化了。spring会判断是否实现了InitializingBean这个接口,如果有,那么就会要执行接口中我们自己实现的afterPropertiesSet()方法,进行初始化。
6.判断(初始化后)
在初始化结束后,spring需要判断是否这个Bean要进行AOP,如果不需要,那么这个Bean就是创建完了,如果需要,那么就要生成一个代理对象作为Bean。
如何判断Bean是否需要AOP 这里大家应该会疑惑,Spring是怎么判断这个Bean需要AOP的呢。大家知道,进行切面编程时,都需要应该Aspect切面类,切面类里面会定义要切入的节点和切入的方法,以及切入函数。那么既然定义了这些,spring只需要先遍历每个切面类,再到每个切面类里面去遍历切入的类,如果切入的类和当前的Bean是一样的,那么就说明这个Bean需要AOP。
Bean创建之后
创建之后Bean分为单例Bean和原型Bean
单例Bean
什么是单例Bean?
单例Bean,顾名思义,在全局中只有一个Bean。那么就很好理解了,在Spring容器中只会创建一个Bean,并且Spring生命周期中请求这个类的Bean时只有这一个Bean。换句话说,单例Bean在IoC容器中是全局唯一的。 果当前Bean是单例Bean,那么会把该Bean对象存入一个Map,Map的key为beanName,value为Bean对象。这样下次getBean时就可 以直接从Map中拿到对应的Bean对象了。(实际上,在Spring源码中,这个Map就是单例池)
原型Bean
原型Bean就和单例Bean相反,可以创建很多次。 那么当要请求原型Bean时,就会重复上述Bean的创建操作
推断构造方法
大家都知道Spring会对Bean进行实例化,那么就和我们实例化对象一样,需要构造方法来实现对对象的实例化。那么Spring是怎么推断需要哪个构造方法的呢?
只有一个构造方法
不管这个构造方法是有参还是无参,Spring都会使用该构造方法进行实例化
存在多个构造方法
在这些构造方法中存在一个无参构造,那么Spring都会使用该构造方法来实例化对象。如果没有无参构造,那么就会报错。
但是,如果在一个构造方法上指定了@Autowired注解,那么Spring就会直接使用这个构造方法
以上,就是Spring底层原理的第一节,谢谢大家!