Spring学习之IOC

一、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一般用多例模式


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。