任何技术或事物都有其适用常景。

关于setter注入和构造注入的讨论就算了……没有谁比谁好,只有看谁更适用于某个常景。选择合适的就是最好的。

一、思想

IoC:控制反转是一种思想,将原本客户端的主动创建、直接依赖、事必躬亲,反转过来了:变成了衣来伸手饭来张口(当然这个伸手和张嘴还是必不可少的,不然你就啥都不做,就心想事成啊……你怎么不去成仙呢)

DI:依赖注入是实现控制反转的一种具体技术。只要你张口或者伸手了,我就给你煮饭喂你吃,买衣服来给你穿上……为什么说是一种实现技术呢,可能还有其他实现技术,目前我还不知道……可能以后不用我亲自煮饭了啊,也不用我亲自喂你了,我直接委托给机器人给你做饭喂饭就可以了,,,,,,,

很多人就初始时纠结于这两个概念间的区别,拼命地想啊,就想把他们理清楚、分开来……但是有的资料又说是一样的,有的又说不一样……其实,这个重要吗?除了在面试时那些无聊的面试官会问下这个问题。真的,可能他们自己理解的都是一知半解,我们应该跳出框框来看问题。如果你认为是一样的,那就继续当做是一样的吧;如果你接收的是不一样的概念,那么也请继续,没必要在这里纠结了,后面还要学的东西多着呢,,,,,我这里分开来也只是我自己给自己的解释而已,不必当真。

1.2、注入方式

Martin Fowler的那篇文章中定义的三种依赖注入方式:构造注入、setter方法注入、接口注入。

有必要争执哪种方式最好吗?No!!!!!!why?请看本文第一句话。

构造注入:写法繁琐,但是可以在初始化时即检测是否有bean被注入了,如果没有就当场发飙弄挂JVM,不要让它再启动了,防止以后出现位置的问题。

setter方法注入:写法简单方便,but 可能由于没有注入bean导致在使用时出现NPE。

发现没有,这两种方式的优缺点真好相对。第三种接口注入方式不说也罢,因为没人会这么使用。之前在编码时,如果使用@Autowired这个注解来要注入bean(张嘴、伸手),IDEA就会提示我:什么不要使用这个啦,然后我就去查资料,在那纠结到底怎么回事,告诉我说应该使用constructor injection ,什么balabala之类的理由……我去,我以前还纠结了好久,现在就不纠结了,和我有什么关系,我反正从没用过@Autowired,我用的一直都是@Resource,只是看到别人用的Autowired时会纠结于IDE的提示……

我自己是一个很纠结的人的,但是只有在说服自己之后,跳出原来的框才能解脱,所以我尽量用自己的想法来理解和看待。

没必要非得选择最好的,也没有唯一,选择只有合适的才是最好的。

1.3、相关概念

IoC的职责:1、创建对象(煮饭);2、注入依赖对象(喂饭)。

IoC怎么管理对象间的依赖关系(给谁喂什么饭可不能错了啊,给小孩喂奶,老人喂粥,其他人吃肉):N种方式:

1、文本记录依赖关系:张三要吃肉、李四要喝粥、王五不吃不喝……

2、语音、图像……

任何可以表示关系的且能被IoC方便识别的信息载体都可以用来管理依赖关系,but,毕竟我们还没到那么先进的地步,所以还是老老实实的想些可行的靠谱的方案吧:

1、编程式:在代码中将依赖关系写好;

2、文本配置:xml或者properties,格式随便自己定,只要写出对应的解析器就可以了;

3、注解方式:最后其实也是编程实现的;

二、Spring的设计

Spring的设计开始了:

既然强调的是基于POJO(相较于EJB说的好了很多,虽然我不清楚EJB是个什么东西……),那么就是管理所有的bean(也别问这个bean是什么含义,没有含义,就这么叫!所有的对象都是一个bean),所以定义了这么一个接口BeanFactory用来管理所有的bean。

2.1、BeanFactory

BeanFactory管理了所有的bean之后,如果需要bean对象(伸手张嘴)就是从中获取了,上面的那么多getBean方法看到了吧,就是用来干这事的。至于怎么将bean创建出来,注入进去,这个就得拆开来慢慢设计了。

这个是原型代码:只显示了获取bean,创建及注入的事后面再说

BeanFactory container = new XmlBeanFactory(new ClassPathResource("配置文件路径"));
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
newsProvider.getAndPersistNews(); 

BeanFactory里面就12个方法,且还都是获取bean和is判断的方法,根本就没有注册对象并管理依赖的功能。那么bean是怎么进去的呢?

2.2、BeanDefinitionRegistry

这里就是一个设计思想了:BeanFactory相当于一个对外的窗口(类似于facade模式),只提供查询的方法,而具体的事由其他的专业类去完成。这就出现了这个BeanDefinitionRegistry,BeanDefinitionRegistry才是真正存放对象的地方,BeanFactory只是一个对外暴露出来的接口。

2.3、BeanDefinition

按照面向对象的思想,每一类相同的事物都应该抽取出一个类来,因此在Spring的IoC容器中管理的POJO也抽取了一个共同类:BeanDefinition,每一个BeanDefinition的实例都表示一个被注入的对象,定义了一些bean的class类型信息、构造方法参数信息等。

BeanDefinitionRegistry就是用来操作BeanDefinition的。BeanDefinition主要有两个实现类:RootBeanDefinition和ChildBeanDefinition

可以上一段代码来看看怎么使用的:

    @Testpublic void testCodeBeanRegister() {DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();BeanFactory container = bindViaCode(beanRegistry);FXNewsProvider newsProvider = (FXNewsProvider) container.getBean("djNewsProvider");newsProvider.getAndPersistNews();}public static BeanFactory bindViaCode(BeanDefinitionRegistry registry) {AbstractBeanDefinition newsProvider = new RootBeanDefinition(FXNewsProvider.class);AbstractBeanDefinition newsListener = new RootBeanDefinition(DowJonesNewsListener.class);AbstractBeanDefinition newsPersister = new RootBeanDefinition(DowJonesNewsPersister.class);// 将bean定义注册到容器中registry.registerBeanDefinition("djNewsProvider", newsProvider);registry.registerBeanDefinition("djListener", newsListener);registry.registerBeanDefinition("djPersister", newsPersister);// 指定依赖关系// 1. 可以通过构造方法注入方式ConstructorArgumentValues argValues = new ConstructorArgumentValues();argValues.addIndexedArgumentValue(0, newsListener);argValues.addIndexedArgumentValue(1, newsPersister);newsProvider.setConstructorArgumentValues(argValues);// 2. 或者通过setter方法注入方式MutablePropertyValues propertyValues = new MutablePropertyValues();propertyValues.addPropertyValue(new PropertyValue("newsListener", newsListener));propertyValues.addPropertyValue(new PropertyValue("newPersistener", newsPersister));newsProvider.setPropertyValues(propertyValues);// 绑定完成return (BeanFactory) registry;}

因此上面这段代码就是定义bean容器,包装bean对象成BeanDefinition,然后将其注入到BeanDefinitionRegistry中,最后就可以从DefaultListableBeanFactory中获取bean了。

2.4、DefaultListableBeanFactory

但是这里的DefaultListableBeanFactory为什么当做BeanDefinitionRegistry来用了呢?因为DefaultListableBeanFactory既实现了BeanFactory又实现了BeanDefinitionRegistry,可以来看一下依赖关系的类图:

绿色的虚线表示实现implement(中间可能有很多abstract的class省略了,包括直接实现和间接实现),灰色的虚线表示关联关系(即拥有某个对象)。不熟悉的可以去复习下UML哈。

DefaultListableBeanFactory内部是用Map来存储beanname和beanDefinition对应关系的:

/** Map of bean definition objects, keyed by bean name */private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

2.5、编码方式实现bean的创建和依赖注入 && 配置文件方式实现的bean创建和依赖注入

理解上面的编码方式实现bean的创建和依赖注入是必须的,因为不管换成什么其他方式,最后都是落实到编码实现上的。下面再来看看配置文件方式实现的bean创建和依赖管理方式

也是类似的思路:将具体的事扔给具体的人来处理(外包?),配置文件的读取解析有专门的类来处理,首先还是定义接口了:BeanDefinitionReader,由BeanDefinitionReader相应的实现类读取配置文件并创建BeanDefinition,然后将BeanDefinition注册到BeanDefinitionRegistry中。

伪代码:

BeanDefinitionRegistry beanRegistry = <某个BeanDefinitionRegistry实现类,通常为DefaultListableBeanFactory>;
BeanDefinitionReader   beanDefinitionReader = new BeanDefinitionReaderImpl(beanRegistry);
beanDefinitionReader.loadBeanDefinitions("配置文件路径");
// 现在我们就取得了一个可用的BeanDefinitionRegistry实例 

Spring提供了PropertiesBeanDefinitionReader用于读取properties配置文件来注册bean,配置文件的格式有其自定义的要求,不过我是从来没用过的,因为一直用的都是xml文件配置,对于xml配置格式的Spring提供的实现类是XmlBeanDefinitionReader,这个具体的配置格式就不说了,无非就是那些scheme约束文件中的元素定义而已,什么<beans><bean> 之类的了……这个具体去看spring的scheme约束文档吧。如果不想用xml配置方式,想要创新?那也可以自定义一种配置方式来管理依赖关系(至于是文件还是语音,那就看你的心情了,记得要实现BeanDefinitionReader就好……)

加载xml配置文件的BeanFactory的使用:

    public static void main(String[] args){DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();BeanFactory container = bindViaXMLFile(beanRegistry);FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");newsProvider.getAndPersistNews();}public static BeanFactory bindViaXMLFile(BeanDefinitionRegistry registry){XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);reader.loadBeanDefinitions("classpath:../ news-config.xml");  return (BeanFactory)registry;// 或者直接  //return new XmlBeanFactory(new ClassPathResource("../news-config.xml"));}

第三个就是使用注解方式来实现的依赖注入了:@Resource、@Autowired、@Controller之类的,以前需要在xml文件中指定这个配置:<context:component-scan base-package="com.xxx.xxx……">

三、Spring容器的具体实现

两阶段:容器启动、bean实例化

3.1、容器启动阶段

通过BeanDefinitionReader加载配置文件,解析,创建相应的BeanDefinition对象,注册到BeanDefinitionRegistry。

3.1.1、插手容器的启动

Spring提供了一种BeanFactoryPostProcessor的容器扩展机制,在容器注册好了BeanDefinition之后,在bean实例化之前,允许我们对BeanDefinition修改。

实现:实现接口BeanFactoryPostProcessor,并通过Ordered接口指定顺序,将此Processor注册到BeanFactory中。

Spring提供了几个现成的BeanFactoryPostProcessor实现类,很少需要自己去写吧,除非有此业务需求。

PropertyPlaceholderConfigurer:修改占位符的数据,在xml中使用了占位符${username.name}之后BeanFactory在加载完所有配置后,BeanDefinition中保存的仍然是占位符的信息,当PropertyPlaceholderConfigurer被作为BeanFactoryPostProcessor应用后,才会使用properties配置文件中的配置信息来替换掉BeanDefinition中占位符所代表的属性值。

PropertyPlaceholderConfigurer同时也会从System类的Properties中查找数据来替换,可以配置是否使用System的配置信息。

应用:可以使用加密的DB账号密码,然后在PropertyPlaceholderConfigurer中解密此字符,给DBpool注入真实的账户密码信息。

PropertyOverrideConfigurer:这个没什么好说的,就是使用properties中的信息来替换掉原来的数据。多个只会保留最后一个。

使用示例代码:

    // 声明将被后处理的BeanFactory实例  ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("..."));// 声明要使用的BeanFactoryPostProcessor  PropertyPlaceholderConfigurer propertyPostProcessor = new PropertyPlaceholderConfigurer();propertyPostProcessor.setLocation(new ClassPathResource("..."));// 执行后处理操作  propertyPostProcessor.postProcessBeanFactory(beanFactory);

CustomEditorConfigurer:对BeanDefinition不做改变,只是辅助使用的:因为xml中的都是字符串,所以需要将此字符串正确的给对象赋值,就需要转换,就需要CustomEditorConfigurer来做。Spring内部通过PropertyEditor来帮助进行string到具体类型的转换,只要每种对象类型都提供一个PropertyEditor,Spring就可以根据该类型来获取其对应的PropertyEditor然后做具体的转换。Spring已经提供了一些类型转换的实现:StringArrayPropertyEditor(逗号,分隔的字符串转数组)、ClassEditor、FileEditor、LocaleEditor、PatternEditor、、、、、这些PropertyEditor Spring都会默认加载,如果是自定义的,那需要自己注入到容器中去。

1、自定义PropertyEditor,继承PropertyEditorSupport(为了简化不必要实现PropertyEditor所有的方法),

2、将自定义的PropertyEditor注册到容器中,

public class DatePropertyEditor extends PropertyEditorSupport {private String datePattern;@Overridepublic void setAsText(String text) throws IllegalArgumentException {DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(getDatePattern());Date dateValue = dateTimeFormatter.parseDateTime(text).toDate();setValue(dateValue);}public String getDatePattern() {return datePattern;}public void setDatePattern(String datePattern) {this.datePattern = datePattern;}
} 
    XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("..."));//CustomEditorConfigurer ceConfigurer = new CustomEditorConfigurer();Map customerEditors = new HashMap();customerEditors.put(java.util.Date.class,new DatePropertyEditor());ceConfigurer.setCustomEditors(customerEditors);//ceConfigurer.postProcessBeanFactory(beanFactory);

使用:

public class DateFoo {private Date date;public Date getDate() {return date;}public void setDate(Date date) {this.date = date;}
}配置类似于
<bean id="dateFoo" class="...DateFoo">  <property name="date">  <value>2007/10/16</value>  </property>
</bean>

在ApplicationContext中可以使用xml来注册bean。

3.2、bean实例化阶段

当请求getBean时,即实例化bean对象。ApplicationContext是在refresh()方法里立即实例化了所有的bean。

Spring使用策略模式来决定采用何种方式实现bean的初始化。反射直接生成bean还是cglib生成其子类。

策略接口:InstantiationStrategy,实现类:SimpleInstantiationStrategy(以反射方式实现的)、CglibSubclassingInstantiationStrategy(extends SimpleInstantiationStrategy 同时又添加了cglib的方式),Spring默认使用的是CglibSubclassingInstantiationStrategy实现类。

Spring并不是直接返回的bean,而是以BeanWrapper包裹了bean实例后的对象。

BeanWrapper:继承了PropertyAccessor ,可以以统一的方式来访问对象属性。BeanWrapper定义同时又直接或者间接继承了PropertyEditorRegistry和TypeConverter接口。当把各种PropertyEditor注册给容器时,就会被BeanWrapper用到!在第一步构造完成对象之后,Spring会根据对象实例构造一个BeanWrapperImpl实例,然后将之前CustomEditorConfigurer注册的PropertyEditor复制一份给BeanWrapperImpl实例(这就是BeanWrapper同时又是PropertyEditorRegistry的原因)。

使用BeanWrapper操作bean:

    Object provider = Class.forName("package.name.FXNewsProvider").newInstance();Object listener = Class.forName("package.name.DowJonesNewsListener").newInstance();Object persister = Class.forName("package.name.DowJonesNewsPersister").newInstance();BeanWrapper newsProvider = new BeanWrapperImpl(provider);  newsProvider.setPropertyValue("newsListener", listener);newsProvider.setPropertyValue("newPersistener", persister);assertTrue(newsProvider.getWrappedInstance() instanceof FXNewsProvider);assertSame(provider, newsProvider.getWrappedInstance());assertSame(listener, newsProvider.getPropertyValue("newsListener"));assertSame(persister, newsProvider.getPropertyValue("newPersistener")); 

各个Aware接口:只要实现了以这个Aware命名结尾的接口,那么Spring就会给其注入相应的对象。

BeanNameAware:

BeanClassLoaderAware:

BeanFactoryAware:

ResourceLoaderAware:

ApplicationContextAware:

ApplicationEventPublisherAware:

MessageSourceAware:

BeanPostProcessor:上图中的前置处理和后置处理。使用:Aware各种接口的实现类就是通过BeanPostProcessor来实现的,当ApplicationContext中每个对象的实例化过程走到BeanPost- Processor前置处理这一步时,ApplicationContext容器会检测到之前注册到容器的Application-ContextAwareProcessor这个BeanPostProcessor的实现类,然后就会调用其postProcessBefore- Initialization()方法,检查并设置Aware相关依赖。Spring的AOP则更多地使用BeanPostProcessor来为对象生成相应的代理对象。

自定义BeanPostProcessor:先定义,然后注册到容器中,

ConfigurableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(...));
beanFactory.addBeanPostProcessor(new PasswordDecodePostProcessor());
...
// getBean(); 

当然xml配置方式有其格式,上面所有的代码配置方式,最后真的是用都是用xml配置的,没见过使用BeanFactory编码实现注入的。

InitializingBean和init-method:在bean实例化完成后调用的方法。

……

Spring揭秘-笔记相关推荐

  1. Spring揭秘 读书笔记 三 bean的scope与FactoryBean

    本书可作为王富强所著<<Spring揭秘>>一书的读书笔记  第四章 BeanFactory的xml之旅 bean的scope scope有时被翻译为"作用域&quo ...

  2. 《Spring揭秘》读书笔记 2:Spring AOP

    7 一起来看AOP 2009年8月,<一起来看流星雨>开播. 2009年9月,<Spring揭秘>出版. 7.1 AOP核心概念 AOP AOP全称为Aspect-Orient ...

  3. 【Spring学习笔记-MVC-13.2】Spring MVC之多文件上传

    作者:ssslinppp       1. 摘要 前篇文章讲解了单文件上传<[Spring学习笔记-MVC-13]Spring MVC之文件上传>http://www.cnblogs.co ...

  4. Spring读书笔记——bean创建(下)

    有关Spring加载bean系列,今天这是最后一篇了,主要接上篇对于从Spring容器中获取Bean的一些细节实现的补充. <Spring读书笔记--bean加载>--Spring如何加载 ...

  5. Spring学习笔记(三) AOP_annotation,AOP_XML

    在学习课程以前,听说AOP有种很神秘的感觉,好像很好深的技术.其实原理很简单,使用动态代理的方式给程序增加逻辑.与此相似的有struts2中的filter拦截器. 再讲AOP之前先把需求说一下: 同S ...

  6. spring 事务笔记(四)

    spring 事务笔记(四) 文章目录 spring 事务笔记(四) spring 事务原理简述 声明式事务 1. 注解方式 配置事务管理器 开启事务并使用 2.xml配置方式 配置事务管理器 配置事 ...

  7. (转) Spring读书笔记-----Spring的Bean之配置依赖

    前一篇博客介绍了Spring中的Bean的基本概念和作用域(Spring读书笔记-----Spring的Bean之Bean的基本概念 ),现在介绍Spring Bean的基本配置. 从开始我们知道Ja ...

  8. spring学习笔记06-spring整合junit(出现的问题,解决的思路)

    spring学习笔记06-spring整合junit(出现的问题,解决的思路) 文章目录 spring学习笔记06-spring整合junit(出现的问题,解决的思路) 3.1测试类中的问题和解决思路 ...

  9. spring学习笔记01-BeanFactory和ApplicationContext的区别

    spring学习笔记01-BeanFactory和ApplicationContext的区别 BeanFactory 和 ApplicationContext 的区别           BeanFa ...

  10. spring学习笔记02-spring-bean创建的细节问题

    spring学习笔记02-spring-bean创建的细节问题 三种创建Bean对象的方式 Bean的作用范围 Bean的生命周期 <?xml version="1.0" e ...

最新文章

  1. 面试官:你知道哪几种事务失效的场景?
  2. 第一年的要求 工程系的研究生
  3. ASP.net Core MVC项目给js文件添加版本号
  4. python学习之路-day8
  5. 南昌工程学院计算机考试题库和答案,南昌工程学院 语试题答案.doc
  6. 简单的求三角函数sin,cos的图像
  7. (二)VISIO 中间带箭头的弧线怎么画
  8. 转发-分享手机游戏辅助编程开发教程
  9. 总结各种RGB转YUV的转换公式
  10. Parallel GC
  11. uchome迁移问题
  12. 基于百度指数的股票收益率多因子模型研究
  13. java 求正割_Java 反射机制详解
  14. 微信小程序云开发-微信小程序账号申请及新手环境配置
  15. unixprocess+java+186_interproscan 的使用和遇到的问题
  16. 【译】Using the SafetyNet API (使用SafetyNet API)
  17. (https专业版)2018年1月5日高仿互站仿友价T5虚拟交易+实物交易商城-站长交易源码送手机版程序10套模版+首页微信登陆+头部下拉导航...
  18. 蓝桥杯——算法训练——数字三角形
  19. HTML 粗体与斜体
  20. 一致性哈希算法的原理与实现

热门文章

  1. HTML+JS调用摄像头拍照并上传图片
  2. vb.net 设置打印纸张与页边距_机关公文格式设置规范(最新整理版)
  3. php过滤多空格_php如何去除多余空格
  4. OpenGL超级宝典(第7版)笔记7 细分曲面初介绍 清单3.7-3.8
  5. 《OpenGL超级宝典》 - 源代码文件
  6. delphi打包python_python for delphi 组件安装和调试的那些坑儿 !
  7. QQ for linux(ubuntu) 下载安装教程
  8. oracle创建用户
  9. vc2010解决方案项目编译顺序_安装及配置 VC2010 的详细步骤
  10. 硬盘坏了怎么恢复数据,需要哪些设备