您的位置:首页 > 编程语言 > Java开发

2、spring_ioc

2016-06-06 14:34 363 查看

1. Ioc简介:

Ioc 是Inversion of Control 控制反转的简称可以理解为将控制权交出来不由自己管理,交给一个总的控制模块进行统一管理,简单的说就是把 javabean中的依赖关系交给一个控制中心(在这里我们指的是spring ioc容器)进行统一分配注入。所以ioc我们也可以把它称为依赖注入di(dependencyinjection)

2. 依赖:

依赖就是有联系,有地方使用到它就可以说是依赖它,一个系统不可能完全避免依赖。如果你的一个类或者模块在项目中没有用到它,恭喜你,可以从项目中剔除它或者删除它了。在java代码中如果A类里包含了B类(一般情况下就是在A类中有一个全局变量是B类的类型)就可以说A类依赖B类,看下面一个简单的示例:

public class HelloAction{

private UserDao userDao = new UserDao();

public UserDao getUserDao() {
return userDao;
}

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

public void sayHello(){
System.out.println(userDao.getUsername());
}
}
public class UserDao {

public String getUsername(){
return "hello";
}
}

上面的示例我们就可以找到一个依赖项,UserAction依赖UserDao,因为HelloAction需要调用UserDao里面的getUsername();方法,所以HelloAction中必须包含一个UserDao的实例,这种关系就称为HelloAction依赖UserDao。

3. 依赖倒置:

现在需求改变了,要求helloAction不但允许 User说hello还添加一个机器狗类型也可以sayhello.所以我们要抽象出来,减少耦合。

耦合关系就是依赖关系,如果依赖关系相当繁杂,很难维护;依赖关系越少,耦合关系就越低,系统就越稳定,所以我们要减少依赖。
Robert Martin大师提出了面向对象设计原则----依赖倒置原则:   
· A. 上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。  
· B. 抽象不能依赖于具象,具象依赖于抽象。
理解:A.上层是使用者,下层是被使用者(上例中使用者是HelloAction,被使用者是UserDao 所以可以说HelloAction是上层模块,UserDao是下层模块),这就导致的结果是上层依赖下层了,下层变动了,自然就会影响到上层了,导致系统不稳定,甚至是牵一发而动全身。那怎么减少依赖呢?就是上层和下层都去依赖另一个抽象,这个抽象比较稳定,整个来说就比较稳定了。
B.面向对象编程是面向抽象或者面向接口编程,抽象一般比较稳定,实现抽象的具体肯定是要依赖抽象的,抽象不应该去依赖别的具体,应该依赖抽象。
那我们根据刚才的分析将上例中的代码进行重构:
添加一个接口IHelloDao;
public interface IHelloDao {

public String getUsername();
}

添加一个机器狗的类型实现iHelloDao;
public class BigDog implements IHelloDao {

@Override
public String getUsername() {
return "BigDog";
}

}

userDao也实现ihelloDao;
public class UserDao implements IHelloDao{
public String getUsername(){
return "hello";
}
}

这样的话helloAction里面就可以只依赖一个iHelloDao接口了代码如下:
public class HelloAction {

private IHelloDao helloDao;

public IHelloDao getHelloDao() {
return helloDao;
}

public void setHelloDao(IHelloDao helloDao) {
this.helloDao = helloDao;
}

public void sayHello(){
System.out.println(helloDao.getUsername()+ " hello!");
}
}
这样的话,我们在使用HelloAction的时候,只需要在调用sayHello方法之前调用一个setIhelloDao这个方法,设置不同的实现类即可,代码如下:

public static void main(String[] args) {
HelloAction hello = new HelloAction();
hello.setHelloDao(new UserDao());
hello.sayHello();
hello.setHelloDao(new BigDog());
hello.sayHello();
}

那这样我们的代码看上去就比较灵活了,如果下次再有什么其它的类型也需要sayhello那么我们只需要加一个类实现ihelloDao并且在main方法里面set 到HelloAction就可以了。但是这样就完美了吗,这样至少可以说是main方法所在的类都依赖了具体的实现类。那我们怎么才能完全消除这种具体实例的依赖关系呢。看下一节。。

4. 控制反转(ioc)

上面的示例中基本实现了隔离,具体的HelloAction跟具体的sayHello的对象隔离了,HelloAction只跟IHelloDao接口有关。但是Main方法里面的具体对象,写死了,控制权非常小,如果我要一支机器猫也可以sayHello呢,只能重新改代码,那这种依赖关系的控制权怎么进行转移呢?就是说不在代码里面来通过new关键字实例化具体的对象

我们可以通过反射来创建,把具体要实例化的对象名写在配置文件里,这时候客户端代码也不用变了,只需要改配置文件就好了,稳定性又有了提高,如下:

添加一个配置文件object.properties:

helloDao=com.demo.BigDog

修改test类的main方法代码如下:

public static void main(String[] args) {
//使用resourceBundle读取配置文件
ResourceBundle rb = ResourceBundle.getBundle("object");
//获取到具体实现类的全限定类名
String objectName = rb.getString("helloDao");
try {
HelloAction ha = new HelloAction();
//反射实例化具体的IHelloDao的实现类
IHelloDao helloDao = (IHelloDao)Class.forName(objectName).newInstance();
ha.setHelloDao(helloDao);
ha.sayHello();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

这样的话,如果我们需要使用不同的对象来sayHello那么只需要修改配置文件就可以了,这样可以说这种依赖关系的控制权交给了配置文件,而不是由上层对象来通过new关键字来实例化了,从而实现解耦合。

5. 依赖注入(DI)

细心的同学会发现上例中的main方法的代码中虽然控制权已经交给配置文件了,但还是通过 new关键字来实例化了HelloAction类,并且调用了helloAction的setHelloDao方法这种方式显然违背了:上层不应该依赖下层的具体实现的原则,那我们还可以对代码进行优化,解除这种依赖关系呢?那就是我们要讲的依赖注入,就是说把HelloAction的调用 setHelloDao的动作也交给配置文件来做,那这样的话我们就需要一个容器来管理所有的bean,就是说把项目中要用到的bean都实例化并且保存到一个map对象中如果某个了bean需要依赖另一个Bean那么由容器来完成这个注入的工作,那么客户端需要使用某一个bean实例,只需要从容器中获取就可以了。这个是spring的核心所在。代码如下

上例中的代码我们需要修改object.properties文件,将helloAction也配置到文件中

helloDao=com.demo.BigDog
action=com.demo.HelloAction
修改main方法中的代码

public static void main(String[] args) {
//使用resourceBundle读取配置文件
ResourceBundle rb = ResourceBundle.getBundle("object");
//获取到具体实现类的全限定类名
String objectName = rb.getString("helloDao");
//使用resourceBundle读取配置文件
//获取到具体实现类的全限定类名
String actionName = rb.getString("action");
try {
IHelloDao helloDao = (IHelloDao)Class.forName(objectName).newInstance();
HelloAction action = (HelloAction)Class.forName(actionName).newInstance();
//通过反射向action中注入helloDao,通过反射得到action实例的参数类型为IhelloDao的setHellodao方法
Method method = action.getClass().getMethod("setHelloDao", IHelloDao.class);
//执行method方法并且传入hellodao实例第一个参数表示调用的是哪个实现的方法,第二个参数表示传入的参数
method.invoke(action, helloDao);
//调用action的方法后,是由容器注入了helloDao的
action.sayHello();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}

通过这样的改造后我们就在可以不需要在main方法中使用new 关键字来实例化任何对象了。当然这个只是一个比较简单的例子实现了控制反转和依赖注入。

依赖注入常的方式就是我们上例中使用的方式叫setter注入,使用set方法。另一种注入方式是构造器注入方式,通过有参数的构造器来实例化对象。目前使用比较多,比较灵活的是set方式注入。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: