Spring Framework源代码解析之IOC容器(一)
2012-05-21 11:56 飘扬的红领巾 阅读(2791) 评论(0) 编辑 收藏 举报序
最近开始写博客,觉得这样对自己很有好处,可以从头到尾把散乱的知识梳理一遍,通过自己的理解把它写下了,这个过程受益匪浅。今天写Spring的随笔,使用Spring大概有3年时间,可大多时候只是使用它的特性,并没有深入学习它。Spring的源码据网友说写的很漂亮,我也来学习一下。
IOC之HelloWorld
假设我们有这样一个业务,根据产品的ID,从产品库中取得该产品的详细信息。用户接口可能是Web浏览器、桌面软件或者超市的条形码扫描器,总之会用一个类来接受用户请求,我们暂且定名为ProductAction.java.
而具体处理逻辑则是放在产品服务接口中,ProductService.java。该接口有一个实现类ProductServiceImpl.java,具体实现写在ProductServiceImpl.java中。代码如下:
ProductService.java
1 2 3 4 5 6 7 8 9 10 11 | public interface ProductService { /** * @Title: getProduct * @Description: 根据ID获取产品信息 * @param id * @return Product * * @throws */ Product getProduct(String id); } |
ProductServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 | @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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 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
1 2 3 4 5 6 7 8 9 10 11 12 | public class ProductAction { /** * 注入ProductServiceImpl.java */ @Resource (name = "productService" ) private ProductService productService; public Product getProductById(String id) { Product product = productService.getProduct(id); return product; } } |
Spring配置文件
1 2 3 4 5 6 7 8 9 10 11 | <? 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" />< beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <? xml:namespace prefix = context />< context:annotation-config ></ context:annotation-config > < context:component-scan base-package="com.cnblogs.leefreeman.action"></ context:component-scan > < context:component-scan base-package="com.cnblogs.leefreeman.domain.service.impl"></ context:component-scan > </ beans > |
这样就可以实现往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开始启动,指定将加载的配置文件。
应用程序:
1 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "application-context.xml" ); |
Web应用:
web.xml
1 2 3 4 5 6 7 | < context-param > < param-name >contextConfigLocation</ param-name > < param-value >classpath:spring-hibernate-mysql.xml</ param-value > </ context-param > < listener > < listener-class >org.springframework.web.context.ContextLoaderListener</ listener-class > </ listener > |
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容器的实现。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?