spring初始化流程

参考

  1. 读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。大致分成定位、载入、解析、注册四个过程。

具体来说:

  1. Resource定位:spring中有个Resource接口,就是对所有资源(xml—> xmlResource、txt、property)等文件资源的抽象。
    1. 策略模式:策略接口定义了同一策略,不同子类有不同的具体策略实现。
  2. Resource载入:spring有一个ResourceLoader接口,有很多对应不同种类资源的ResourceLoader实现类,与resource同样使用了策略模式
  3. Resource解析:spring中有BeanDefinition组件,与bean定义的相关属性一一对应,只要将Resource都转化成BeanDefinition的形式就可以将resource的配置信息转换为容器的内部表示了,spring中有BeanDefinitionReader用来转换,将resource转换成BeanDefinition,例如可以通过XmlBeanDefinitionReader将xml文件解析成BeanDefinition
  4. BeanDefinition的注册:有了BeanDefinition后,你还必须将它们注册到工厂中去,所以当你使用getBean()方法时工厂才知道返回什么给你。spring使用map这个数据结构将BeanDefinition保存了起来。
  5. spring的GenericApplicationContext内部持有保存BeanDefinition的map的引用,能够调取初始化时获取的BeanDefinition从而生成bean,除此之外,其中还包含了整个程序运行的环境参数等信息(比如 JDK 版本,jre 等)。

spring初始化时只会创建所有非懒惰加载得bean(默认情况下lazyInit为false,即都是非懒惰得),不会创建定义了懒惰加载得bean

Spring IoC是如何进行依赖注入的

参考链接
依赖注入和控制反转含义相同,它们是从两个角度描述的同一个概念。当某个Java实例需要另一个Java实例时,传统的方法是由调用者创建被调用者的实例(例如,使用new关键字获得被调用者实例),而使用Spring框架后,被调用者的实例不再由调用者创建,而是由Spring容器创建,这称为控制反转。Spring容器在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,这样,调用者通过Spring容器获得被调用者实例,这称为依赖注入。

spring底层有一个beansFactory,它可以根据spring初始化时通过定位解析得到的BeanDefinition来创建对象。
如果对象的scope属性是singleton,那对象创建之后会缓存在singletonObjects这样一个map中,下次再请求此对象的时候,直接从map中取出返回,不需要重新创建。如果对象的scope属性是prototype,那每次请求对象,BeansFactory都会创建一个新的对象返回。
模拟spring依赖注入过程的最小原型。
BeansFactory创建对象用到的主要技术点就是反射,beansFactory在创建对象的时候就是获取到相应的beanDefinition,就能获取全类名,调用class.forname()就可以获取class对象,beanDefinition也有这个类的成员变量信息,在beanDefinition中用一个ConstructorArgs列表保存,在创建bean时,迭代创建他的ConstructorArgs列表里面的bean,创建一个成员变量bean列表,最后用class.getConstructor()方法完成全参构造器创建bean,从而完成依赖注入。
如果是scope设置为single的bean,则是在初次创建后放在map中,如果是多例的,则是每次用到时候都创建一次。
如果设置为懒惰初始化,则是第一次用到之后再动态创建,如果是默认非懒惰初始化的,就是容器启动时创建。

spring控制反转怎么解耦合

参考链接
实际开发应该做到:编译器不依赖,运行时才依赖,这样设计就比较灵活
所以用class.newInstance()更好,因为在运行时才会生成对象,并且可以通过修改全类名生成不同的对象。

应该避免用new,因为那样在编译期就造成了很高的耦合度,因为编译看左运行看右,用new的方式就相当于限定死了。Animal a = new cat(),运行时只能是cat
spring的ioc容器就是通过一个容器,就像一个在中间运行的齿轮一样,把原本紧紧耦合在一起的几个齿轮分开,原本强耦合的齿轮被分开了,a齿轮可以从容地将原本与他耦合的c齿轮换掉。因为没有用new的方式,我只要改配置文件里面的全类名就可以了。

spring bean 生命周期管理

先说spring初始化流程,如果还追问,再说这个

  1. Spring启动,查找并加载需要被Spring管理的Bean,进行Bean的实例化;
  2. Bean实例化后,对Bean的引用和值注入到Bean的属性中;
  3. 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法;
  4. 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
  5. 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将Bean所在应用上下文引用传入进来;
  6. 如果Bean实现了BeanPostProcessor接口,Spring就将调用它们的
    postProcessBeforeInitialization()方法; //表示在bean初始化前做一些操作
  7. 如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet()方法。类似地,如果Bean使用init-method声明了初始化方法,该方法也会被调用;
  8. 如果Bean实现了BeanPostProcess接口,Spring就将调用它们的postProcessAfterInitialization() //表示在bean初始化后做一些操作
    方法;
  9. 此时,Bean已经准备就绪,可以被应用程序使用了。它们将一直驻留在应用上下文中,直到应用上下文被销毁;
  10. bean被销毁时,如果Bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法,同样,如果Bean使用了destroy-method声明销毁方法,该方法也会被调用。

工厂模式

参考链接
使用工厂 getProduct()方法来获取对应的工厂产品,在创建对象的时候不会暴露创建逻辑,你只需要找到相应的工厂getProduct就行了,属于面向接口编程了。