首页 > 程序开发 > 软件开发 > Java >

Spring(4)--Spring的核心机制:依赖注入(面向接口)

2017-08-02

Spring(4)--Spring的核心机制:依赖注入(面向接口),Spring框架的核心功能有两个:1>Spring容器作为超级大工厂,负责创建管理所有的java对象,这些java对象被称为Bean。

Spring(4)--Spring的核心机制:依赖注入(面向接口),Spring框架的核心功能有两个:

1>Spring容器作为超级大工厂,负责创建管理所有的java对象,这些java对象被称为Bean;

2>Spring容器管理容器中Bean之间的依赖关系,Spring使用一种被称为依赖注的方式来管理Bean之间的依赖关系;

一,依赖注入

A对象调用B对象,在传统模式下有如下两种做法:

1>原始做法:调用者主动创建被依赖对象,然后在调用被依赖对象的方法;

2>简单工厂模式:调用者先找到被依赖对象的工厂,然后主动通过工厂去获取被依赖对象,最后在调用被依赖对象的方法;

对于简单工厂方式,大致需要把握三点:

1>将被依赖对象的创建交给工厂完成;

2>调用者面向被依赖对象的接口编程;

3>调用者通过工厂来获得被依赖的组件;

通过这三点改造,可以保证调用者只须与被依赖对象的接口耦合,这就避免了类层次的硬编码耦合,但是缺点是调用者组件需要主动通过工厂去获取被依赖对象,这就会带来调用组件与被依赖对象工厂的耦合;

依赖注入通常有如下两种:

1>设值注入:IoC容器使用成员变量的setter方法来注入被依赖对象;

2>构造注入:IoC容器使用构造器来注入被依赖对象;

1,设值注入:

Spring推荐面向接口编程,这样可以更好的让规范和实现分离,从而提供更好的解耦,对于一个JavEE应用,不管是DAO组件,还是业务逻辑组件,都应该先定义一个接口,该接口定义了该组件应该实现的功能,但功能的实现则由其实现类提供,以便于后期的升级和维护,下面的示例更加规范:

package com.anlw.service;

public interface Person {

//定义一个使用斧头的方法

public void useAxe();

}

package com.anlw.service;

public interface Axe {

//Axe接口定义一个chop()的方法

public String chop();

}

package com.anlw.service.impl;

import com.anlw.service.Axe;

public class StoneAxe implements Axe {

@Override

public String chop() {

// TODO Auto-generated method stub

return "斧头砍柴好慢~";

}

}

package com.anlw.service.impl;

import com.anlw.service.Axe;

import com.anlw.service.Person;

public class Chinese implements Person {

private Axe axe;

//设值注入所需的setter方法

public void setAxe(Axe axe){

this.axe = axe;

}

//实现Perosn接口的useAxe()方法

@Override

public void useAxe() {

//调用axe的chop()方法

//表明Person对象依赖axe对象

System.out.println(axe.chop());

}

}

Spring配置文件配置bean实例通常会指定两个属性:

1>id:指定该Bean的唯一标识,Spring会根据id属性值来管理bean,程序通过id属性值来访问该bean实例;

2>class:指定该bean的实现类,此处不可再用接口,必须使用实现类,Spring容器会根据xml解析器读取该属性值,并利用反射来创建该实现类的实例;

测试方法:

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.anlw.service.Person;

import com.anlw.service.impl.Chinese;

public class Test {

public static void main(String[] args) {

//创建Spring容器

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

//下面两个是重载方法,获取id为person的Bean

Person c = (Person) ctx.getBean("chinese");

//Person c = ctx.getBean("chinese", Person.class);

//调用方法

c.useAxe();

}

}

Spring IoC容器的三个基本要点:

1>应用程序的各组件面向接口编程。面向接口编程可以将组件之间的耦合关系提升到接口层次,从而有利于项目后期的扩展;

2>应用程序的各组件不再由程序主动创建,而是由Spring容器负责产生并初始化;

3>Spring采用配置文件或注解来管理bean的实现类、依赖关系,Spring容器则根据配置文件或注解,利用反射来创建实例,并为之注入依赖关系;

2,构造注入:

通过setter方法为目标bean注入依赖关系的方式被称为设值注入,另外还有一种注入方式,这种方式在构造实例时,已经为其完成了依赖关系的初始化,这种利用构造器来设置依赖关系的方式,被称为构造注入;

元素总是默认驱动Spring调用无参构造器来创建对象,那怎么驱动Spring调用有参构造器去创建对象呢?答案是子元素,每个子元素代表一个构造器参数,如果元素包含N个子元素,就会驱动Spring调用带N个参数定的构造器来创建对象;

对上面的设值注入的示例做两步更改:

1>

package com.anlw.service.impl;

import com.anlw.service.Axe;

import com.anlw.service.Person;

public class Chinese implements Person {

private Axe axe;

//构造注入所需的带参数的构造器

public Chinese(Axe axe){

this.axe = axe;

}

//实现Perosn接口的useAxe()方法

@Override

public void useAxe() {

//调用axe的chop()方法

//表明Person对象依赖axe对象

System.out.println(axe.chop());

}

}

2>

上面是构造注入的示例;

其反射代码如下:

String idStr = "chinese";//解析元素得到id属性值为chinese

String refStr = "stoneAxe";//解析元素得到的ref属性值为stoneAxre

Object paramBean = container.get("refStr");

//Spring会用反射的方式执行下面的代码,此处为了降低阅读难度,该行代码没有使用反射

Object obj = new com.anlw.service.impl.Chinese(paramBean);

//container代表Spring容器

container.put(idStr,obj);

由此可以看出,使用了有参构造器创建实例,当Bean实例被创建完成后,该Bean的依赖关系已经设置完成,与设值注入的区别在于:创建Person实例中Axe属性的时机不同,设置注入是先通过无参数的构造器创建一个Bean实例,然后调用对应的setter方法注入依赖关系;而构造注入则直接调用有参构造器,当bean实例创建完成后,已经完成了依赖关系的注入;

配置元素时可以指定一个index属性,用于指定该构造参数值将作为第几个构造参数值,例如index="0"表明该构造参数值将作为第一个构造参数值;

注意几点:

1>

如果配置文件为:

上面的粗体字代码相当于让Spring调用如下代码:(Spring底层用反射执行该代码)

Object bean1 = new lee.Test("hello","23");

由于Spring本身提供了功能强大的类型转换机制,因此如果lee.Test1只包含一个Test1(String,int)构造器,那么上面配置文件相当于让Spring执行如下代码:(Spring底层用反射执行该代码)

Object bean1 = new lee.Test("hello",23);

那么如果lee.Test1既含有Test1(String,int)构造器,又含有Test1(String,String)构造器,肯定指定Test1(String,String)这个,因为23毕竟是字符串,正确匹配;

为了明确指定数据类型,Spring为元素指定了一个type属性,例如

两种注入方式的对比:

相比之下,设值注入有如下优点;

1>与传统的JavaBean的写法更相似,程序开发人员更容易理解,接受,通过setter方法设定依赖关系显得更加直观,自然;

2>对于复杂的依赖关系,如果采用构造注入,会导致构造器非常臃肿,难以阅读,Spring在创建Bean实例时,需要同时实例化其依赖的全部实例,因而导致性能下降,而使用设置注入则能避免这些问题;

3>尤其在某些成员变量可选的情况下,多参数的构造器更加笨重;

某些特定的场景下,构造注入有如下优势:

1>构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入,例如组件中其他依赖关系的注入,常常需要依赖DataSource的注入。采用构造注入,可以在代码中清晰的决定注入顺序;

2>对于依赖关系无需变化的Bean,构造注入更有用处,因为没有setter方法,所有的依赖关系全部在构造器内设定,因此无需担心后续的代码对依赖关系产生破环;

3>依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系,对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则;

建议采用以设值注入为主,构造注入为辅的注入策略;

对于依赖关系无需变化的注入,尽量采用构造注入;

而其他依赖关系的注入,则考虑采用设值注入;

相关文章
最新文章
热点推荐