一、首先看applicationContext.xml里的配置项bean
我们采用xml配置文件的方式对bean进行声明和管理,每一个bean标签都代表着需要被创建的对象并通过property标签可以为该类注入其他依赖对象,通过这种方式Spring容器就可以成功知道我们需要创建那些bean实例
二、ApplicationContext, Spring的容器
然后通过ClassPathXmlApplicationContext去加载spring的配置文件,接着获取想要的实例bean并调用相应方法执行。对于ClassPathXmlApplicationContext默认加载classpath路径下的文件,只需指明对应文件的classpath路径下的配置文件名字即可。如果存在多个配置文件,ClassPathXmlApplicationContext是一个可以接收可变参数的构造函数。实际上ClassPathXmlApplicationContext还有一个孪生兄弟FileSystemXmlApplicationContext,它默认为项目工作路径 即项目的根目录 ,至于使用哪个,个人觉得没多大的差别。
这里, 不管用按哪个方法去获取spring的配置文件, 返回的都是一个ApplicationContext, Spring的IOC的容器, 但实际上ApplicationContext是一个接口:
这里的ConfigurableApplicationContext子接口,给我们提供了一些方法close(), refresh(), 可以让ApplicationContext刷新和关闭的方法, 后面要用到,这里先认识一下。
ApplicationContext在初始化的时候, 就实例化所有单列的Bean。
具体的从ApplicationContext容器中获取对象实例的方法getBean:
注意: 平时,我都是用id值来获取的, 虽然从xxx.class也可以获取, 但是用这个有个限制: ApplicationContext只有一个这个类型的对象实例, 才能用, 否则会报错。
三、依赖注入
Spring通过标签实现依赖注入, Spring支持的注入方式有三种:
①属性注入
②构造器注入
③工厂方法注入(很少使用, 也不推荐使用,这个就不讲了)
1、属性注入, 也叫Setter注入
Setter注入顾名思义,被注入的属性需要有set方法, Setter注入支持简单类型和引用类型,Setter注入是在bean实例创建完成后执行的。直接观察前面的案例,对象注入使用<property>的ref属性,对象注入同时也可以注入简单值和map、set、list、数组,简单值注入使用<property>的value属性。
2、构造函数注入
构造注入也就是通过构造方法注入依赖,构造函数的参数一般情况下就是依赖项,spring容器会根据bean中指定的构造函数参数来决定调用那个构造函数,同样看一个案例:
当然跟setter注入一样,构造注入也可传入简单值类型和集合类型,这个比较简单,不啰嗦。需要注意的是,当一个bean定义中有多个<constructor-arg>标签时,它们的放置顺序并不重要,因为Spring容器会通过传入的依赖参数与类中的构造函数的参数进行比较,尝试找到合适的构造函数。在某些情况下,如某个类,带有两个构造函数,参数类型和个数都是一样的,只是顺序不同,这在class的定义中是允许的,但对于Spring容器来说默认会只会去调用前面的。
如果我们要指定使用哪个构造方法也是可以的,在<constructor-arg>标签中存在一个index的属性,通过index属性可以告诉spring容器传递的依赖参数的顺序,下面的配置将会令Spring容器成功找到第二个构造函数并调用创建实例。
在日常的开发中,setter注入和构造注入经常会混合使用, 构造注入中index和type也可以混合使用,这并不用感觉到诧异,后面我们还会分析到注解装配,它在开发中将更为常用。
补充知识点:循环依赖
除了上述的现象,在构造函数注入还有一个无法解决的循环依赖的问题,如下有两个bean,A和B,这两个bean通过构造函数互为依赖,这种情况下Spring容器将无法实例化这两个bean。
这是由于A被创建时,希望B被注入到自身,然而,此时B还有没有被创建,而且B也依赖于A,这样将导致Spring容器左右为难,无法满足两方需求,最后脑袋奔溃,抛出异常。解决这种困境的方式是使用Setter依赖,但还是会造成一些不必要的困扰,因此,强烈不建议在配置文件中使用循环依赖。