一、IoC(Inversion of Control,控制反转)
我们在开发java程序中会有这样的体会,java程序中的每个业务逻辑至少需要两个或以上的对象来协作完成,通常,每个对象在使用他的合作对象时,自己均要使用像new object()这样的语法来完成合作对象的申请工作。你会发现:对象间的耦合度高了。而IOC的思想是:Spring容器来实现这些相互依赖对象的创建、协调工作。对象只需要关系业务逻辑本身就可以了。从这方面来说,对象如何得到他的协作对象的责任被反转了(IOC、DI)。DI其实就是IOC的另外一种说法。
如果对这一核心概念还不理解:这里引用一个叫Bromon的blog上找到的浅显易懂的答案:
首先想说说IoC(Inversion of Control,控制倒转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。如果你还不明白的话,我决定放弃。
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。关于反射的相关资料请查阅java doc。
二、基于XML文件配置的注入
这里首先构造一个工程,工程的目录大概是这样的:
这个简单的工程分为DAO层、Service层、Model层,类名带字母"I"的表示是接口,各层之间的调用关系是这样的:
通过XML配置文件的注入方式的步骤是这样的:
1.导入spring相应的jar包
2.在src目录下创建相应的beans.xml
3.为beans.xml添加相应的schema
<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.xsd">
4.新建一个类
5.在bean.xml中创建对象
<!-- 如下配置相当于完成了:HelloWorld helloWorld = new HelloWorld() -->
<bean id="helloWorld" class="com.hh.spring.model.HelloWorld"></bean>
6.在测试类中使用这个对象
6.1 创建Spring工厂
BeanFactory factory = new ClassPathXmlApplicationContext("beans.xml");
6.2 通过Spring工厂获取相应的对象
HelloWorld helloWorld = factory.getBean("helloWorld", HelloWorld.class);
model层中的User.java文件很简单,只有id和username两个属性,代码如下:
public class User {
private int id;
private String username;
//省略setter和getter方法
}
DAO层中的IUserDao接口定义了三个方法,分别是add,delete,load方法,UserDao实现了IUserDao接口:
public class UserDao implements IUserDao {
@Override
public void add(User user) {
System.out.println("add:"+user);
}
@Override
public void delete(int id) {
System.out.println("delete:"+id);
}
@Override
public User load(int id) {
System.out.println("load:"+id);
return null;
}
}
Service层的IUserService接口中定义了三个方法,这三个方法分别调用了IUserDao中的三个方法,所以在UserService实现中,依赖于IUserDao这个类:
public class UserService implements IUserService {
private IUserDao userDao;
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
@Override
public void add(User user) {
userDao.add(user);
}
@Override
public void delete(int id) {
userDao.delete(id);
}
@Override
public User load(int id) {
return userDao.load(id);
}
}
最后是UserAction层,这一层调用UserService中的方法实现相应的功能。请注意:基于XML配置文件的注入方式一定要写依赖对象的setter方法。
public class UserAction {
private User user;
private IUserService userService;
private int id;
private List<String> names;
//省略setter和getter方法
}
最后是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.xsd">
<!-- 如下配置相当于完成了:HelloWorld helloWorld = new HelloWorld() -->
<bean id="helloWorld" class="com.hh.spring.model.HelloWorld"></bean>
<bean id="userDao" class="com.hh.spring.dao.UserDao"></bean>
<!-- 创建一个User对象,并注入属性值 -->
<bean id="user" class="com.hh.spring.model.User">
<property name="id" value="2"></property>
<property name="username" value="悟能"></property>
</bean>
<!-- autowire="byType" 表示根据类型注入,如果一个类中有两个同类型的对象就会抛出异常,开发中一般都是用byName
byName表示name完成注入。虽然自动注入可以减少配置,但是通过beans.xml文件无法很好的了解整个类的结构,所以不建议使用autowire -->
<bean id="userService" class="com.hh.spring.service.UserService" autowire="byName">
<!-- name中的值会在userService对象中调用setXXX方法来注入
ref="userDao" 表示是配置文件中的bean中所创建的DAO的id
-->
<!-- <property name="userDao" ref="userDao"></property> -->
</bean>
<!-- 对于userAction而言,里面的属性的值的状态会根据不同的线程得到不同的值,所以应该使用多例 -->
<bean id="userAction" class="com.hh.spring.action.UserAction" scope="prototype">
<property name="userService" ref="userService"></property>
<property name="user" ref="user"></property>
<property name="id" value="11"></property>
<!-- 注入List -->
<property name="names" >
<list>
<value>aaa</value>
<value>bbb</value>
</list>
</property>
</bean>
<!-- 以下是使用构造函数注入,不常用,一般使用set方法注入,即property -->
<!-- <bean id="userAction" class="com.hh.spring.action.UserAction" scope="prototype">
<constructor-arg ref="userService"></constructor-arg>
</bean> -->
</beans>
三、基于Annotation的注入
基于Annotation的注入方式可以大大减少XML配置文件中的内容,在使用Annotation注入方式之前,需要在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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 打开spring的Annotation支持 -->
<context:annotation-config/>
<!-- 设定spring去哪些包中找Annotation -->
<context:component-scan base-package="com.hh.spring"/>
</beans>
然后在各个类中(是在接口的实现类中加入注解,而非接口)加入注解:
scope表示的bean的创建模式,当类的状态不会被修改的时候,用单例,如果有属性,属性并且会变化,则用多例。Action一般用多例模式
参考文章:http://blog.csdn.net/it_man/article/details/4402245