关于Spring的IOC原理

原创
2014/09/22 16:38
阅读数 110
 Spring   IOC原理


内部最核心的就是IOC了,直观地讲,就是容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在。控制权由应用代码中转到了外部容器,控制权的转移是所谓反转。

IoC还有另外一个名字——“依赖注入(Dependency Injection)”。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象地说,即由容器动态地将某种依赖关系注入到组件之中。


依赖注入的三种实现类型:接口注入、 Setter 注入和构造器注入。

1、接口注入

public class ClassA 
{ 
private InterfaceB clzB; 
public void doSomething() { 
Ojbect obj =Class.forName(Config.BImplementation).newInstance(); 
clzB = (InterfaceB)obj; 
clzB.doIt() 
} 
…… 
}
上面的代码中, ClassA 依赖于 InterfaceB  的实现,如何获得 InterfaceB 实现类的实例?传统的方法是在代码中创建 InterfaceB 实现类的实例,并将起赋予 clzB 。  而这样一来,  ClassA 在编译期即依赖于 InterfaceB  的实现。为了将调用者与实现者在编译期分离,于是有了上面的代码.  我们根据预先在配置文件中设定的实现类的类名  (Config.BImplementation) ,动态加载实现类,并通过 InterfaceB 强制转型后为 ClassA  所用。这就是接口注入的一个最原始的雏形。  而对于一个 接口 型 IOC  容器而言,加载接口实现并创建其实例的工作由容器完成,见如下代码。 

public class ClassA 
{ 
private InterfaceB clzB; 
public Object doSomething(InterfaceB b) 
{ 
clzB = b; 
return clzB.doIt(); 
} 
…… 
}
在运行期, InterfaceB 实例将由容器提供。 接口型 IOC 发展较早(有意或无意),在实际中得到了普遍应用,即使在 IOC 
的概念尚未确立时,这样的方法也已经频繁出现在我们的代码中。
public class 
MyServlet extends HttpServlet { 
public void doGet(HttpServletRequest 
request,HttpServletResponse response)throws ServletException, IOException { …… } 
}
上面的代码应该有印象吧
这也是一个 接口 型注入, HttpServletRequest 和 HttpServletResponse 实例由 Servlet  Container 在运行期动态注入。 


2、Setter( 设值 ) 注入 

各种类型的依赖注入模式中,设值注入模式在实际开发中得到了最广泛的应用。

public class 
ClassA { 
private InterfaceB clzB; 
public void setClzB(InterfaceB clzB) 
{ 
this. clzB = clzB; } 
…… 
}


再如下面的例子:

这里是TextEditor.java文件的内容:

package com.yiibai;

public class TextEditor {
   private SpellChecker spellChecker;

   // a setter method to inject the dependency.
   public void setSpellChecker(SpellChecker spellChecker) {
      System.out.println("Inside setSpellChecker." );
      this.spellChecker = spellChecker;
   }
   // a getter method to return spellChecker
   public SpellChecker getSpellChecker() {
      return spellChecker;
   }

   public void spellCheck() {
      spellChecker.checkSpelling();
   }
}
在这里,需要检查setter方法​​的命名约定。设置我们使用setSpellChecker()方法,这是非常类似于Java POJO类的变量的拼写检查器。让我们创造另一个相关的类文件SpellChecker.java,内容如下:
package com.yiibai;

public class SpellChecker {
   public SpellChecker(){
      System.out.println("Inside SpellChecker constructor." );
   }

   public void checkSpelling() {
      System.out.println("Inside checkSpelling." );
   }
   
}
以下是MainApp.java文件的内容:

package com.yiibai;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");

      TextEditor te = (TextEditor) context.getBean("textEditor");

      te.spellCheck();
   }
}
配置文件为

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.yiibai.TextEditor">
      <property name="spellChecker" ref="spellChecker"/>
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="spellChecker" class="com.yiibai.SpellChecker">
   </bean>

</beans>

3、构造器注入 

依赖关系是通过类构造函数建立的 容器通过调用类的构造方法将其所需的依赖关系注入其中 

public 
class DIByConstructor { private final DataSource dataSource; 
public 
DIByConstructor(DataSource ds) { 
this.dataSource = ds; 
} 
…… 
}
再如下例子:

在需要注入的类中,编写注入的构造方法

public PersonServiceBean(PersonDao personDao, String string) {
		this.personDao = personDao;
		this.string = string;
	}
在beans.xml中申明被注入的类

<bean id="personDao" class="com.dwt1220.PersonDaoBean" />
在注入类的<bean>标签体中使用<constructor-arg>标签

<bean id="personService" class="com.dwt1220.PersonServiceBean">
		<!--index=构造方法的参数位置(下标从0开始), type=注入的类型,ref=注入类在beans.xml中注册的ID -->
		<constructor-arg index="0" type="com.dwt1220.PersonDao" ref="personDao"/>
		<constructor-arg index="1" value="dwt1220" />
	</bean>
PersonServiceBean.java类

package com.dwt1220;

public class PersonServiceBean implements PersonService {
	private PersonDao personDao;
	private String string;
	
	public PersonServiceBean(PersonDao personDao, String string) {
		this.personDao = personDao;
		this.string = string;
	}

	public PersonDao getPersonDao() {
		return personDao;
	}

	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}

	public void save() {
		System.out.println("这是save方法");
		System.out.println(string);
	}
}
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
	<bean id="personDao" class="com.dwt1220.PersonDaoBean" />
	<bean id="personService" class="com.dwt1220.PersonServiceBean">
		<!--index=构造方法的参数位置(下标从0开始), type=注入的类型,ref=注入类在beans.xml中注册的ID -->
		<constructor-arg index="0" type="com.dwt1220.PersonDao" ref="personDao"/>
		<constructor-arg index="1" value="dwt1220" />
	</bean>
</beans>
Test.java

package com.dwt1220;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	public static void main(String[] args) {
		AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
		PersonService personService = (PersonService) ctx.getBean("personService");
		personService.save();
		ctx.close();
	}
}

动态注入,让一个对象的创建不用new了,可以自动的生产,这其实就是利用java里的反射  
反射其实就是在运行时动态的去创建、调用对象,Spring就是在运行时,跟xml Spring的配置  

文件来动态的创建对象,和调用对象里的方法的 。  


Spring它是一个开源的项目,而且目前非常活跃;它基于IoC(Inversion of Control,反向控制)和AOP的构架多层j2ee系统的框架,但它不强迫你必须在每一层 中必须使用Spring,因为它模块化的很好,允许你根据自己的需要选择使用它的某一个模块;它实现了很优雅的MVC,对不同的数据访问技术提供了统一的 接口,采用IoC使得可以很容易的实现bean的装配,提供了简洁的AOP并据此实现Transcation Managment,等等


优点  
a. Spring能有效地组织你的中间层对象,不管你是否选择使用了EJB。如果你仅仅使用了Struts或其他为J2EE的 API特制的framework,Spring致力于解决剩下的问题。  
b. Spring能消除在许多工程中常见的对Singleton的过多使用。根据我的经验,这是一个很大的问题,它降低了系统的可测试性和面向对象的程度。  
c. 通过一种在不同应用程序和项目间一致的方法来处理配置文件,Spring能消除各种各样自定义格式的属性文件的需要。曾经对某个类要寻找的是哪个魔法般的属性项或系统属性感到不解,为此不得不去读Javadoc甚至源编码?有了Spring,你仅仅需要看看类的JavaBean属性。Inversion of Control的使用(在下面讨论)帮助完成了这种简化。  
d. 通过把对接口编程而不是对类编程的代价几乎减少到没有,Spring能够促进养成好的编程习惯。  
e. Spring被设计为让使用它创建的应用尽可能少的依赖于他的APIs。在Spring应用中的大多数业务对象没有依赖于Spring。  
f. 使用Spring构建的应用程序易于单元测试。  
g. Spring能使EJB的使用成为一个实现选择,而不是应用架构的必然选择。你能选择用POJOs或local EJBs来实现业务接口,却不会影响调用代码。  
h. Spring帮助你解决许多问题而无需使用EJB。Spring能提供一种EJB的替换物,它们适用于许多web应用。例如,Spring能使用AOP提供声明性事务管理而不通过EJB容器,如果你仅仅需要与单个数据库打交道,甚至不需要一个JTA实现。  
i. Spring为数据存取提供了一个一致的框架,不论是使用的是JDBC还是O/R mapping产品(如Hibernate)。  
Spring确实使你能通过最简单可行的解决办法来解决你的问题。而这是有有很大价值的。


使用IOC框架应该注意什么
使用IOC框架产品能够给我们的开发过程带来很大的好处,但是也要充分认识引入IOC框架的缺点,做到心中有数,杜绝滥用框架。
第一、软件系统中由于引入了第三方IOC容器,生成对象的步骤变得有些复杂,本来是两者之间的事情,又凭空多出一道手续,所以,我们在刚开始使用IOC框架的时候,会感觉系统变得不太直观。所以,引入了一个全新的框架,就会增加团队成员学习和认识的培训成本,并且在以后的运行维护中,还得让新加入者具备同样的知识体系。
第二、由于IOC容器生成对象是通过反射方式,在运行效率上有一定的损耗。如果你要追求运行效率的话,就必须对此进行权衡。
第三、具体到IOC框架产品(比如:Spring)来讲,需要进行大量的配制工作,比较繁琐,对于一些小的项目而言,客观上也可能加大一些工作成本。
第四、IOC框架产品本身的成熟度需要进行评估,如果引入一个不成熟的IOC框架产品,那么会影响到整个项目,所以这也是一个隐性的风险。
我们大体可以得出这样的结论:一些工作量不大的项目或者产品,不太适合使用IOC框架产品。另外,如果团队成员的知识能力欠缺,对于IOC框架产品缺乏深入的理解,也不要贸然引入。最后,特别强调运行效率的项目或者产品,也不太适合引入IOC框架产品,象WEB2.0网站就是这种情况。
展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部