2、spring_ioc

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方式注入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未名胡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值