1.写在前面
由于上次看spring的源码的时候已经是半年前了,笔者对spring的源码只记得一个大概了,于是我打算重新看一次spring的源码,看spring的源码之前最好的方式就是模拟一遍spring的,但是奈何我的水平有限,于是就去网上找现成的例子,于是笔者找到了GitHub上的一个项目《tiny-spring》,本系列的博客主要是参考这个开源项目来写的,加上自己的一些理解。
2.step1最基本的容器。
看过我的spring源码的系列的专题,都知道spring的Bean的描述信息都是通过一个类BeanDefinition接口来描述,于是我这儿也创建一个BeanDefinition类。具体的代码如下:
package com.ys.tinyioc;
public class BeanDefinition {
private Object bean;
public BeanDefinition(Object bean){
this.bean = bean;
}
public Object getBean() {
return bean;
}
}
我们都知道Bean是通过Bean工厂创建的,同时所有BeanDefinition存在spring容器的一个Map中,于是我就有了对应的思路,创建对应BeanFactory类,具体的代码如下:
package com.ys.tinyioc;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BeanFactory {
//存放所有的BeanDefinition
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
//获取Bean,就是在Map中通过name获取这个BeanDefinition,然后获取这个Bean
public Object getBean(String name) {
return beanDefinitionMap.get(name).getBean();
}
//注册BeanDefinition就是往Map中存放一个键值对
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
beanDefinitionMap.put(name, beanDefinition);
}
}
这样我们一个最简单的容器就构建完成。我们再来写一个BeanDefinition,然后把它添加到容器中,测试一下,具体的代码如下:
package com.ys.tinyioc;
public class HelloWorldService {
public void helloWorld(){
System.out.println("Hello World");
}
}
测试类如下:
package com.ys.tinyioc;
import org.junit.Test;
public class BeanFactoryTest {
@Test
public void test() {
//1.初始化BeanFactory
BeanFactory beanFactory = new BeanFactory();
//2.注入Bean
BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService());
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
//3.获取Bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
}
}
运行结果如下:
通过我们的运行结果,我们可以得出我们书写的代码是对的,一个最基本的容器就构建完成。可以你发现这个Bean是我们创建好的放到Bean工厂的,那么Bean工厂就失去它的职责了,创建Bean,实际使用中,我们希望容器来管理bean的创建。于是我们将bean的初始化放入BeanFactory中。为了保证扩展性,我们使用Extract Interface的方法,将BeanFactory
替换成接口,而使用AbstractBeanFactory
和AutowireCapableBeanFactory
作为其实现。
3.step2将bean创建放入工厂
我们先要修改BeanDefinition这个类,要添加几个属性,让其能够通过反射来创建对应的Bean,于是修改后代码如下:
package com.ys.ioc;
public class BeanDefinition {
private Object bean;
private Class beanClass;
private String beanClassName;
public BeanDefinition() {
}
public Object getBean() {
return bean;
}
public void setBean(Object bean) {
this.bean = bean;
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
public String getBeanClassName() {
return beanClassName;
}
//设置BeanClassName的同时,将beanClass的属性设置上
public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
try {
this.beanClass = Class.forName(beanClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
修改完BeanDefinition类后,我们需要修改BeanFactory类,将这个类改成接口,抽象出两个方法(获取Bean,注册BeanDefinition),具体的代码如下:
package com.ys.ioc.factory;
import com.ys.ioc.BeanDefinition;
public interface BeanFactory {
Object getBean(String name);
void registerBeanDefinition(String name, BeanDefinition beanDefinition);
}
既然这两个方法是两个最基本的方法,而其他的工厂有这两个方法,同时可能有其他的方法,于是我这儿将这两个方法通过一个抽象类来实现,其他的工厂如果要扩展只需要继承这个抽象工厂类,具体的如下:
package com.ys.ioc.factory;
import com.ys.ioc.BeanDefinition;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public abstract class AbstractBeanFactory implements BeanFactory {
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
@Override
public Object getBean(String name) {
return beanDefinitionMap.get(name).getBean();
}
@Override
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
Object bean = doCreateBean(beanDefinition);
beanDefinition.setBean(bean);
beanDefinitionMap.put(name, beanDefinition);
}
//初始化Bean
protected abstract Object doCreateBean(BeanDefinition beanDefinition);
}
和原来的差不多,只不过将Bean的创建交给了BeanFactory,同时抽象出来一个创建Bean的方法,由对应的子类去实现,于是有了下面的实现。
package com.ys.ioc.factory;
import com.ys.ioc.BeanDefinition;
public class AutowireCapableBeanFactory extends AbstractBeanFactory{
@Override
protected Object doCreateBean(BeanDefinition beanDefinition) {
try {
Object bean = beanDefinition.getBeanClass().newInstance();
return bean;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
其实就是通过反射的方式来创建对应的Bean,我们再来测试一下,写出如下的Bean,具体的代码如下:
package com.ys.ioc;
public class HelloWorldService {
public void helloWorld(){
System.out.println("hello World");
}
}
测试类的代码如下:
package com.ys.ioc;
import com.ys.ioc.factory.AutowireCapableBeanFactory;
import com.ys.ioc.factory.BeanFactory;
import org.junit.Test;
public class BeanFactoryTest {
@Test
public void test(){
//1.初始化beanFactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();
//2.注入bean
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("com.ys.ioc.HelloWorldService");
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
//3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
}
}
运行结果如下:
可以发现我们的运行的结果没有问题,但是我们创建的Bean没有任何属性,于是有了第三步的属性的注入。
3.step3为Bean注入属性
既然有属性注入,那我们就要清楚这些属性通过什么来存,于是我打算创建一个类PropertyValue用来Bean的属性,具体的代码如下:
package com.ys.ioc;
public class PropertyValue {
private String name;
private Object value;
public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
但是一个Bean的属性不止一个,所有我们这儿也需要一个类PropertyValues来存Bean中的属性,可能有人会说为什么不直接用List来存呢?因为这儿我们可能对PropertyValue进行一些操作,所以用PropertyValues来存,具体的代码如下:
package com.ys.ioc;
import java.util.ArrayList;
import java.util.List;
// 包装一个对象所有的PropertyValue。<br/>
// 为什么封装而不是直接用List?因为可以封装一些操作。
public class PropertyValues {
private final List<PropertyValue> propertyValueList = new ArrayList<>();
public void addPropertyValue(PropertyValue pv) {
this.propertyValueList.add(pv);
}
public List<PropertyValue> getPropertyValues() {
return this.propertyValueList;
}
}
我这儿没有对PropertyValue进行了一些操作,主要是为了简单,如果你们想对PropertyValue属性进行操作,只需要在addPropertyValue方法中进行一些操作。既然封装了Bean属性的类,那么这儿BeanDefinition的代码也需要修改了,具体的代码如下:
package com.ys.ioc;
//bean的内容及元数据,保存在BeanFactory中,包装bean的实体
public class BeanDefinition {
private Object bean;
private Class beanClass;
private String beanClassName;
private PropertyValues propertyValues;
public BeanDefinition() {
}
public Object getBean() {
return bean;
}
public void setBean(Object bean) {
this.bean = bean;
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
public String getBeanClassName() {
return beanClassName;
}
public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
try {
this.beanClass = Class.forName(beanClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public PropertyValues getPropertyValues() {
return propertyValues;
}
public void setPropertyValues(PropertyValues propertyValues) {
this.propertyValues = propertyValues;
}
}
在原来的基础上添加了PropertyValues(Bean的属性)然后BeanFactory
和AbstractBeanFactory
都没有变化,只需要AutowireCapableBeanFactory类,将Bean的属性注入即可,具体的代码如下:
package com.ys.ioc.factory;
import com.ys.ioc.BeanDefinition;
import com.ys.ioc.PropertyValue;
import java.lang.reflect.Field;
public class AutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
Object bean = createBeanInstance(beanDefinition);
//设置属性
applyPropertyValues(bean, beanDefinition);
return bean;
}
protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
//遍历所有的属性通过反射的方式设置给Bean
for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
declaredField.setAccessible(true);
declaredField.set(bean, propertyValue.getValue());
}
}
protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
return beanDefinition.getBeanClass().newInstance();
}
}
至此代码就写完了,我们来写测试类,具体的代码如下,首先是我们的Bean
package com.ys.ioc;
public class HelloWorldService {
private String text;
public void helloWorld(){
System.out.println(text);
}
public void setText(String text) {
this.text = text;
}
}
然后是我们的测试类,具体代码如下:
package com.ys.ioc;
import com.ys.ioc.factory.AutowireCapableBeanFactory;
import com.ys.ioc.factory.BeanFactory;
import org.junit.Test;
public class BeanFactoryTest {
@Test
public void test() throws Exception {
// 1.初始化BeanFactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();
// 2.Bean定义
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("com.ys.ioc.HelloWorldService");
// 3.设置属性
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("text", "Hello World!"));
beanDefinition.setPropertyValues(propertyValues);
// 4.生成Bean
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
// 5.获取Bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
}
}
运行的结果如下:
可以发现我们的属性是注入进去了。
4.写在最后
今天笔者就简单的模拟spring的IOC的容器,当然spring的IOC容器肯定不止这些,还有读取xml配置来初始化bean,为bean注入bean,以及ApplicationContext,我会在后面的博客介绍,今天就先这样吧。