内部最核心的就是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网站就是这种情况。