Spring IOC原理
参考示例源代码:
https://github.com/huangzhenshi/SpringFreamworkDemo/tree/master
https://zhuanlan.zhihu.com/p/29344811
基本原理:
基本原理其实就是通过反射解析类及其类的各种信息,包括构造器、方法及其参数,属性。
然后将其封装成bean定义信息类、constructor信息类、method信息类、property信息类,最终放在一个map里,也就是所谓的container,池等等,其实就是个map
当你写好配置文件,启动项目后,框架会先按照你的配置文件找到那个要scan的包,然后解析包里面的所有类,找到所有含有@bean,@service等注解的类,利用反射解析它们,包括解析构造器,方法,属性等等,然后封装成各种信息类放到一个map里。每当你需要一个bean的时候,框架就会从container找是不是有这个类的定义啊?如果找到则通过构造器new出来(这就是控制反转,不用你new,框架帮你new),再在这个类找是不是有要注入的属性或者方法,比如标有@autowired的属性,如果有则还是到container找对应的解析类,new出对象,并通过之前解析出来的信息类找到setter方法,然后用该方法注入对象(这就是依赖注入)。如果其中有一个类container里没找到,则抛出异常,比如常见的spring无法找到该类定义,无法wire的异常。还有就是嵌套bean则用了一下递归,container会放到servletcontext里面,每次reQuest从servletcontext找这个container即可,不用多次解析类定义。如果bean的scope是singleton,则会重用这个bean不再重新创建,将这个bean放到一个map里,每次用都先从这个map里面找。如果scope是session,则该bean会放到session里面。
Spring 中核心类和接口
BeanFactory interface
顶层接口,也是实现工厂方式的抽象工厂,默认懒加载
- getBean()
- containsBean()
- isSingleton\isPrototype()Car car = (Car) bf.getBean("car");Car car2 = bf.getBean("car",Car.class);
BeanDefinition
web容器会把所有的bean都注册到BeanDefinitionRegistry当中
- bean本身的相关信息 getBeanClassName() isLazyInit() getPropertyValues() isSingleton()
- 父类相关信息 getParentName()
- 依赖类信息 getDependsOn()
- 加载器相关信息 getFactoryBeanName()
XmlBeanFactory
常用的一个工厂实现类,读取spring.xml里面配置的bean
DefaultSingletonBeanRegistry
ConcurrentHashMap 存储各种注册的类的缓存池 类名/类基本信息Object K/V
HierarchicalBeanFactory
建立父子级别容器的扩展类,MVC就是依赖这个工厂实现的
ApplicationContext
继承自 HierarchicalBeanFactory,除了继承了 父子容器 和创建bean还扩展了很多功能
- ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。实现了 ApplicationListener 事件监听接口的 Bean 可以接收到容器事件 , 并对事件进行响应处理
- LifeCycle:该接口实现生命周期管理
- ResourcePatternResolver: 正则表达式 读取配置文件信息 application*.xmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:application-datasource.xml");int count=ctx.getBeanDefinitionCount();//这里获取的是注册表中的数量不是实例化类的数量ctx.getBean("car");ctx.getBean("dataSource");public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
WebApplicationContext
实现web容器和spring的整合,web启动时ServletContext有两种方式获取到WebApplicationContext
web容器启动原理:
- tomcat启动触发web.xml,web容器提供SevletContext
- 会根据ContextLoaderListener监听,或者ContextLoaderSevlet初始化WebApplicationContext
- 读取指定路径下的bean配置文件,加载初始化所有的配置的或者注解的Bean存储到DefaultSingletonBeanRegistry缓存起来。
- 再扫描web.xml的serlvet元素,比如MVC的DispatcherServlet,也会重复类似的过程,但是是在Spring的子容器IOC容器当中,父容器配置的元素对子容器可见。
具体过程
web.xml 的ContextLoaderListener最先执行,监听tomcat的容器启动,启动之后调用初始化方法,初始化一个 WebApplicationContext
public void contextInitialized(ServletContextEvent event) {this.contextLoader = createContextLoader();if (this.contextLoader == null) {this.contextLoader = this;}this.contextLoader.initWebApplicationContext(event.getServletContext());}再通过配置的参数取读取配置文件
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param><param-name>contextConfigLocation</param-name><param-value>classpath*:application-datasource.xml</param-value></context-param>再读取web.xml里面的servelt元素,初始化类似MVC的子容器,子容器初始化的过程是先拷贝一份父容器,也就是spring容器当中的类的信息,再读取MVC的配置文件,读取只属于MVC的子类,所以MVC里面的类对Spring容器不可见
<servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath*:application-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping>
通过BeanFactory生产类的流程
通过Resource指定配置文件的位置,并且启动IOC容器,但是不会实例化配置或者扫描的bean,只有在第一次调用getBean的时候,会初始化这个单例的对象并且存储在DefaultSingletonBeanRegistry的singletonObjects = new ConcurrentHashMap
Tomcat中Spring的bean的创建过程
web容器的操作:
- web容器启动的时候会读取到配置文件的地址context-param
- ResourceLoader读取配置文件–>生成Resource对象
- BeanDefinitionReader读取Resource,生成各种BeanDefinition,并存储在BeanDefinitionRegistry中
Spring执行操作:
- Spring读取BeanDefinitionRegistry中的BeanDefinition,先对bean进行加工处理,最后如果是单例的话,存在DeaSinBeanRegi里面的hashmap中
Spring的Bean生命周期
生命周期概论:
- 简单的bean的生命周期就是 调用构造函数创建实例 – >设置属性(如果在xml里面有设置的话) –> 执行init方法(如果有配置) –> destroy(如果容器关闭时)
- 再此基础之上,可以让bean实现一些接口,来干预bean的实例化,比如设置beanName,BeanFactory,afterPropertiesSet、destroy等方法
- 最后还可以在容器级别上实现 构造函数、设置属性值、初始化 这3个过程当中的全局性所有bean的干预方法
Bean 本身的方法
|
结果
配置和启动
Bean级生命周期接口方法
BeanNameAware,bean设置完属性值后执行
@Overridepublic void setBeanName(String name) {System.out.println("setBeanName");}BeanFactoryAware,setBeanName执行完后执行
@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("setBeanFactory");}InitializingBean,初始化方法调用之前执行
@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("afterPropertiesSet");}DisposableBean,容器要销毁之前执行,但是优先于 bean的destroy-method之前执行
@Overridepublic void destroy() throws Exception {System.out.println("destory");}
普通bean的执行顺序结果
容器级生命周期接口方法
容器级别的处理器,在创建所有的bean的时候都会调用
BeanPostProcessor,在每一个bean调用init方法执行前和执行后执行
执行结果顺序
执行构造函数设置属性SpringsetBeanNamesetBeanFactorypostProcessBeforeInitializationafterPropertiesSetinitpostProcessAfterInitializationdestorydestorying接口代码
public class Processor implements BeanPostProcessor{@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {System.out.println("postProcessBeforeInitialization");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {System.out.println("postProcessAfterInitialization");return bean;}}
超级InstantiationAwareBeanPostProcessor接口,分别在实例化、设置属性值、初始化3个过程当中都干预bean的生命周期,甚至可以阻住bean的属性值设置是否执行
执行结果
# 实例化干预实例化bean之前调用,即调用bean类构造函数之前调用 com.test.domain.SpringTest执行构造函数postProcessAfterInstantiation 返回boolean,bean实例化后调用,并且返回false则不会注入属性# 属性值设置干预postProcessPropertyValues,在属性注入之前调用...... beanName = test 属性名集合 : [bean property 'name']设置属性SpringsetBeanNamesetBeanFactory# 初始化设置干预postProcessBeforeInitializationafterPropertiesSetinitpostProcessAfterInitializationdestorydestorying接口代码
public class InstanceBeanPostProcessor implements InstantiationAwareBeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {System.out.println("postProcessBeforeInitialization");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {System.out.println("postProcessAfterInitialization");return bean;}@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass,String beanName) throws BeansException {System.out.println("实例化bean之前调用,即调用bean类构造函数之前调用 " + beanClass.getName());return null;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName)throws BeansException {System.out.println("返回boolean,bean实例化后调用,并且返回false则不会注入属性");return true;}@Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs,PropertyDescriptor[] pds, Object bean, String beanName)throws BeansException {System.out.println("postProcessPropertyValues,在属性注入之前调用...... beanName = " + beanName + " 属性名集合 : "+ Arrays.toString(pvs.getPropertyValues()));return pvs;}}