Простая реализация IOC простой версии фреймворка Spring

задняя часть программист GitHub Spring

Упрощенная версия фреймворка Spring.

Функции

  • Поддерживает bean-компоненты типа singleton, включая инициализацию, внедрение свойств и внедрение bean-компонентов зависимостей.
  • Конфигурацию можно прочитать из xml.
  • АОП может быть написан наподобие Aspectj, который поддерживает интерфейс и прокси класса.

иллюстрировать

Если вам посчастливится увидеть

  • 1. Справочник по Simple Spring Framework в этой статьеGithub, ссылка на комментарий к кодуGithub.
  • 2. Право собственности принадлежит оригинальному автору, здесь я просто копирую, обратитесь к процессу восстановления аннотации. Если не понятно, можете посмотреть авторское видео.
  • 3. Давайте работать вместе и учиться вместе.Если вам интересно, вы также можете посмотреть моиGithub. Загрузил исходный код Spring. Вы можете увидеть большого парня.
  • 4. Я надеюсь, что у вас есть исходный код Spring до прочтения этой статьи. Сравните, чтобы найти нужный интерфейс и класс. углубить впечатление.
  • 5. Эта статья предназначена только для моего собственного обзора в будущем, пожалуйста, простите меня, если это неправильно.

tiny-springОн разработан для изучения Spring и может рассматриваться как упрощенная версия Spring. В Spring много кода, сложных слоев и его трудно читать. Я пытаюсь использовать функцию с точки зрения, обратиться к реализации Spring, построить ее шаг за шагом и, наконец, завершить упрощенную версию Spring. Некоторые сравнивают программистов с художниками.У художников есть базовый навык, называемый копированием.Tiny-spring можно рассматривать как копирующую версию программы - отталкиваясь от собственных нужд, дизайна программы, и в то же время ссылаясь на известные проекты.

Часть 1: Контейнеры IoC

Увы, это не пустая трата времени, чтобы увидеть это раньше.

1. Схема последовательности - процесс загрузки обычных бобов

2. Основные родственные классы для загрузки ресурсов

3. Загрузите основные связанные классы определения компонента из xml.

4. Основные родственные классы ассемблерного компонента

5. Связанные классы, реализующие интерфейс ApplicationContext

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

1. Во-первых, давайте посмотрим на общую структуру кода, главное здесь — интерфейс BeanFactory. Мы должны программировать для интерфейсов.

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

4. С определением 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) {
            //1、刚创建的对象,其他什么都没做
            bean = doCreateBean(beanDefinition);
            //2、初始化bean对象
            bean = initializeBean(bean,name);
            //3、这里的bean是初始化之后的bean,与刚开始创建的bean不一样。
            beanDefinition.setBean(bean);
        }
        return bean;
    }
}

5. Давайте посмотрим на конкретныеdoCreateBean,initializeBean,registerBeanDefinition


  /**
   * 初始化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;
  }

6. Неактуальные методы пока не будут выкладываться, а описывать общий процесс. Найдите в исходниках эти классы и интерфейсы. Пожалуйста, игнорируйте шаги 2 и 3. Перейдите сразу к шагу 4. Помните, что это в исходном коде Spring, это намного проще, но основные функции


2.step2- поместите создание бина на фабрику

Компоненты на шаге 1 инициализируются, а затем устанавливаются. В реальном использовании мы хотим, чтобы контейнер управлял созданием компонентов. Итак, мы помещаем инициализацию бина в BeanFactory. Чтобы обеспечить расширяемость, мы используем метод Extract Interface для замены BeanFactory интерфейсом и используем AbstractBeanFactory и AutowireCapableBeanFactory в качестве его реализации. «AutowireCapable» означает «autowireable», готовый для того, чтобы мы могли добавить свойства позже.

// 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-Inject свойства для bean-компонента

На этом этапе мы хотим внедрить свойства в bean-компонент. Мы решили сохранить информацию о внедрении свойства как объект PropertyValue и сохранить ее в BeanDefinition. Таким образом, когда bean-компонент инициализируется, мы можем внедрить свойства bean-компонента в соответствии со свойством PropertyValue. Сам Spring использует сеттеры для инъекций, для простоты кода мы используем 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-компонента

1. У вас должен быть поток 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);
    }
}

2. Далее давайте рассмотрим более важные интерфейсы и классы реализации.

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
}

3. Пришло окончательное осознание


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

    }

4. Чтобы облегчить понимание, я нарисовал метод. Здесь в основном парсинг и регистрация

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);
}

5. Настоящий анализ здесь.



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));
            }
        }
    }
}
}

6. Вот тестовый код

@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 для ее инициализации. Мы определяем bean-компонент инициализации BeanDefinitionReader, реализация которого называется 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-Inject bean for bean

После использования конфигурации xml кажется, что всем известный Spring стал на шаг ближе! Но теперь есть большая нерешенная проблема: мы не можем обрабатывать зависимости между bean-компонентами, не можем внедрять bean-компоненты в bean-компоненты, поэтому его нельзя назвать полноценным IoC-контейнером! Как этого достичь? Мы определяем BeanReference, чтобы указать, что это свойство является ссылкой на другой компонент. Это инициализируется при чтении 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-компонентов.

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

2. Для удобства метод выложен здесь

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

3. Появились знакомые вещи

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

3. Тестовый код

@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 и инициализируем bean-компонент в методе refresh() класса AbstractApplicationContext.

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

Это очень знакомо? На данный момент можно сказать, что часть IoC нашего крошечного источника завершена. Имена и функции классов и методов в этой части соответствуют соответствующим компонентам в Spring. Хотя объем кода составляет всего более 400 строк, в нем уже есть базовые функции IoC!