简易版的Spring框架之IOC简单实现

一个简易版的Spring框架

功能

  • 支持singleton类型的bean,包括初始化、属性注入、以及依赖bean注入。
  • 可从xml中读取配置。
  • 可使用Aspectj的方式进行AOP编写,支持接口和类代理。

说明

若是你有幸能看到java

  • 一、本文简易Spring框架参考Github,代码注释参考Github.
  • 二、全部权归原做者,在这里本身只是临摹,参考注释还原过程。不懂的能够看做者的视频。
  • 三、你们一块儿努力,一块儿学习,有兴趣的也能够看下个人Github。上传了Spring源码。大佬能够看看。
  • 四、看本文以前但愿你有一份Spring源码。对照着找你想要的接口和类。加深印象。
  • 五、本文只为本身之后复习用,若是不对还请谅解。

tiny-spring是为了学习Spring的而开发的,能够认为是一个Spring的精简版。Spring的代码不少,层次复杂,阅读起来费劲。我尝试从使用功能的角度出发,参考Spring的实现,一步一步构建,最终完成一个精简版的Spring。 有人把程序员与画家作比较,画家有门基本功叫临摹,tiny-spring能够算是一个程序的临摹版本-从本身的需求出发,进行程序设计,同时对著名项目进行参考。node

第一部分:IoC容器

早看到也不浪费时间了,唉。git

一、时序图-普通bean的加载过程程序员

二、加载资源的主要相关类github

三、从xml中加载bean定义的主要相关类spring

四、装配bean的主要相关类编程

五、实现ApplicationContext接口的相关类app

1.step1-最基本的容器

IoC最基本的角色有两个:容器(BeanFactory)和Bean自己。这里使用BeanDefinition来封装了bean对象,这样能够保存一些额外的元信息。测试代码:框架

// 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();

复制代码

一、首先咱们来看下总体代码结构,这里的要点是BeanFactory接口。咱们应该面向接口编程。ide

/** * Created by guo on 3/1/2018. * bean的容器,工厂 */
public interface BeanFactory {
    Object getBean(String name) throws Exception;
}

/** * Created by guo on 3/1/2018. * 抽象bean工厂 */
public abstract class AbstractBeanFactory implements BeanFactory {
}

``

2、你们想知道AbstractBeanFactory抽象类有什么做用吗?和接口有什么区别吗?何时用接口何时用抽象类?

- 1、若是你拥有一些方法想让他们中的一些默认实现,那么使用抽象类。
- 2、若是你想实现多重继承,那么你必须使用接口。因为java不支多继承,子类不可以继承多个类,但能够实现多个接口
- 3、若是基本功能在不断改变,那么就须要使用抽象类。若是不断改变基本功能而且使用接口 ,那么就须要改变全部实现了该接口的类。

3、有了bean工厂你总得bean的定义吧。`BeanDefinition`来了。bean的定义

```java
/** * Created by guo on 3/1/2018. * bean的内容及元数据,保存在beanFactory中,包装bean的实体。 */
public class BeanDefinition {

  private Object bean;

  //类的class信息
  private Class beanClass;

  //类名
  private String beanClassName;

  //保存全部的属性,
  private PropertyValues  propertyValues  = new PropertyValues();

  public BeanDefinition() {

  }

    public BeanDefinition() {}

    //setters and gettes 略
    public void setBeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
        try {
            //加载类,并返回class对象
            //这里已经有类的实例来,可是没有引用,怎么能够获取这个引用呢?
            this.beanClass = Class.forName(beanClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
复制代码

四、有了bean的定义你总得建立啊,初始化那,注册啊,验证啊,否则一点卵用都没有。来瞅瞅。这时候咱们来看下AbstractBeanFactory抽象类具体的实现。

/** * Created by guo on 3/1/2018. * 抽象bean工厂 */
public abstract class AbstractBeanFactory implements BeanFactory {

    //bean工程里维护类的字典,类名+class对象
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

    private final List<String> beanDefinitionNames = new ArrayList<String>();

    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>();

    /** * 获取bean的时候,才建立类的实例对象,原来只是保存类名和类的Class对象 。 * 到这一步会根据Class对象建立类的实例 * * @param name * @return * @throws Exception */
    @Override
    public Object getBean(String name) throws Exception {
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        if (beanDefinition == null) {
            throw new IllegalArgumentException("no bean named " + name + "is defined");
        }
        Object bean = beanDefinition.getBean();
        if (bean == null) {
            //一、刚建立的对象,其余什么都没作
            bean = doCreateBean(beanDefinition);
            //二、初始化bean对象
            bean = initializeBean(bean,name);
            //三、这里的bean是初始化以后的bean,与刚开始建立的bean不同。
            beanDefinition.setBean(bean);
        }
        return bean;
    }
}
复制代码

五、咱们在来看看具体的doCreateBeaninitializeBeanregisterBeanDefinition

/** * 初始化bean,BeanPostProcessor初始化先后处理器。 */
  protected Object initializeBean(Object bean, String name) throws Exception {
      for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
          bean = beanPostProcessor.postProcessBeforeInitialization(bean, name);
      }

      for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
          bean = beanPostProcessor.postProcessAfterInitialization(bean, name);
      }
      return bean;
  }

  /** * 建立bean的实例 */
  protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
      return beanDefinition.getBeanClass().newInstance();
  }

  /** * 注册bean,即将类名和定义保存到内存中(map对象)中 */
  public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
      beanDefinitionMap.put(name, beanDefinition);
      //保存一份作准备
      beanDefinitionNames.add(name);
  }

  /** * 建立bean并设置bean的引用 */
  protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
      //这里会建立bean的实例对象
      Object bean = createBeanInstance(beanDefinition);

      //将bean的实例对象设置到beandefinition中去
      beanDefinition.setBean(bean);
      //设置bean的引用的实例对象
      applyPropertyValues(bean, beanDefinition);

      return bean;
  }

复制代码

六、可有可无的方法暂时就不贴出来了,描述个大概过程。带着这些类和接口在源码中找。请忽略第二步和第三步。直接跳到第四步。记得是在Spring源码,这个简单多了,可是基本功能有


2.step2-将bean建立放入工厂

step1中的bean是初始化好以后再set进去的,实际使用中,咱们但愿容器来管理bean的建立。因而咱们将bean的初始化放入BeanFactory中。为了保证扩展性,咱们使用Extract Interface的方法,将BeanFactory替换成接口,而使用AbstractBeanFactory和AutowireCapableBeanFactory做为其实现。"AutowireCapable"的意思是“可自动装配的”,为咱们后面注入属性作准备。

// 1.初始化beanfactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();

// 2.注入bean
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService");
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);

// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
复制代码

3.step3-为bean注入属性

这一步,咱们想要为bean注入属性。咱们选择将属性注入信息保存成PropertyValue对象,而且保存到BeanDefinition中。这样在初始化bean的时候,咱们就能够根据PropertyValue来进行bean属性的注入。Spring自己使用了setter来进行注入,这里为了代码简洁,咱们使用Field的形式来注入。

// 1.初始化beanfactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();

// 2.bean定义
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("us.codecraft.tinyioc.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();
复制代码

step4-读取xml配置来初始化bean

一、看到读取你总得有IO流吧 ,还有有资源啊(Xxx.xml),还有一个读取器。让咱们看下重要的接口和实现类。

/** * Resource是Spring内部定位资源接口 */
public interface Resource {
    InputStream getInputStream() throws Exception;
}
-----------------加载资源------------------------------
public class ResourceLoader {

    //获取资源
    public Resource getResource(String location){
        URL resource = this.getClass().getClassLoader().getResource(location);
        return new UrlResource(resource);
    }
}
--------------------------------------------------
*/
public class UrlResource implements Resource {

   private final URL url;

   public UrlResource(URL url) {
       this.url = url;
   }

   @Override
   //根据URL载入输入流
   public InputStream getInputStream() throws IOException{
       URLConnection urlConnection = url.openConnection();
       urlConnection.connect();
       return urlConnection.getInputStream();
   }
}

--------------------测试--------------------------------
public class ResourceLoaderTest {

	@Test
	public void test() throws IOException {
		ResourceLoader resourceLoader = new ResourceLoader();
        Resource resource = resourceLoader.getResource("tinyioc.xml");
        InputStream inputStream = resource.getInputStream();
        Assert.assertNotNull(inputStream);
    }
}
复制代码

二、接下来就让咱们看看更为重要的接口和实现类

public interface BeanDefinitionReader {

    void loadBeanDefinitions(String location) throws Exception;
}

----------------------重要实现----------------------------------
/** * Created by guo on 3/1/2018. * 从配置文件中读取BeanDifinition 抽象类 */
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {

    //bean集合
    private Map<String,BeanDefinition> registry;

    //资源加载器
    private ResourceLoader resourceLoader;

    protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
        this.registry = new HashMap<String, BeanDefinition>();
        this.resourceLoader = resourceLoader;
    }
      //setter。getter
}
复制代码

三、最终的实现来了

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
        super(resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(String location) throws Exception {
        InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
        doLoadBeanDefinitions(inputStream);

    }

复制代码

四、为了便于理解 ,我把方法抽出来了。这里主要是解析和注册

protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
    //xml解析
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder docBuilder = factory.newDocumentBuilder();
    Document doc = docBuilder.parse(inputStream);
    // 解析bean
    registerBeanDefinitions(doc);
    inputStream.close();
}

public void registerBeanDefinitions(Document doc) {
    Element root = doc.getDocumentElement();
    parseBeanDefinitions(root);
}

复制代码

五、真正的解析在这里。

protected void parseBeanDefinitions(Element root) {
    NodeList nl = root.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (node instanceof Element) {
            Element ele = (Element) node;
            processBeanDefinition(ele);
        }
    }
}

protected void processBeanDefinition(Element ele) {
    //获取id和classname
    String name = ele.getAttribute("id");
    String className = ele.getAttribute("class");
    BeanDefinition beanDefinition = new BeanDefinition();
    //处理属性
    processProperty(ele, beanDefinition);
    //注册Class
    beanDefinition.setBeanClassName(className);
    getRegistry().put(name, beanDefinition);
}

//添加bean的属性,和ref引用
private void processProperty(Element ele, BeanDefinition beanDefinition) {
    NodeList propertyNode = ele.getElementsByTagName("property");
    for (int i = 0; i < propertyNode.getLength(); i++) {
        Node node = propertyNode.item(i);
        if (node instanceof Element) {
            Element propertyEle = (Element) node;
            String name = propertyEle.getAttribute("name");
            String value = propertyEle.getAttribute("value");
            if (value != null && value.length() > 0) {
                beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
            } else {
                String ref = propertyEle.getAttribute("ref");
                if (ref == null || ref.length() == 0) {
                    throw new IllegalArgumentException("Configuration problem: <property> element for property '"
                            + name + "' must specify a ref or value");
                }
                //bean对其余对象的引用,直接放到本身的属性里面
                BeanReference beanReference = new BeanReference(ref);
                beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference));
            }
        }
    }
}
}
复制代码

六、这里是测试代码

@Test
public void test() throws Exception {
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
    xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
    Map<String, BeanDefinition> registry = xmlBeanDefinitionReader.getRegistry();
    Assert.assertTrue(registry.size() > 0);
}
复制代码

这么大一坨初始化代码让人心烦。这里的BeanDefinition只是一些配置,咱们仍是用xml来初始化吧。咱们定义了BeanDefinitionReader初始化bean,它有一个实现是XmlBeanDefinitionReader。

// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");

// 2.初始化BeanFactory并注册bean
BeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
        beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}

// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
复制代码

5.step5-为bean注入bean

使用xml配置以后,彷佛里咱们熟知的Spring更近了一步!可是如今有一个大问题没有解决:咱们没法处理bean之间的依赖,没法将bean注入到bean中,因此它没法称之为完整的IoC容器!如何实现呢?咱们定义一个BeanReference,来表示这个属性是对另外一个bean的引用。这个在读取xml的时候初始化,并在初始化bean的时候,进行解析和真实bean的注入。

for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) {
    Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
    declaredField.setAccessible(true);
    Object value = propertyValue.getValue();
    if (value instanceof BeanReference) {
        BeanReference beanReference = (BeanReference) value;
        value = getBean(beanReference.getName());
    }
    declaredField.set(bean, value);
}
复制代码

同时为了解决循环依赖的问题,咱们使用lazy-init的方式,将createBean的事情放到getBean的时候才执行,是否是一会儿方便不少?这样在注入bean的时候,若是该属性对应的bean找不到,那么就先建立!由于老是先建立后注入,因此不会存在两个循环依赖的bean建立死锁的问题。

// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");

// 2.初始化BeanFactory并注册bean
AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
    beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}

// 3.初始化bean
beanFactory.preInstantiateSingletons();

// 4.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
复制代码

6.step6-ApplicationContext登场

无论三七二十一,咱们先看重要的接口以及重要实现

/** * 继承beanFactory,继承了factory全部的遗产 */
public interface ApplicationContext extends BeanFactory {
}
-----------------------------------------------------------------
public abstract class AbstractApplicationContext implements ApplicationContext {
	protected AbstractBeanFactory beanFactory;

	public AbstractApplicationContext(AbstractBeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}

	public void refresh() throws Exception {
		//加载bean
		loadBeanDefinitions(beanFactory);
		//注册以前,干点什么事情
		registerBeanPostProcessors(beanFactory);
		onRefresh();
	}

	//调用beanfactory工厂获取bean的实例对象
	@Override
	public Object getBean(String name) throws Exception {
		return beanFactory.getBean(name);
	}
}
复制代码

二、为了方便,方法放这里

protected abstract void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception;

protected void registerBeanPostProcessors(AbstractBeanFactory beanFactory) throws Exception {
  List beanPostProcessors = beanFactory.getBeansForType(BeanPostProcessor.class);
  for (Object beanPostProcessor : beanPostProcessors) {
    beanFactory.addBeanPostProcessor((BeanPostProcessor) beanPostProcessor);
  }
}

protected void onRefresh() throws Exception{
      beanFactory.preInstantiateSingletons();
  }
复制代码

三、熟悉的东东出场了

public class ClassPathXmlApplicationContext extends AbstractApplicationContext {

	private String configLocation;

	public ClassPathXmlApplicationContext(String configLocation) throws Exception {
		this(configLocation, new AutowireCapableBeanFactory());     //可自动装配内容的BeanFactory
	}

	public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception {
		super(beanFactory);
		this.configLocation = configLocation;
		//直接所有初始化
		refresh();
	}

	@Override
	protected void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception {
		//定位bean,而后加载bean
		XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
		xmlBeanDefinitionReader.loadBeanDefinitions(configLocation);
		//注册bean,这里bean已经加载到虚拟机中,但尚未实例化对象,先不急嘛。
		for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
			beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
		}
	}

复制代码

三、测试代码

@Test
public void test() throws Exception {
    //就是把beanfactory封装一下,使调用更加方便。注册,所有初始化。
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
    HelloWorldService helloWorldService = (com.guo.codecraft.tinyioc.HelloWorldService) applicationContext.getBean("helloWorldService");
    helloWorldService.helloWorld();
}

@Test
public void testPostBeanProcessor() throws Exception {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc-postbeanprocessor.xml");
    HelloWorldService helloWorldService = (com.guo.codecraft.tinyioc.HelloWorldService) applicationContext.getBean("helloWorldService");
    helloWorldService.helloWorld();
}
复制代码

如今BeanFactory的功能齐全了,可是使用起来有点麻烦。因而咱们引入熟悉的ApplicationContext接口,并在AbstractApplicationContext的refresh()方法中进行bean的初始化工做。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
helloWorldService.helloWorld();
复制代码

是否是很是熟悉?至此为止,咱们的tiny-spring的IoC部分可说完工了。这部分的类、方法命名和做用,都是对应Spring中相应的组件。虽然代码量只有400多行,可是已经有了基本的IoC功能!

相关文章
相关标签/搜索