前面一篇博客大致讲了一下Spring的IOC容器的原理,IOC即控制反转主要是依靠依赖注入的方式来实现的。依赖注入是指所依赖的对象不是由自己new出来的,而是用别的方式像打针似的注入进来。 其实说白了不管是控制反转还是依赖注入都说明了Spring采用动态、灵活的方式来管理各种对象。
Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。有以下几种注入方式:
1. Setter 注入
因为对于javaBean来说,我们可以通过setter和getter方法分别改变对象和获取对象的属性。所以当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。例如下面的代码:
. Person接口
package com.tgb.depency; /** * 定义Person接口 * @author MINGXUANYUN * */ public interface Person { //定义一个吃东西的方法 public void eatSomething(); //定义一个个人信息方法 public String personInfo(); }
.Eat接口
package com.tgb.depency; public interface Eat { //定义一个吃苹果的方法 public String eatApple(); }
.Person的实现类
/** * Person的实现类 * @author MINGXUANYUN */ public class Huohuo implements Person{ private Eat eat; //默认的构造器 public Huohuo(){} public Eat getEat() { return eat; } public void setEat(Eat eat) { this.eat = eat; } //实现Person吃东西的方法 @Override public void eatSomething() { System.out.println(eat.eatApple()); } @Override public String personInfo() { return null; } }
.Eat的实现类
/** * 大口大口吃 * @author MINGXUANYUN */ public class WideMouthEat implements Eat { @Override public String eatApple() { return"张大嘴巴吃苹果很粗鲁的!!!"; } }
.配置文件,将Person实例和Eat实例组织在一起
<!--Setter注入测试实例 --> <bean id="Huohuo" class= "com.tgb.depencyimpl.Huohuo"> <property name="eat" ref="WideMouthEat"></property> </bean> <bean id="WideMouthEat" class="com.tgb.depencyimpl.WideMouthEat"> </bean>
从以上例子可以看出:Spring将bean与bean之间的依赖关系放到配置文件里管理,而不是写在代码里。通过配置文件,Spring精确的为每个bean注入每个属性。<bean>中的name属性是class属性的一个别名,class属性指真正的实现类(类的全名)。Spring自动接管每个bean里的 property元素定义。Spring再执行无参数的构造器,创建默认的bean后,调用对应的setter方法为程序注入属性值。
.主程序
public class SetterTest { public static void main(String[] args) { //实例化Spring的上下文 ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml"); //强制转换接口类型 Person person = (Person) ctx.getBean("Huohuo"); //执行Person的eatSomething方法 person.eatSomething(); } }
.运行结果输出 :
张大嘴巴吃苹果很粗鲁的!!!
说明: 主程序调用person.eatSomething方法,该方法方法体里需要有Eat的实例,但主程序里并没有任何地方为Person实例传入Eat实例,Eat实例是由Spring在运行期间动态注入的。Person实例不需要了解Eat实例的具体实现和创建过程。程序运行到Eat实例的时候,Spring自动创建Eat实例,然后注入给它的调用者,Person实例运行到需要Eat实例的时候,自动产生Eat实例。
这样有什么好处呢?如果我们需要另一个Eat实现类来为Person类使用。Person和其实现类都无需改变。只需要增加一个Eat的实现类,并在配置文件中配置就可以。
例如,增加Eat实现类ChewslowlyEat
import com.tgb.depency.Eat; public class ChewslowlyEat implements Eat { @Override public String eatApple() { return "细嚼慢咽淑女的典范!!"; } }
修改配置文件:
<bean id="Huohuo" class= "com.tgb.depencyimpl.Huohuo"> <property name="eat" ref="ChewslowlyEat"></property> </bean> <bean id="ChewslowlyEat" class="com.tgb.depencyimpl.ChewslowlyEat"></bean>
输出结果:
细嚼慢咽淑女的典范!!
2. 构造器注入
指被注入对象通过在其构造函数中声明依赖对象的参数列表。
.定义一个Person实现类,用于构造器注入
/** * 构造器注入测试 * @author MINGXUANYUN */ public class Yunyun implements Person{ private String userName; private String kind; private Eat eat; //默认构造方法 public Yunyun(){}; //构造注入所需的带参数构造器 public Yunyun(Eat eat,String userName,String kind){ this.eat = eat; this.userName = userName; this.kind = kind; } @Override public String personInfo() { return userName + "永远" + kind +"岁" + "----" + eat.eatApple(); } @Override public void eatSomething() { // TODO Auto-generated method stub } }
在构造Yunyun实例时,创建三个成员变量userName、kind和对象类型的eat。但是并没有设置setter方法。在构造Person实例时,Spring为Person实例注入所依赖的Eat实例并且为两个成员变量赋值。构造注入的配置文件如下:
.xml配置文件
<!--构造器注入配置实例 --> <bean id="Yunyun" class= "com.tgb.depencyimpl.Yunyun"> <constructor-arg index="0" ref="ChewslowlyEat"></constructor-arg> <constructor-arg index="1"> <value>朱火云 </value> </constructor-arg> <constructor-arg index="2"> <value>18</value> </constructor-arg> </bean> <bean id="ChewslowlyEat" class="com.tgb.depencyimpl.ChewslowlyEat"/>
在配置文件中,不用<property>的形式,而是使用<constructor-arg>标签。ref属性指向其它<bean>标签的name属性。由于我们可能传入多个类型一致的构造参数。所以可以用type和index确定我们使用的哪一个构造函数。
.主程序
public class ConstructorTest { public static void main(String[] args) { String fileName = "bean.xml"; ApplicationContext ac = new FileSystemXmlApplicationContext(fileName); // 取得一个实例 Yunyun yunyun = (Yunyun) ac.getBean("Yunyun"); System.out.println(yunyun.personInfo()); } }
输出结果:
朱火云 永远18岁细嚼慢咽淑女的典范!!
3.静态工厂方法注入
指通过调用静态工厂的方法来获取自己需要的对象。
.定义一个UserDao类
public class UserDao { public static UserDao getInstance(){ return new UserDao("static factory method"); } private String name=""; public UserDao(String name){ this.name = name; } public void create(){ System.out.println("create user from-" + name); } }
.定义一个UserManager类
import com.tgb.depency.UserDao; public class UserManager { //注入UserDao对象 private UserDao userDao; public void createUser(){ userDao.create(); } public void setUserDao(UserDao userDao) { this.userDao = userDao; } public UserDao getUserDao() { return userDao; } }
.xml配置文件
<!-- 静态工厂方法注入 --> <bean name="userManager" class="com.tgb.depencyimpl.UserManager"> <property name="userDao" ref="userDao"></property> </bean> <bean name="userDao" class="com.tgb.depency.UserDao" factory-method="getInstance"> </bean>
factory-menthod定义了userDao 。Bean使用UserDao类的getInstance方法来创建自己的实例。
.主程序
public class StaticFactoryTest { public static void main(String[] args) { String fileName = "bean.xml"; ApplicationContext ac = new FileSystemXmlApplicationContext(fileName); UserManager userManager = (UserManager) ac.getBean("userManager"); userManager.createUser(); } }
输出结果:
create user from-static factory method
总结:我们原来学三层的时候,UI层调用BLL、BLL调用DAO层。各层与各层之间虽然抽象出了接口层,调用接口。但是在new的时候指向的还是具体的实现。而现在Spring有效的管理各层之间对象的调用。 不管是Action调用Services对象,还是Services调用Dao对象,Spring以松耦合的方式将它们组织在一起。各层之间不需要关心对象间的具体实现,以及如何创建,完全面向接口编程。
版权声明:本文为博主原创文章,未经博主允许不得转载。