Spring进阶(二)—初识IOC


背景

   两个对象之间,一个对象方法的实现需要另外一个对象的支持,这两个对象就存在依赖关系,那么她们的关系如何建立?

   传统设计:通过由调用者来创建被调用者的实例,即使使用接口来隔离使用者和具体实现之间的依赖关系,但是不管怎样抽象,最终还是要创建具体实现类的实例,这种创建具体实现的实例对象就会对于具体实现的依赖,为了消除这种创建依赖性,需要把依赖移出到程序的外部。

    IOC很好的解决了这个问题,它将组件间关系从程序内部提到外部容器来管理,由容器在运行期将组件间的某种依赖关系动态的注入组件中。

   注:我们说的容器(在 Spring 框架中是 IOC 容器


介绍

   IOCInversion of Control),由著名的好莱坞理论:Do not call us, we will call you.依赖对象(控制权)发生变化,由最初的类本身来管理对象,转变为IOC框架来管理这些对象,使得依赖脱离类本身控制,从而实现松耦合。

    DI(DependencyInjection)是IOC的一种特定形态,是指寻找依赖项的过程不在当前执行代码的直接控制之下

 

应用场景——变化

   我们预先根据经验预料或者后期重构在某个地方会产生变化,就可以将依赖解耦,采用外部文件的方式动态的将对象的创建注入到程序中。

 

演变过程

手机、使用者、客户端

1、依赖

    依赖就是有联系,有地方使用它就是有依赖它

       客户端:
		public class Client {
			//客户端调用
			public static void main(String[] args) {	
			       Girl xiaohong=new Girl();
			       HuaWei huaWei=new HuaWei();
			       xiaohong.use(huaWei);
			}
		}
	使用者:
		//女孩使用者
		public class Girl{
			public void use(HuaWei phone){
				phone.phoneType();
			}
		}
	手机:
		//华为手机
		public class HuaWei{
			public void phoneType() {
				System.out.println("    华为手机");
			}
		}

结果:


   用户操作Person类中的use方法,指定某个人使用某种类型的手机,三者关系:

Person依赖Phone

Client依赖Person

Client依赖Phone

2、依赖倒置

   需求变了:有不同的人,使用不同的手机.上面的实例只能解决某类人的需求,耦合性极高,那怎样解决呢?

方案:

   依赖倒置原则:上层模块不应该依赖于下层模块,它们共同依赖于抽象/接口

具体实现:

Person依赖Phone,Person类依赖一个抽象的IPhone

Client依赖Person,让Client类依赖一个抽象的Iperson

Client依赖Phone,让Client类依赖一个抽象的IPhone

IPerson不依赖具体的Phone,依赖于抽象的Iphone


<span style="font-size:18px;">     client:
	        public static void main(String[] args) {	
			//女孩使用三星手机
			System.out.println("女生:");
			IPerson xiaoHong=new Girl();	   
			IPhone sanXing=new SanXing();
			xiaoHong.use(sanXing);
			
			
			//男孩使用华为手机
			System.out.println("男生:");
			IPerson xiaoMing=new Boy();
			IPhone huaWei=new HuaWei();
			xiaoMing.use(huaWei);
		}
    使用者:
	//使用者接口
	public interface IPerson {
		public void use(IPhone phone);
	}
	//女孩使用者
	public class Girl implements IPerson{
		public void use(IPhone phone){
			phone.phoneType();
		}
	}
	//男孩使用者
	public class Boy implements IPerson {
		public void use(IPhone phone) {
			phone.phoneType();
		}
	}
   手机类型:
	//手机类型接口
	public interface IPhone {
		public void phoneType();
	}
	//三星手机
	public class SanXing implements IPhone{
	
		public void phoneType() {
			System.out.println("    三星手机");
		}	
	}	
	//华为手机
	public class HuaWei implements IPhone {
		public void phoneType() {
			System.out.println("    华为手机");
		}
	}
</span>



   面代码进行了抽象,目的减少依赖,但客户端依赖关系反而增加了接口和具体实现,接口不需变动,但具体实现是变动的。接口一定是需要实现的,肯定会使用new来产生对象;这样一来,耦合关系就产生了。


3、控制反转IOC

   上面的实现结果是哪一类人用特定类型的手机,这并不是我们想要的结果,具体什么样的人用什么样的手机,控制权应该交给用户自己。那控制怎样进行转移?IOC底层通过反射来创建,把具体的文件名写在配置文件中,只需要修改配置文件就好了,可以让对象在生成时才决定要生成哪一种对象。

    IOC容器帮我们实现了,你要什么对象它就给你什么对象,原先的依赖关系就没了。

两种实现模式:

    依赖查找(Dependency Lookup)容器提供回调接口和上下文条件给组件。这样一来,组件就必须使用容器提供的API来查找资源和协作对象,仅有的控制反转只体现在那些回调方法上,容器将调用这些回调方法,从而让应用代码获得相关资源。

    依赖注入(Dependency Injection,简称DI)组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。容器全权负责的组件的装配,它会把符合依赖关系的对象通过JavaBean属性或者构造函数传递给需要的对象。

4、依赖注入DI

   控制反转,是一个思想概念,而依赖注入是其具体的思想。

   依赖注入:就是IOC容器在运行期间,动态将依赖关系注入到对象中。     

   

   三种方式

   接口注入:服务需要实现专门的接口,通过接口,由对象提供这些服务,可以从对象查询依赖性

   setter注入:通过JavaBean的属性分配依赖性。

   构造函数注入:依赖性以构造函数的形式提供,不以 JavaBean 属性的形式公开。

       Spring 框架的 IOC 容器采用构造方法和setter方法

 

具体实现:构造方法注入

     

  配置文件applicationContext:
         <?xml version="1.0" encoding="UTF-8"?>
	<beans xmlns="http://www.springframework.org/schema/beans"
		     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		     xmlns:aop="http://www.springframework.org/schema/aop"
		     xmlns:tx="http://www.springframework.org/schema/tx"
		     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
	    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
	    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
	 
	  <bean id="SanXing" class="com.pjbowernode.ioctest.SanXing"/>
	  <bean id="Girl" class="com.pjbowernode.ioctest.Girl"/>
	</beans>
	
	//客户端调用
	public class Client {
		public static void main(String[] args) {
			//读取applicationContext.xml配置文件
			BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
			
			//获取人和手机的实例
			IPerson xiaohong=(IPerson)factory.getBean("Girl");
			IPhone sanxing=(IPhone)factory.getBean("SanXing");
			
			//调用使用者方法
			xiaohong.use(sanxing);
		}
	}

	//使用者接口
	public interface IPerson {
		public void use(IPhone phone);
	}
	//女孩使用者
	public class Girl implements IPerson{
		public Girl(){
			
		}
		public void use(IPhone phone){
			phone.phoneType();
		}
	}

	//手机类型接口
	public interface IPhone {
		public void phoneType();
	}
	//三星手机
	public class SanXing implements IPhone{
		public void SanXing(){
			
		}	
		public void phoneType() {
			System.out.println("    三星手机");
		}	
	}	

输出结果:三星手机

 

 小结:

     我们将IPersonIPhone的具体对象的实例化放在applicationContext中去控制,程序运行时将依赖关系注入到两个具体的对象中。

 

优缺点

  好处:降低代码之间的耦合度,让代码更易于测试、更易读,应对变化只需要修改XML即可

  不足:

      生成一个对象的步骤变复杂了

      对象生成因为是使用反射编程,在效率上有些损耗。

      缺少IDE重构操作的支持,如果在Eclipse要对类改名,那么你还需要去XML文件里手工去改了。

 

总结

    IOC的概念是从王勇老师Spring视频中接触到了,听老师讲的时候很简单,例子也顺利实现。现在总结的时候发现IOC的博大精深,网上各种大牛写了很多这个方面的文章,很受益。自己这个阶段的学习只是宏观上去了解,简单总结一下,以后随着实践不断深入。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值