博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
黄晓童SPRING学习笔记:对DI的深入探究
阅读量:2713 次
发布时间:2019-05-13

本文共 3957 字,大约阅读时间需要 13 分钟。

 spring的特性之一是DI,而DI的关键在于bean的装配,即创建系统各组件之间的协作关系。而组件是存活在spring容器中的,容器是spring的核心,spring提供多种容器的实现。第一种是bean工厂,即BeanFactory接口;第二种是ApplicationContext接口。BeanFactory是最简单的容器,只提供了基础的依赖注入支持;而后者是在BeanFactory基础之上的,提供了更多的系统服务。

 Spring中有多种Beanfactory的实现,其中最常见的一种就是根据XML配置文件来装载bean的XmlBeanFactory。但是要创建一个XmlBeanFactory实例需要传递一个Resource类型的实例给构造函数,正是此Resource对象提供了XML文件给BeanFactory。因此,创建一个Beanfactory可以这样做:

BeanFactory  factory  =  new  XmlBeanFactory( new FileSystemResource( “C:/abc.xml” ) );

 

这是通过文件系统来读取xml文件初始化BeanFactory的,还有许多种方法,比如ClassPathResource、InputStreamResource等。但是,执行上述代码并没有实例化任何一个bean,要实例化一个bean还要调用getBean方法:factory.getBean(“beanName”); 当调用过该方法之后,BeanFactory会依据xml配置文件设置该bean的属性并实例化它。
 然而,很多时候我们需要一些更强大的功能,这时我们就需要用到ApplicationContext了。而且在大多数情况下,我们是会使用后者的。然而spring的应用上下文对象也很好用,载入一个上下文很简单:

ApplicationContext context = new FileSystemXmlApplicationContext(“C:/abc.xml”);

 

或者像这样:

ApplicationContext context = new ClassPathXmlApplicationContext(“abc.xml”);

 

 这时要获得一个bean和BeanFactory时的一样,毕竟ApplicationContext是继承自BeanFactory的。
 下面将通过一些例子来由浅入深的进行阐述。一年一度的Java天才大赛来了,每个参赛者都可以表演任何节目。因此定义一个接口,每位参赛者都要实现该接口:

public  interface  Performer{ public  void  perform(); }

 

首先出场的是一位魔术师,它是一个简单的bean:

public  class  Juggler  implements  Performer {  private  int  beanBags = 3;     public  Juggler(int  beanBags) {    this.beanBags = beanBags;    }  public  void  perform(){    System.out.println("JUGGLING " + beanBags + " BEANBAGS");  }}

 

传统的注入该bean方式如下:

 

spring容器装载该bean时,会调用该类的默认构造函数,因此该类的实例的beanBags变量的值是3。我们还可以通过如下的配置方法让该类的实例的变量值是15:

 

这样,spring注入该bean的时候,就会调用该类的重载构造函数。如果对于一些特殊的构造函数的参数中包含对象的(非基本类型),可以通过如下方式指定参数的值:

 

其中“object-Bean-Id”就是另一个bean的id

下面,为了演示spring的setter注入方式,我们荣幸的请到了出色的乐器演奏家Kenny。

public  class  InstrumentKenny  implements  Performer {  public void perform()  {    System.out.print("Playing " + song + " : ");    instrument.play();  }    private String song;  public void setSong(String song) {    this.song = song;  }    private Instrument instrument;  public void setInstrument(Instrument instrument) {    this.instrument = instrument;  }}

 

当然,我们需要定义一个乐器接口Instrument:

public  interface  Instrument{  public void play();}

 

现在,我们创造一个萨克斯风乐器:

public  class  Saxophone  implements  Instrument{ public void play()  { System.out.println("TOOT TOOT TOOT"); }}

 

这时,我们只需要进行如下配置,就可以让Kenny去演奏萨克斯风了:

 

 其实我们会发现,使用setter注入和使用构造函数注入,没什么区别,代码几乎相同。如果,kenny很聪明并且还会演奏钢琴的话,我们只需要声明一个钢琴类,再修改一下配置文件,ref=” piano” 即可。几乎不需要修改任何已有代码,这样就实现了松耦合的目的。

 现在,kenny可以演奏任何实现Instrument接口的乐器了,然而,这些乐器也可以被其他人所演奏,因此,就产生了乐器的个人卫生问题(萨卡斯风是要使用嘴吹的)。我们可以通过注入内部bean来解决这个问题:

 

 内部bean不能被其他bean所复用,因此该bean也就没有必要有id属性。当然,在构造函数注入方式下也可以使用内部bean。

 下面,将阐述一下spring的自动注入。spring提供了四种自动装配类型:byname、bytype、constructor、autodetect。其中,byname使用最为广泛。现在让我们来回忆一下kenny演奏萨克斯风的配置:

 

我们可以将上面的配置改成如下这样,以实现spring的自动注入:

 

 然而,我们也可以使用混合的方式,如:在设置autowire=”byType”时,显式的装配某个bean,而不需要spring自己去寻找一个type相同的bean,以避免有多个相同类型的bean时spring抛出异常。

 由此我们可以发现,spring的自动装配存在很多问题,其中最大的缺点就是缺乏代码的可读性和透明性。
 我们已经了解了spring对bean的装载的基础知识。下面我们来说一下spring中bean的范围。在spring中,bean的范围可以是如下几种:singleton、prototype、request、session、global-session,比如:

 

其中,如果不指定scope的话,默认会是单例的,即singleton的,意思是在整个spring容器中,该bean只有一个实例;而prototype则与之相反,每次使用的时候都会创建一次。当然,我们也可以通过其他手段来实现单例:比如我们可以设置bean标签的factory-method属性来指定该类的工厂方法。

 在bean生命周期中有两个重要的环节:初始化和销毁。为一个bean指定一个初始化方法和销毁方法很简单,只要设置bean标签的init-method属性和destroy-method即可。当然我们还有另一个方法:实现InitializingBean和DisposableBean接口,然后,在该类中实现afterPropertiesSet方法和destroy方法,以实现bean的初始化和销毁。
 下面我们来看一下bean的继承。spring的bean标签中有两个属性:parent和abstract。前者相当于java里的extends,后者则是告诉spring容器,该bean不能被实例化。举个例子,前面我们说过,kenny是一个擅长吹萨克斯的选手,他的配置文件如下:

 

 可是,这个比赛不止kenny一个人会吹萨克斯,现在又有一个david也吹萨克斯,而且也演奏“Jingle bells”。这样的话,如果配置两条除id以外都相同的bean多少有些累赘。这时我们就需要使用到bean的继承特性。配置如下:

 

 当然,如果现在又来了一位选手,他会吹萨克斯,但是演奏的不是“Jingle bells”,这是否意味着已有的父bean不能使用了呢?当然不是的,我们还可以覆盖父bean的属性。比如:

 

 然而,声明父bean时我们也可以不指定bean的class属性,具体的class由子bean去指定,这样,我们就可以将一些常用到的属性提出到父bean,让各个bean去继承,而各子bean不必是相同的类型。

转载地址:http://oulvd.baihongyu.com/

你可能感兴趣的文章
小甲鱼笔记:数据结构——线性表(一)线性表的顺序存储结构,线性表顺序存储结构的增,删,插入元素操作
查看>>
JAVA基础——集合框架(一)对象数组建立,迭代器原理,集合的遍历之集合转数组遍历,迭代器遍历,Collection的基本功能测试
查看>>
JAVA基础——集合框架(二)List集合(Vector集合,ArrayList集合,LinkedList集合)各集合方法介绍,去除重复元素,栈堆模拟,foreach语句,数组转集合及集合转数组
查看>>
JAVA框架——SSM整合(maven项目),使用IDEA做一个简单的增删改查,使用ajax展示数据
查看>>
JAVA框架——SpringMVC总结(二)拦截器与过滤器的区别,静态资源的访问问题,请求重定向和转发,异常处理以及拦截器,拦截器与过滤器的区别
查看>>
Linux笔记(一)——虚拟机的安装与Centors安装,安装vmtools,虚拟机的克隆,虚拟机的快照,共享文件的使用
查看>>
小甲鱼笔记C语言入门(一)——数据类型,运算符和表达式
查看>>
小甲鱼笔记C语言入门(二)——数据的输入与输出,putchar(),.getchar,printf,scanf,顺序结构
查看>>
小甲鱼笔记C语言入门(三)——分支结构,运算符优先级,if语句注意事项,三目运算,Switch语句
查看>>
小甲鱼笔记C语言入门(四)——goto语句,while语句,dowhile语句,for语句。break与continue
查看>>
互联网面试之数据库
查看>>
PHP基础 -- 简答
查看>>
Java基础之数据类型
查看>>
Spring 基础框架一:深入理解Spring IOC
查看>>
spring AspectJ的Execution表达式
查看>>
JAVA设计模式之访问者模式详解
查看>>
Java过滤器与SpringMVC拦截器之间的关系与区别
查看>>
关于group by 两个或以上条件的分析
查看>>
SpringMVC @PathVariable 映射 URL 绑定的占位符 /{xxx}
查看>>
non-compatible bean definition of same name and class [x
查看>>