代码改变世界

Spring Framework源代码解析之IOC容器(一)

2012-05-21 11:56  飘扬的红领巾  阅读(2781)  评论(0编辑  收藏  举报

    最近开始写博客,觉得这样对自己很有好处,可以从头到尾把散乱的知识梳理一遍,通过自己的理解把它写下了,这个过程受益匪浅。今天写Spring的随笔,使用Spring大概有3年时间,可大多时候只是使用它的特性,并没有深入学习它。Spring的源码据网友说写的很漂亮,我也来学习一下。

IOC之HelloWorld

    假设我们有这样一个业务,根据产品的ID,从产品库中取得该产品的详细信息。用户接口可能是Web浏览器、桌面软件或者超市的条形码扫描器,总之会用一个类来接受用户请求,我们暂且定名为ProductAction.java.

而具体处理逻辑则是放在产品服务接口中,ProductService.java。该接口有一个实现类ProductServiceImpl.java,具体实现写在ProductServiceImpl.java中。代码如下:

ProductService.java

public interface ProductService {
	/**
	 * @Title: getProduct
	 * @Description: 根据ID获取产品信息
	 * @param id
	 * @return Product
	 * 
	 * @throws
	 */
	Product getProduct(String id);
}

ProductServiceImpl.java

@Service("productService")
public class ProductServiceImpl implements ProductService {

	/* (non-Javadoc)
	 * @see com.cnblogs.leefreeman.domain.service.ProductService#getProduct(java.lang.String)
	 */
	@Override
	public Product getProduct(String id) {
		// TODO 此处编写取得产品详细信息的逻辑
		return null;
	}

}

Product.java

public class Product {
	/**
	 * id
	 */
	private String id;
	/**
	 * 名称
	 */
	private String name;
	/**
	 * 生产日期
	 */
	private Date productionDate;
	/**
	 * 保质期
	 */
	private Date shelfLife;

	/**
	 * @return the id
	 */
	public String getId() {
		return id;
	}

	/**
	 * @param id
	 *            the id to set
	 */
	public void setId(String id) {
		this.id = id;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the productionDate
	 */
	public Date getProductionDate() {
		return productionDate;
	}

	/**
	 * @param productionDate
	 *            the productionDate to set
	 */
	public void setProductionDate(Date productionDate) {
		this.productionDate = productionDate;
	}

	/**
	 * @return the shelfLife
	 */
	public Date getShelfLife() {
		return shelfLife;
	}

	/**
	 * @param shelfLife
	 *            the shelfLife to set
	 */
	public void setShelfLife(Date shelfLife) {
		this.shelfLife = shelfLife;
	}
}

ProductAction. java

public class ProductAction {
	/**
	 * 注入ProductServiceImpl.java
	 */
	@Resource(name = "productService")
	private ProductService productService;

	public Product getProductById(String id) {
		Product product = productService.getProduct(id);
		return product;
	}
}

Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<?XML:NAMESPACE PREFIX = [default] http://www.springframework.org/schema/beans NS = "http://www.springframework.org/schema/beans" /><?XML:NAMESPACE PREFIX = [default] http://www.springframework.org/schema/beans NS = "http://www.springframework.org/schema/beans" />
	<?xml:namespace prefix = context />
	
	

这样就可以实现往ProductAction类中注入指定的ProductService,而具体业务的交给了ProductServiceImpl处理。

何为IOC?

    从上例可以看出,通过Spring的IOC特性,将ProductService具体的实现类注入到ProductAction中,从而ProductAction可以调用ProductService的方法进行逻辑处理。在没有使用Spring的情况下,要完成以上功能,也可以通过new关键字,将ProductSerivce接口的实现类实例化出来。但他们的区别在哪里呢?当使用new关键字实例化对象时,ProductAction类对ProductService实现类的实例化具有控制权;而使用Spring IOC时,ProductAction和ProductService之间的关系则通过Spring来配置(通过注解或者XML配置文件),而对ProductService的控制权由ProductAction转移到了Spring IOC容器上,这就是所谓的控制反转。IOC特性的直接用处就是解耦,在ProductAction中,我们并没有看到对ProductService实现类的依赖,而它们之间的依赖关系则是通过第三方(Spring IOC)来维护。

IOC工作流程

Step1:系统运行时,Spring开始启动,指定将加载的配置文件。

应用程序:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");

Web应用:

web.xml


       contextConfigLocation
       classpath:spring-hibernate-mysql.xml
    
    
          org.springframework.web.context.ContextLoaderListener
    

Step2:接着AbstractApplicationContext类开始调用prepareRefresh方法,对ApplicationContext进行刷新。

Step3:XmlBeanDefinitionReader类调用loadBeanDefinitions方法进行配置文件加载,配置文件中定义的Java Bean会被加载在IOC容器的一个HashMap当中。HashMap的key就是Bean的id,HasMap的value就是这个Java Bean。(当中详细过程分为定位、载入、注册,我们后面再详细说明)

Step4:依赖注入,当用户向IOC容器索要Bean时,将触发BeanFactory中的getBean方法,当然不同类型的配置会调用不同的BeanFactory,如XmlBeanFactory。从而得到相应的JavaBean。

小结

    上面简要介绍了Spring IOC的特性,对IOC的理解,IOC容器的大致工作流程,下文将依据上述流程详细解析IOC容器的实现。