class Programer {
Computer computer = new Mac2015();
private void work() {
computer.help();
}
}
复制代码
此时有一个问题就是computer和programer耦合在一块儿,这个programer不具有扩展性(它只会用mac2015),若是此时公司换了一批电脑Mac2016,那么须要从新建立一个新的程序员类,这显然是不合理的。从设计的角度来说,类自己就是定义的一个模板亦或是一种抽象,若是抽象和具体的实现绑定或者耦合在一块儿,那么就不是纯粹的抽象了。
这也违背了设计模式中的don't call me法则。因此这个时候要把computer和programer解耦,解耦的方法很简单。 computer的具体实现由调用方指定就ok,而不是在类内部自行指定。那么类就须要暴露一些接口供外界实现注入功能。
常见的方法有三种java
这就是IOC的基本思路,而对于咱们web开发来说通常不存在调用方(其实也有不过在容器层面),因此如何保证类中属性的动态注入,这个就要由Spring登场了
接下来,选择何种注入方式以及须要注入哪些属性是怎么通知到Spring的呢? 常见的方法有两种
程序员
这里多提一句,bean的实例化方式能够是普通的构造器构造也能够是工厂方法构造 下面这个是经过静态工厂注入的方式web
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
复制代码
class指向的是包含工厂方法的类,并不是工厂方法返回的类
下面这个是经过普通工厂注入的方式spring
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
复制代码
BeanFactorysql
默认lazy-load,因此启动较快 经常使用的一个实现类是DefaultListableBeanFactory,该类还实现了BeanDefinitionRegistry接口,该接口担当Bean注册管理的角色 registerBeanDefinition#BeanDefinitionRegistry bean会以beanDefinition的形式注册在BeanDefinitionRegistry中 BeanFactory接口中只定义了一些关于bean的查询方法,而真正的工做须要其子类实现的其余接口定义~apache
ApplicationContext设计模式
构建于BeanFactory基础之上,非lazy,启动时间较长,除了BeanFactory的基础功能还提供了一些额外的功能(事件发布、国际化信息支持等)缓存
基于配置文件的注入 咱们称他为手动app
<bean> <name="propertyName" ref="otherBean">ide
基于注解的注入 咱们称他为全自动
@Conponet/@Autowire/@Resource + <componet-scan>
还有注解+配置的半自动
<bean> + @autowire/@resource
通常最顶层是beans标签 beans标签有几个独特的属性,对包含的全部bean生效
default-lazy-init
全部bean是否懒加载
default-autowire
全部bean内部注入的方式no(默认)/byName/byType/constrctor/autodetect
这里byName/byType ≈ @resource/@autowire
default-dependency-check
是否进行依赖检查 none(默认)
default-init-method
全部bean的初始化方法好比init
default-destroy-method
全部bean的销毁方法好比destroy
基本上beans的全部属性均可以对应到bean中,这里提一下bean的scope属性 关于Singleton和Prototype singleton类型的bean的存活时间和容器同样长 prototype类型的bean的生命周期不禁容器维护,容器再也不拥有当前对象的引用,任由其自生自灭
想起当初解决的一个问题,持有对象的引用(new/reflect),可是这个对象未进行注入,如何利用Spring对其进行注入?
applicationContext.getAutowireCapableBeanFactory().
autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_NO, true);
复制代码
先放结论,后面有具体的代码论证(图文结合效果更佳)
public class Car implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean {
private String carName;
private BeanFactory beanFactory;
private String beanName;
public Car() {
System.out.println("bean的构造函数");
}
public void setCarName(String carName) {
System.out.println("属性注入");
this.carName = carName;
}
// 这是BeanFactoryAware接口方法
public void setBeanFactory(BeanFactory arg0) throws BeansException {
System.out
.println("BeanFactoryAware.setBeanFactory()");
this.beanFactory = arg0;
}
// 这是BeanNameAware接口方法
public void setBeanName(String arg0) {
System.out.println("BeanNameAware.setBeanName()");
this.beanName = arg0;
}
// 这是InitializingBean接口方法
public void afterPropertiesSet() throws Exception {
System.out
.println("InitializingBean.afterPropertiesSet()");
}
// 这是DiposibleBean接口方法
public void destroy() throws Exception {
System.out.println("DiposibleBean.destory()");
}
// 经过<bean>的init-method属性指定的初始化方法
public void myInit() {
System.out.println("<bean>的init-method属性指定的初始化方法");
}
// 经过<bean>的destroy-method属性指定的初始化方法
public void myDestory() {
System.out.println("<bean>的destroy-method属性指定的初始化方法");
}
}
复制代码
public class MyBeanPostProcessor implements BeanPostProcessor {
public MyBeanPostProcessor() {
super();
System.out.println("BeanPostProcessor构造函数");
}
public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException {
if (arg1.equals("car")) {
System.out
.println("BeanPostProcessor.postProcessAfterInitialization()");
}
return arg0;
}
public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException {
if (arg1.equals("car")) {
System.out
.println("BeanPostProcessor.postProcessBeforeInitialization()");
}
return arg0;
}
}
复制代码
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public MyBeanFactoryPostProcessor() {
super();
System.out.println("BeanFactoryPostProcessor构造函数");
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
System.out.println("BeanFactoryPostProcessor.postProcessBeanFactory()");
System.out.println("我如今能够修改beanDefination可是我这里就不修改了");
}
}
复制代码
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
public MyInstantiationAwareBeanPostProcessor() {
super();
System.out
.println("InstantiationAwareBeanPostProcessorAdapter构造函数");
}
// 接口方法、实例化Bean以前调用
@Override
public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
if(beanName.equals("car")) {
System.out
.println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()");
}
return null;
}
// 接口方法、实例化Bean以后调用
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if(beanName.equals("car")) {
System.out
.println("InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()");
}
return true;
}
// 接口方法、设置某个属性时调用
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
if(beanName.equals("car")) {
System.out
.println("InstantiationAwareBeanPostProcessor.postProcessPropertyValues()");
}
return pvs;
}
}
复制代码
<bean id="beanPostProcessor" class="test.MyBeanPostProcessor"/>
<bean id="instantiationAwareBeanPostProcessor" class="test.MyInstantiationAwareBeanPostProcessor"/>
<bean id="beanFactoryPostProcessor" class="test.MyBeanFactoryPostProcessor"/>
<bean id="car" class="test.Car" init-method="myInit" destroy-method="myDestory" scope="singleton" p:carName="BMW"/>
复制代码
下面来见证下整个流程
public static void main(String[] args) {
System.out.println("开始启动容器");
ApplicationContext factory = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
System.out.println("容器初始化成功");
Car car = factory.getBean("car",Car.class);
System.out.println("开始关闭容器");
((ClassPathXmlApplicationContext)factory).registerShutdownHook();
}
复制代码
最终的console输出为:
开始启动容器
BeanFactoryPostProcessor构造函数
BeanFactoryPostProcessor.postProcessBeanFactory()
我如今能够修改beanDefination可是我这里就不修改了
InstantiationAwareBeanPostProcessorAdapter构造函数
BeanPostProcessor构造函数
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
bean的构造函数
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
InstantiationAwareBeanPostProcessor.postProcessPropertyValues()
属性注入
BeanNameAware.setBeanName()
BeanFactoryAware.setBeanFactory()
BeanPostProcessor.postProcessBeforeInitialization()
InitializingBean.afterPropertiesSet()
<bean>的init-method属性指定的初始化方法
BeanPostProcessor.postProcessAfterInitialization()
容器初始化成功
开始关闭容器
DiposibleBean.destory()
<bean>的destroy-method属性指定的初始化方法
复制代码
1)容器启动阶段
容器须要依赖某些工具类(BeanDefinitionReader)对加载的Configuration MetaData进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必 要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动工做就完成了。
BeanDefinitionReader
1)用来和配置文件打交道
2)负责从对应的配置文件中读取内容并将bean映射到BeanDefinition
3)而后将BeanDefinition注册到BeanDefinitionRegistry中
eg:PropertiesBeanDefinitionReader、XmlBeanDefinitionReader
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanDefinitionRegistry);
reader.loadBeanDefinitions("classpath:xxx");
return (BeanFactory) beanDefinitionRegistry;
复制代码
惟一能插手容器启动阶段的大佬-----BeanFactoryPostProcessor
在容器实例化对象以前,对注册到容器的BeanDefinition信息进行修改
下面是几个比较典型的BeanFactoryPostProcessor
在实例化bean前,将bean配置中的占位符替换为实际值
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
复制代码
# jdbc.properties
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
复制代码
在运行时,占位符内的内容会被替换为属性文件中的匹配的value,这个工做就是由PlaceholderConfiguer完成的,不难看出这个类应该实现了BeanFactoryPostProcessor
即容器从XML格式的文件中读取的都是字符串形式,最终应用程序倒是由各类类型的对象所构成。 要想完成这种由字符串到具体对象的转换(无论这个转换工做最终由谁来作),都须要这种转换规则 相关的信息,而CustomEditorConfigurer就是帮助咱们传达相似信息的。
以下咱们须要将String类型的2018/07/27转为date
<bean id="dateFoo" class="...DateFoo">
<property name="date">
<value>2018/07/27</value>
</property>
</bean>
复制代码
用户能够自定义CustomEditorConfigurer,来指定string到特定类型转换到逻辑
以前项目中使用JUNIT进行单元测试,可是每次单元测试都要加载全部的bean,项目规模若是较大,那么单元测试启动须要很长时间,为了解决这个问题开发了一个简单的BeanFactoryPostProcessor,原理很简单就是将全部的bean都设置为懒加载模式,单元测试中用到哪一个实例化哪一个
public class LazyBeanFactoryProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory fac = (DefaultListableBeanFactory) beanFactory;
Map<String, AbstractBeanDefinition> map = (Map<String, AbstractBeanDefinition>) ReflectionTestUtils.getField(fac, "beanDefinitionMap");
for (Map.Entry<String, AbstractBeanDefinition> entry : map.entrySet()) {
entry.getValue().setLazyInit(true);
}
}
}
复制代码
2)Bean实例化阶段 1.InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(Class beanClass,String beanName)
在执行Bean的构造器以前,若是有InstantiationAwareBeanPostProcessor那么会先执行它的postProcessBeforeInstantiation()若是此时返回的bean不为null,那么不会再继续执行后续的Bean流程,只执行postProcessAfterInitialization(),形成了"短路" 有一点须要注意的在提到InstantiationAwareBeanPostProcessor的方法的时候最好指定入参,由于它有好几个相同名字入参不一样的方法,容易发生混淆
2.对bean进行实例化
容器采用策略模式来决定采用何种方式初始化bean实例(反射 or CGLIB默认) 若是配置的bean有look-up or replace此时须要使用CGLIB建立代理类
3.InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(Class beanClass,String beanName)
4.InstantiationAwareBeanPostProcessor.postProcessPropertiesValues(PropertyValues pvs,PropertyDescriptor[] pds, Object bean, String beanName)
5.对bean进行依赖注入
实例化后的产物是BeanWrapper实例,它是对bean的包裹,接下来对这个包裹设置属性值 Ps借助Wrapper能够用统一的方式对对象属性进行注入,BeanWrapper会使用PropertyEditor进行属性的类型转换
6.检查是否实现Aware接口(BeanFactory独有)
BeanNameAware:将BeanName设置到当前实例中 BeanClassLoaderAware:将加载当前bean的ClassLoader设置到当前实例中 BeanFactoryAware:将BeanFactory设置到当前实例中
7.BeanPostProcessor.postProcessBeforeInitialization
不少ApplicationContext独有的Aware接口也是经过BeanPostPorcessor实现属性注入的
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher
(this.applicationContext); }
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); }
return bean;
}
复制代码
8.InititalizingBean.afterPropertiesSet()
9.init-method
10.BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName)
11.容器初始化成功,正常执行业务逻辑
12.DisposableBean.destroy()
13.destroy-method
Spring对于单例的bean之间的循环依赖会进行处理(Set注入),换言之对于单例构造器注入以及prototype类型的bean没法处理循环依赖。假设A依赖B,B又依赖于A,在实例化A的时候会将半成品的A放到缓存中,再去实例化所依赖的B,而在实例化B过程当中又须要去实例化A,此时直接将半成品的A填充到B中,就完成了循环依赖Bean的实例化。