手把手教你手写简单的IOC框架
前言
就像标题说的一样,我们今天动手来实现一个简单的IOC框架。我们动手实现的目的如下:
- 整体上去了解IOC思想的落地方案架构,为以后阅读Spring家族的源码打基础
- 熟悉工厂模式、建造者模式、模板模式等常用的设计模式在代码中的运用
- 熟悉反射在代码中的使用
- 养成面向接口编程的好习惯
- 熟悉一些经典问题的解决方案:比如循环依赖的问题、硬编码问题等
什么是IOC/DI?
对于“控制反转”和“依赖注入”这两个名词,相信大家已经耳熟能详。但是为了后续内容的衔接,我们在这里重复一下。
其实“控制反转”和“依赖注入”说的是一回事儿,只不过站的角度不同,则使用的词汇不一样。我们站在框架的角度,我们需要把用户定义好的依赖关系维护到我们的容器中,这就叫做“依赖注入”;我们站在用户的角度,我们需要把对象的创建以及初始化的过程交给容器,这就是所谓的“控制反转”。
设计我们的框架
一个良好的编码习惯,对于我们大家的工作异常的重要。所以我们在动手开发之前,一定要对自己做的东西有一个清晰的认识。我们先看一下我们的开发流程图:
由上面的流程图可知,我们首先要定义我们框架支持的XML配置文件的格式,我们定义如下:
<beans>
<bean id="order" class="com.rubin.ioc.context.beans.Order">
<property name="id" value="1"></property>
<property name="orderNo" value="O00001"></property>
<property name="address" ref="address"></property>
</bean>
<bean id="address" class="com.rubin.ioc.context.beans.Address" init-method="init">
<property name="province" value="北京市"></property>
<property name="city" value="北京市"></property>
<property name="region" value="丰台区"></property>
<property name="address" value="东铁匠营街道666号"></property>
</bean>
<bean id="user" class="com.rubin.ioc.context.beans.UserFactoryBean" init-method="init">
<property name="userInfo" value="rubin,27"></property>
</bean>
<component-scan base-package="com.rubin.ioc.context"></component-scan>
</beans>
根据上面的配置文件定义,我们整理出我们框架的UML类图:
框架开发
项目创建
我们首先创建我们的顶级项目rubin-framework,添加子Module rubin-framework-bom,对应pom如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rubin</groupId>
<artifactId>rubin-framework-bom</artifactId>
<version>1.0</version>
<description>项目父模块</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<dom4j.version>1.6.1</dom4j.version>
<xpath.version>1.1.6</xpath.version>
<lombok.version>1.16.18</lombok.version>
<commons-lang3.version>3.5</commons-lang3.version>
<commons-collections.version>3.2.1</commons-collections.version>
<slf4j.version>1.7.32</slf4j.version>
<logback.version>1.2.6</logback.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 单元测试Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!--dom4j依赖-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>${dom4j.version}</version>
</dependency>
<!--xpath表达式依赖-->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>${xpath.version}</version>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!--commons-lang3-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!--commons-collections-->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collections.version}</version>
</dependency>
<!--log-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
创建我们的IOC容器模块rubin-ioc-context,pom如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>rubin-framework-bom</artifactId>
<groupId>com.rubin</groupId>
<version>1.0</version>
<relativePath>../rubin-framework-bom/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>rubin-spring-context</artifactId>
<description>项目容器模块</description>
<dependencies>
<!-- 单元测试Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--dom4j依赖-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
</dependency>
<!--xpath表达式依赖-->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!--commons-lang3-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!--commons-collections-->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<!--log-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>
开发实体配置加载器
我们从实体配置加载器作为切入点,来开发我们的项目。根据我们上面定义的XML配置文件格式,我们抽象出实体配置类如下:
package com.rubin.ioc.context.bean;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* 实体配置类
*/
@Data
@Accessors(chain = true)
public class BeanDefinition implements Serializable {
private static final long serialVersionUID = -5701769260842523813L