1.spring ioc的三种依赖注入的方式
- 构造方法注入 -被注入对象通过在其构造方法中申明依赖对象的参数列表,对象在构造完成之后马上进入就绪状态
- setter方法注入 -当前对象只要为其依赖对象所对应的属性添加setter方法,对象无法在构造完成之后马上进入就绪状态
- 接口注入
2.spring提供的容器类型
1.BeanFactory-基础类型IOC容器,提供完整的ioc服务支持,默认采用延迟初始化策略
- BeanFactory只是一个接口,只定义了如何访问容器内管理的bean的方法,各个BeanFactory的具体实现类负责具体bean的注册以及管理工作
- beanDefinitionRegistry接口定义抽象了bean的注册逻辑。通常情况下,具体的beanFactory实现类会实现这个接口来管理bean的注册
- BeanFactory、BeanDefinitionRegistry以及DefaultListableBeanFactory之间的关系
2.ApplicationContext-ApplicationContext在Bean Factory的基础上构建,除了拥有BeanFactory的所有支持,还提供了其他高级特性,比如事件发布、国际化信息支持等,ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。
3.BeanFactory的对象注册与依赖绑定方式
- 直接编码方式
- 外部配置文件方式
- 注解方式
4. beanFactory的XML之旅
-
构造方法注入的xml之道
<constructor-arg type=""/> 当有多个构造函数时,如果构造函数的参数类型不同,可以使用type进行区分 <constructor-arg index=""/> #某个类的构造方法需要同时传入多个类型相同的参数时,就需要根据参数位置进行区分 -
setter方法注入的XML之道
<property> </property>
5. 工厂方法与FactoryBean
提供一个工厂类来实例化具体的接口实现类,这样,主体对象只需要依赖工厂类,具体使用的实现类有变更的话,只变更工厂类,而主体对象不需要做任何变动
public class Test {
private A a;
public Test() {
a = AFactory.getInstance;
//或者
a = new AFactory().getInstance();
}
}
常见的工厂模式
- 静态工厂方法
public class StaticAFactory {
public static A getInstance(){
return new AImpl();
}
}
若静态方法有参数,可使用传入参数,这里传入的是工厂方法得参数,而不是静态工厂方法实现类的构造方法的参数,况且,静态工厂方法实现类也没有提供显示的构造方法。
-
非静态工厂方法 同上静态工厂方法,只是需要new出实例来调用方法,在xml中配置bean时使用factory-bean和factory-method配合,如果成员方法有参数,需要使用
-
FacctoryBean FactoryBean是spring容器提供的一种可以扩展容器对象实例化逻辑的接口,请不要将其与容器名称BeanFactory相混淆。FactoryBean,其主语是Bean,定语是Factory,也就是说,它本身与其他注册到容器的对象一样,只是一个Bean而已,只不过,这种类型的Bean本身就是生产对象的工厂。 使用场景:当某些对象的实例化过程过于繁琐,通过XML配置过于复杂,我们宁愿使用Java代码来完成这个实例化过程。我们就可以使用org.springframework.beans.factory.FactoryBean接口,给出自己的对象实例化逻辑代码。
6.spring容器启动的各个阶段
大致可分为两个阶段:容器启动阶段和Bean实例化阶段
1.容器启动阶段
容器启动伊始,首先会通过某种途径加载Configuration MetaData。除了代码方式比较直接,在大部分情况下,容器需要依赖某些工具类(BeanDefinitionReader)对加载的Configuration MetaData进行解析和分析,并将分析后的信息编组为相应的BeanDefintion,最后把这些保存了Bean定义必要信息的Bean Definition,注册到相应的BeanDefinitionRegistry,这样容器启动工作就完成了。
2.Bean实例化阶段
经过第一阶段,现在所有的Bean定义信息都通过BeanDefintion的方式注册到了BeanDefinitionRegistry中。当某些请求方通过容器的getBean方法明确地请求某个对象,或者因依赖关系容器需要隐式地调用getBean方法时,就会触发第二阶段的活动。 该阶段,容器会首先检查所请求的对象是否已经初始化。如果没有,则会根据注册的BeanDefinition所提供的信息实例化被请求对象,并为其注入依赖。如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当该对象装配完毕之后,容器会立即将其返回请求方使用。如果说第一阶段只是根据图纸装配生产线的话,那么第二阶段就是使用装配好的生产线来生产具体的产品了。
7.如何插手容器的启动
spring提供了一种叫做BeanFactoryPostProcessor的容器扩展机制。该机制允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改。这就相当于在容器实现的第一阶段最后加入一道工序,让我们对最终的BeanDefinition做一些额外的操作,比如修改其中Bean定义的某些属性,为Bean定义增加其他信息等。
如果要自定义实现BeanDefinitionPostProcessor,通常我们需要实现BeanFactoryPostProcessor接口。
8. spring为我们提供了几个BeanFactoryPostProcessor的实现
1.PropertyPlaceholderConfigurer
将一些数据库连接信息、邮件服务器等相关信息单独配置到一个properties文件中,这样如果因系统资源变动的话,只需要关注这些简单properties配置文件即可。PropertyPlaceholderConfigurer允许我们在XML配置文件中使用占位符,并将这些占位符所代表的资源单独配置到简单的properties文件中加载。以数据源的配置为例,使用了PropertyPlaceholderConfigurer之后,可以在XML配置文件中按照下面的方式配置数据源,而不用将连接地址、用户名和密码等都配置到XML中.
ApplicationContext.xml
<beans>
<bean class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name = "locations">
<list>
<value>conf/jdbc.properties</value>
<value>conf/mail.properties</value>
</list>
</property>
</bean>
<bean id = "dataSource" class = "org.apache.commons.dbcp.BasicDataSource" destory-method = "close">
<property name = "url">
<value>
${jdbc.url}
</value>
</property>
</bean>
</beans>
jdbc.properties
jdbc.url = jdbc:mysql://*********
jdbc.driver = com.mysql.jdbc.Driver
基本机制就是之前所说的那样,当beanfactory在第一阶段加载完成所有配置信息时,beanfactory中保存的对象的属性信息还只是以占位符的形式存在,如${jdbc.url}。当PropertyPlaceholderConfigurer作为BeanFactoryPostProcessor被应用时,它会使用properties配置文件中的配置信息来替换相应BeanDefinition中占位符所表示的属性值。这样,当进入容器实现的第二阶段实例化bean时,bean定义中的属性值就是最终替换完成的了。
- PropertyOverrideConfigurer
可以通过PropertyOverrideConfigurer对容器中配置的任何你想处理的bean定义的property信息进行覆盖替换。
使用规则:需要提供一个PropertyOverrideConfigurer使用的配置文件: beanName.propertyName=value。也就是说,properties文件中的键是以XML中配置的Bean定义的beanName为标志开始的(通常就是id指定的值),后面跟着相应被覆盖的property的名称。
- CustomEditorConfigurer
上面两种都是通过对BeanDefinition中的数据进行变更以达到某种目的。与它们有所不同,CustomEditorConfigurer是另一种类型的BeanFactoryPostProcessor实现,它只是辅助性地将后期会用到的信息注册到容器,对BeanDefiniton没有做任何变动。
我们知道,不管对象是什么类型,也不管这些对象所声明的依赖对象是什么类型,通常都是通过 XML(或者properties甚至其他媒介)文件格式来配置这些对象类型,但XML所记载的。都是String类型,即容器从XML格式的文件中读取的都是字符串形式, 最终应用程序却是由各种类型的对象所构成。要想完成这种由字符串到具体对象的转换(不管这个转换工作最终由谁来做),都需要这种转换规则相关的信息,而CustomEditorConfigurer就是帮助我们传达类似信息的.
Spring内部通过JavaBean的PropertyEditor来帮助送行String类型到其他类型的转换工作。只要为每种对象类型提供一个PropertyEditor,就可以根据该对象类型取得与其相对应的PropertyEditor来做具体的类型转换。Spring容器内部在做具体的类型转换的时候,会采用JavaBean 框架内默认的PropertyEditor搜寻逻辑,从而继承了对原生类型以泣如va.lang.String.java.awt.Color和java.awt.Font等类型的转换支持。同时,Spring框架还提供了自身实现的一些PropertyEditor,这些PropertyEditor大部分都位于org. springframework.beans.propertyeditors包下。
我们还可以自定义PropertyEditor。
9. 了解bean的一生
在已经可以借助于BeanFactoryPostProcessor来干预Magic实现的第一个阶段(容器启动阶段)的活动之后,我们就可以开始探索下一个阶段,即bean实例化阶段的实现逻辑了。
容器启动之后,并不会马上就实例化相应的bean定义。我们知道,容器现在仅仅拥有所有对象的BeanDefinition来保存实例化阶段将要用的必要信息。只有当请求方通过BeanFactory的getBean()方法来请求某个对象实例的时候,才有可能触发Bean实例化阶段的活动。BeanFactory的getBean方法可以被客户端对象显式调用,也可以在容器内部隐式地被调用。隐式调用有如下两种情况.
-
对于BeanFactory来说,对象实例化默认采用延迟初始化.
通常情况下,当对象A被请求而需要第一次实例化的时候,如果它所依赖的对象B之前同样没有被实例化,那么容器会先实例化对象A所依赖的对象。这时容器内部就会首先实例化对象B,以及对象A依赖的其他还没有被实例化的对象。这种情况是容器内部调用getBean(),对于本次请求的请求方是隐式的。
-
ApplicationContext启动之后会实例化所有的bean定义
但Applicationcontext在实现的过程中依然遵循Spring容器实现流程的两个阶段,只不过它会在启动阶段的活动完成之后,紧接着调用注册到该容器的所有bean定义的实例化方法getBean()。这就是为什么当你得到Applicationcontext类型的容器引用时,容量内所有对象已经被全部实例化完成。不信你查一下类org.springframework.context.support.AbstractApplicationContext的refresh()方法。
之所以说getBean()方法是有可能触发Bean实例化阶段的活动,是因为只有当对应某个bean定义的getBean()方法第一次被调用时,不管是显式的还是隐式的,Bean实例化阶段的活动才会被触发, 第二次被调用则会直接返回容器缓存的第一次实例化完的对象实例(prototype类型bean除外)。当getBean()方法内部发现该bean定义之前还没有被实例化之后,会通过createBean()方法来进行具体的对象实例化。实例化过程如下图。
- Bean的实例化与BeanWrapper
容器在内部实现的时候,采用策略模式来决定采用何种方式初始化bean实例, 通常,可以通过反射或者CGLIB动态字节码生成初始化相应的bean实例或者动态生成其子类。
容器只要根据相应bean定义的BeanDefinition取得实例化信息,结合CglibSubClassingInstantiationStrategy以及不同的bean定义类型,就可以返回实例化完成的对象实例。但是,返回方式上有些点缀。不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。至此,第一步结束。
BeanWrapper其作用是对某个bean进行包裹,然后对这个包裹的bean进行操作,比如设置或者获取bean的相应属性值。而在第一步结束后返回BeanWrapper实例而不是原先的对象实例,就是为了第二步“设置对象属性”。
BeanWrapper定义继承了org.springframework.beans.PropertyAccessor接口,可以以统一的方式对对象属性进行访问;BeanWrapper定义同时又直接或者间接继承了PropertyEditorRegistry和TypeConverter接口。不知你是否还记得CustomEditorConfigurer?当把各种PropertyEditor注册给容器时,知道后面谁用到这些PropertyEditor吗?对,就是BeanWrapper!在第一步构造完成对象之后,Spring会根据对象实例构造-个BeanWrapperimpl实例,然后将之前CustomEditorConfigurer注册的PropertyEditor复制一份给BeanWrapperimpl实例〈这就是Beanwrapper同时又是PropertyEditorRegistry的原因)。这样,当Beanwrapper转换类型、设置对象属性值时,就不会无从下手了。 使用Beanwrapper对bean实例操作很方便,可以免去直接使用Java反射API (Java Reflection API操作对象实例的烦琐。
- 各色的Aware接口
当对象实例化完成并且相关属性以及依赖设置完成之后,Spring容器会检查当前对象实例是否实现了一系列的以Aware命名结尾的接口定义。如果是,则将这些Aware接口定义中规定的依赖注入给当前对象实例。 这些Aware接口为如下几个。
- org.springframework.beans.factory.BeanNameAware.如果Spring容器检测到当前对象实例实现了该接口,会将该对象实例的bean定义对应的beanName设置到当前对象实例。
- org.springframework.beans.factory.BeanClassLoaderAware. 如果容器检测到当前对象实例实现了该接口,会将对应加载当前bean的Classloader注入当前对象实例。默认会使用加载ρrg.springframework.util.ClassUtils类的Classloader。
- org.springframework.beans.factory.BeanFactoryAware。在介绍方法注入的时候,我们提到过使用该接口以便每次获取prototype类型bean的不同实例。如果对象声明实现了BeanFactoryAware接口,BeanFactory容器会将自身设置到当前对象实例。这样,当前对象实例就拥有了一个BeanFactory容器的引用,并且可以对这个容器内允许访问的对象按照需要进行访问。 以上几个Aware接口只是针对BeanFactory类型的容器而言,对于ApplicationContext类型的容器,也存在几个Aware相关接口。不过在检测这些接口并设置相关依赖的实现机理上,与以上几个接口处理方式有所不同,使用的是下面将要说到的BeanPostProcessor方式。不过,设置Aware接口这一步与BeanPostProcessor是相邻的,把这几个接口放到这里一起提及,也没什么不可以的。
对于ApplicationContext类型容器,容器在这一步还会检查以下几个Aware接口并根据接口定义设置相关依赖。
- org.springframework.context.ResourceLoaderAware
- org.springframework.context.ApplicationEventPublisherAware
- org.springframework.context.MessageSourceAware
- org.springframework.context.ApplicationContextAware
-
BeanPostProcessor
-
InitializingBean和init-method
10. ApplicationContext
作为Spring提供的较之BeanFactory更为先进的IoC容器实现,ApplicationContext 除了拥有BeanFactory支持的所有功能之外,还进一步扩展了基本容器的功能,包括BeanFactoryPostProcessor、BeanPostProcessor 以及其他特殊类型bean 的自动识别、容器启动后bean实例的自动初始化、国际化的信息支持、容器量内事件发布等。
Spring为基本的BeanFactory类型容器提供了XMLBeanFactory实现。相应地,它也为ApplicationContext类型容器提供了以下几个常用的实现。
-
org.springframework.context.support.FileSystemXmlApplicationContext 。在默认情况下,从文件系统加载bean定义以及相关资源的ApplicationContext实现.
-
org.springframework.context.support.ClassPathXrnlApplicationContext 。 在默认情况下,从Classpath加载bean定义以及相关资源的ApplicationContext 实现。
-
org.springframework.web.context.support.XmlWebApplicationContext。 Spring提供的用于Web应用程序的ApplicationContext实现.