面向过程手写 Spring IoC

Spring 框架的意义

Spring 框架是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,它的核心是控制反转和依赖注入。

控制反转(IoC):将对象的创建和对象之间的依赖关系交给 Spring 容器管理,降低了组件之间的耦合度。

举个例子看看如果没有 IoC

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
/**
* 使用者根本不想关心UserServiceImpl对象是怎么new的,他只是想测试查询功能。
* IoC要做的事情,就是让使用对象的同学只需要找工厂去要对应的对象即可,不需要自己创建。
* IoC是将创建对象的权利,由程序员这边,反转给spring容器去创建
*/
@Test
public void test(){
// 需要创建userService对象, 还需要创建userDao对象(依赖注入userservice), 还需要创建dataSource对象(依赖注入userDao)

UserServiceImpl userService = new UserServiceImpl();

UserDaoImpl userDao = new UserDaoImpl();

BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://192.168.56.101:3306/test?characterEncoding=utf8");
dataSource.setUsername("root");
dataSource.setPassword("123456");

userDao.setDataSource(dataSource);
userService.setUserDao(userDao);

Map<String,Object> map = new HashMap<>();
map.put("username","李四");
List<User> users = userService.queryUsers(map);
System.out.println(users);
}

如何实现IoC

  1. 使用配置文件(XML)解决扩展性问题,比如要创建的对象,配置对应的信息即可(类的全路径、类的属性信息)
    a)类的全路径信息(要创建一个对象,就要对应一个信息)

    b)配置类的属性信息
  2. 加载XML配置信息
  3. 创建对应的对象,将创建的对象放入map集合中(key: 唯一标识,value: 对象)

下面来手写一个简单的 Spring 框架,实现 IoC 和 AOP 的功能。

前置条件

存储bean的配置信息,使用 BeanDefinition 类来存储。
存储bean的实例,使用 singletonObjects 来存储。

1
2
3
4
5
6
7
8
// 存储bean的定义信息的集合
private Map<String, BeanDefinition> beanDefinitions = new HashMap<>();

/**
* 一级缓存
* @see org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.singletonObjects
*/
private Map<String, Object> singletonObjects = new HashMap<>();
BeanDefinition 类代码
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
79
80
81
82
83
84
85
86
87
88
89
90
91
public class BeanDefinition {
// bean标签的class属性
private String clazzName;
// bean标签的class属性对应的Class对象
private Class<?> clazzType;
// bean标签的id属性和name属性都会存储到该属性值,id和name属性是二选一使用的
private String beanName;
// 初始化方法名称
private String initMethod;
// 该信息是默认的配置,如果不配置就默认是singleton
private String scope;
/**
* bean中的属性信息
*/
private List<PropertyValue> propertyValues = new ArrayList<>();

private static final String SCOPE_SINGLETON = "singleton";
private static final String SCOPE_PROTOTYPE = "prototype";

public BeanDefinition(String clazzName, String beanName) {
this.beanName = beanName;
this.clazzName = clazzName;
this.clazzType = resolveClassName(clazzName);
}

private Class<?> resolveClassName(String clazzName) {
try {
return Class.forName(clazzName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}

public String getBeanName() {
return beanName;
}

public void setBeanName(String beanName) {
this.beanName = beanName;
}

public String getInitMethod() {
return initMethod;
}

public void setInitMethod(String initMethod) {
this.initMethod = initMethod;
}

public List<PropertyValue> getPropertyValues() {
return propertyValues;
}

public void addPropertyValue(PropertyValue propertyValue) {
this.propertyValues.add(propertyValue);
}

public String getClazzName() {
return clazzName;
}

public void setClazzName(String clazzName) {
this.clazzName = clazzName;
}

public String getScope() {
return scope;
}

public void setScope(String scope) {
this.scope = scope;
}

public void setPropertyValues(List<PropertyValue> propertyValues) {
this.propertyValues = propertyValues;
}

public Class<?> getClazzType() {
return clazzType;
}

public boolean isSingleton() {
return SCOPE_SINGLETON.equals(this.scope);
}

public boolean isPrototype() {
return SCOPE_PROTOTYPE.equals(this.scope);
}

}
PropertyValue 类代码
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
public class PropertyValue {

private String name;

private Object value;

public PropertyValue(String name, Object value) {
super();
this.name = name;
this.value = value;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Object getValue() {
return value;
}

public void setValue(Object value) {
this.value = value;
}
}
RuntimeBeanReference 类代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 封装<bean>标签中子标签<property>的ref属性值
*/
public class RuntimeBeanReference {

// ref的属性值
private String ref;

public String getRef() {
return ref;
}

public void setRef(String ref) {
this.ref = ref;
}

public RuntimeBeanReference(String ref) {
super();
this.ref = ref;
}
}
TypedStringValue 类代码
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
/**
* 封装<bean>标签中子标签<property>的value属性值
*/
public class TypedStringValue {

// value属性值
private String value;

// value属性值对应的真正类型(Bean中属性的类型)
private Class<?> targetType;

public TypedStringValue(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public Class<?> getTargetType() {
return targetType;
}

public void setTargetType(Class<?> targetType) {
this.targetType = targetType;
}

}

1. 加载配置文件

配置文件 applicationContext.xml 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<beans>
<bean id="userService"
class="com.ithawk.demo.spring.custom.demo.service.UserServiceImpl">
<!-- 引用类型 -->
<property name="userDao" ref="userDao"></property>
</bean>

<!-- 该类有一个初始化方法 -->
<bean id="userDao" class="com.ithawk.demo.spring.custom.demo.dao.UserDaoImpl"
init-method="init">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 该类有一个初始化方法 -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" scope="singleton" >
<property name="driverClassName"
value="com.mysql.jdbc.Driver"></property>
<property name="url"
value="jdbc:mysql://192.168.56.101:3306/test?characterEncoding=utf-8"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
</beans>

加载配置文件的代码如下:
主要实现了解析 XML 文件,将解析的结果放入 beanDefinitions 中。

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
@Before
public void before() {
//加载XML配置信息,放入Map集合
// TODO XML解析
//完成XML解析,其实就是完成BeanDefinition的注册
// XML解析,解析的结果,放入beanDefinitions中
String location = "applicationContext.xml";

// 获取流对象
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(location);

// 创建文档对象
Document document = null;
try {
SAXReader reader = new SAXReader();
document = reader.read(inputStream);
} catch (DocumentException e) {
e.printStackTrace();
}

// 按照spring定义的标签语义去解析Document文档
List<Element> elements = document.getRootElement().elements();
for (Element element : elements) {
// 获取标签名称
String name = element.getName();
if (name.equals("bean")) {
// 解析默认标签,其实也就是bean标签
parseDefaultElement(element);
} else {
// 解析自定义标签,比如aop:aspect标签
parseCustomElement(element);
}
}
}
parseDefaultElement(element) 代码
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/**
* 读取标签
* @param beanElement
*/
@SuppressWarnings("unchecked")
private void parseDefaultElement(Element beanElement) {
try {
if (beanElement == null) {
return;
}
// 获取id属性
String id = beanElement.attributeValue("id");

// 获取name属性
String name = beanElement.attributeValue("name");

// 获取class属性
String clazzName = beanElement.attributeValue("class");
if (clazzName == null || "".equals(clazzName)) {
return;
}

// 获取init-method属性
String initMethod = beanElement.attributeValue("init-method");
// 获取scope属性
String scope = beanElement.attributeValue("scope");
scope = scope != null && !scope.equals("") ? scope : "singleton";

// 获取beanName
String beanName = id == null ? name : id;
Class<?> clazzType = Class.forName(clazzName);
beanName = beanName == null ? clazzType.getSimpleName() : beanName;
// 创建BeanDefinition对象
// 此次可以使用构建者模式进行优化
BeanDefinition beanDefinition = new BeanDefinition(clazzName, beanName);
beanDefinition.setInitMethod(initMethod);
beanDefinition.setScope(scope);
// 获取property子标签集合
List<Element> propertyElements = beanElement.elements();
for (Element propertyElement : propertyElements) {
parsePropertyElement(beanDefinition, propertyElement);
}

// 注册BeanDefinition信息
this.beanDefinitions.put(beanName, beanDefinition);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

private void parsePropertyElement(BeanDefinition beanDefination, Element propertyElement) {
if (propertyElement == null)
return;

// 获取name属性
String name = propertyElement.attributeValue("name");
// 获取value属性
String value = propertyElement.attributeValue("value");
// 获取ref属性
String ref = propertyElement.attributeValue("ref");

// 如果value和ref都有值,则返回
if (value != null && !value.equals("") && ref != null && !ref.equals("")) {
return;
}

/**
* PropertyValue就封装着一个property标签的信息
*/
PropertyValue pv = null;

if (value != null && !value.equals("")) {
// 因为spring配置文件中的value是String类型,而对象中的属性值是各种各样的,所以需要存储类型
TypedStringValue typeStringValue = new TypedStringValue(value);

Class<?> targetType = getTypeByFieldName(beanDefination.getClazzName(), name);
typeStringValue.setTargetType(targetType);

pv = new PropertyValue(name, typeStringValue);
beanDefination.addPropertyValue(pv);
} else if (ref != null && !ref.equals("")) {

RuntimeBeanReference reference = new RuntimeBeanReference(ref);
pv = new PropertyValue(name, reference);
beanDefination.addPropertyValue(pv);
} else {
return;
}
}

private Class<?> getTypeByFieldName(String beanClassName, String name) {
try {
Class<?> clazz = Class.forName(beanClassName);
Field field = clazz.getDeclaredField(name);
return field.getType();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

2. Bean 对象创建

2.1 创建Bean对象

实现Bean的实例化、属性填充(依赖注入)、初始化操作。

1
2
3
4
5
6
7
8
9
10
private Object createBean(BeanDefinition bd) throws Exception {
// 1 Bean的实例化(new)
Object bean = createBeanInstance(bd);
// 2 Bean的属性填充(依赖注入)
populateBean(bean, bd);
// 3 Bean的初始化(调用初始化方法,完成初始化操作)
//进行初始化操作
initializeBean(bean, bd);
return bean;
}

2.1.1 Bean的实例化

通过反射创建Bean的实例。

1
2
3
4
5
6
7
8
9
10
private Object createBeanInstance(BeanDefinition bd) throws Exception {
// TODO 1、通过静态工厂方法去创建Bean的实例
// TODO 2、通过对象工厂去创建Bean的实例
// 3、通过构造方法去创建Bean的实例
Class<?> clazzType = bd.getClazzType();
// 获取默认的无参构造
Constructor<?> constructor = clazzType.getDeclaredConstructor();
Object bean = constructor.newInstance();
return bean;
}

2.1.2 Bean的属性填充(依赖注入)

通过反射设置Bean的属性值。

1
2
3
4
5
6
7
8
9
10
11
12
13
private void populateBean(Object bean, BeanDefinition bd) throws Exception {
List<PropertyValue> propertyValues = bd.getPropertyValues();
for (PropertyValue pv : propertyValues) {
String name = pv.getName();
Object value = pv.getValue();

// 处理之后的value值 获取依赖的值
Object valueToUse = resolveValue(value);

//反射设置属性值
setProperty(bean, name, valueToUse, bd);
}
}

通过反射获得属性值。

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
private Object resolveValue(Object value) throws Exception {
Object valueToUse = null;
if (value instanceof TypedStringValue) {
TypedStringValue typedStringValue = (TypedStringValue) value;
String stringValue = typedStringValue.getValue();
Class<?> targetType = typedStringValue.getTargetType();

valueToUse = handleType(stringValue, targetType);
} else if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference beanReference = (RuntimeBeanReference) value;
String ref = beanReference.getRef();
// 此处有可能会发生循环依赖问题
valueToUse = getBean(ref);
}
return valueToUse;
}

private Object handleType(String stringValue, Class<?> targetType) {
if (targetType == Integer.class) {
return Integer.parseInt(stringValue);
} else if (targetType == String.class) {
return stringValue;
}// ......
return null;
}

通过反射设置属性值。

1
2
3
4
5
6
private void setProperty(Object bean, String name, Object valueToUse, BeanDefinition bd) throws Exception {
Class<?> clazzType = bd.getClazzType();
Field field = clazzType.getDeclaredField(name);
field.setAccessible(true);
field.set(bean, valueToUse);
}

2.1.3 Bean的初始化

通过反射调用初始化方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void initializeBean(Object bean, BeanDefinition bd) throws Exception {

// TODO 处理Aware接口
// TODO 处理InitializingBean接口的初始化操作

// 处理init-method标签属性对应的初始化方法
invokeInitMethod(bean, bd);

}

private void invokeInitMethod(Object bean, BeanDefinition bd) throws Exception {
String initMethod = bd.getInitMethod();
if (initMethod == null) {
return;
}
Class<?> clazzType = bd.getClazzType();
Method method = clazzType.getDeclaredMethod(initMethod);

// 完成了初始化方法的调用
method.invoke(bean);
}

2.2 获取Bean对象

根据Bean的名称获取Bean对象。

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
private Object getBean(String beanName) throws Exception {

// 1、先从单例Bean的map集合中查找
Object singletonObject = this.singletonObjects.get(beanName);

// 2、如果存在,则直接返回该bean
if (singletonObject != null) {
return singletonObject;
}

// 3、不存在,则从Bean的定义信息集合中查找对应的BeanDefinition
BeanDefinition bd = this.beanDefinitions.get(beanName);
if (bd == null) {
return null;
}

// 4、创建Bean
if (bd.isSingleton()) {
singletonObject = createBean(bd);
// 6、创建完Bean,加入单例Bean的集合
this.singletonObjects.put(beanName, singletonObject);
} else if (bd.isPrototype()) {
singletonObject = createBean(bd);
}

return singletonObject;
}

通过以上代码,我们实现了 Bean 对象的创建。