Spring源码-自定义IOC容器及Bean解析注册【4】

实验环境:spring-framework-5.0.2、jdk8、gradle4.3.1

 

上文介绍了一些常用容器及初始化、Bean解析过程,源码里面的实现比较复杂,如果我们想要自己实现一个IOC容器,自定义bean配置文件该怎么做呢?
其实只要了解核心思路实现起来是很简单的,只需3步:
1)继承AbstractApplicationContext 
2)读取配置文件,封装成beanDefinition对象,存入beanDefinitionMap中
3)调用refresh方法

 

1、定义配置文件 新建一个文件,格式如下:`user`是bean的名字,`beans.User`是bean的类路径,其中的`id`、`name`是字段属性值

user=beans.User{id:001,name:小明}
user2=beans.User2{id:002,name:小明2}
package beans;

import org.springframework.stereotype.Component;


@Component
public class User {

    private int id;
    private String name;

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

 

2、既然我们定义好了配置文件格式,那么就需要一个BeanDefinitionReader来针对这类文件进行解析

package context;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

/**
 * 自定义BeanDefinitionReader
 */
public class MyBeanDefinitionReader extends AbstractBeanDefinitionReader {

	protected final Log logger = LogFactory.getLog(getClass());

	public MyBeanDefinitionReader(BeanDefinitionRegistry registry) {
		super(registry);
	}

	@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		// 解析配置文件
		List<BeanDef> beanDefList = parseResource(resource);

		beanDefList.forEach(beanDef -> {
			// 构造BeanDefinition
			BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
					.genericBeanDefinition(beanDef.getClassPath());
			// 填充参数
			beanDef.getParamMap().forEach(beanDefinitionBuilder::addPropertyValue);
			// 注册
			super.getBeanFactory().registerBeanDefinition(beanDef.getId(),
					beanDefinitionBuilder.getBeanDefinition());
		});
		return beanDefList.size();
	}

	private List<BeanDef> parseResource(Resource resource) {
		List<BeanDef> beanDefList = new ArrayList<>();
		BufferedReader reader = null;

		try {
			reader = new BufferedReader(new BufferedReader(new InputStreamReader(resource.getInputStream())));
			String line = reader.readLine();
			while (!StringUtils.isBlank(line)) {
				BeanDef beanDef = buildBeanDef(line);
				if (!beanDefList.contains(beanDef)) {
					beanDefList.add(beanDef);
				} else {
					throw new RuntimeException("bean定义重复" + beanDef);
				}
				line = reader.readLine();
			}
			reader.close();

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
		return beanDefList;
	}

	private BeanDef buildBeanDef(String line) {
		BeanDef beanDef = new BeanDef();
		String[] split = line.split("=");

		String id = split[0];
		beanDef.setId(id);

		String source = split[1];
		if (source.contains("{") && source.contains("}")) {
			int begin = source.indexOf("{");
			int end = source.indexOf("}");

			String classPath = source.substring(0, begin);
			beanDef.setClassPath(classPath);

			String params = source.substring(begin + 1, end);
			String[] paramArray = params.split(",");

			for (String param : paramArray) {
				String[] kv = param.split(":");
				beanDef.getParamMap().put(kv[0], kv[1]);
			}
		} else {
			beanDef.setClassPath(source);
		}
		return beanDef;
	}


	public static class BeanDef {
		private String id;
		private String classPath;
		private Map<String, Object> paramMap = new HashMap<>();

		public String getId() {
			return id;
		}

		public void setId(String id) {
			this.id = id;
		}

		public String getClassPath() {
			return classPath;
		}

		public void setClassPath(String classPath) {
			this.classPath = classPath;
		}

		public Map<String, Object> getParamMap() {
			return paramMap;
		}

		@Override
		public boolean equals(Object o) {
			if (this == o) {
				return true;
			}
			if (o == null || getClass() != o.getClass()) {
				return false;
			}
			BeanDef beanDef = (BeanDef) o;
			return id.equals(beanDef.id) &&
					classPath.equals(beanDef.classPath);
		}

		@Override
		public int hashCode() {
			return Objects.hash(id, classPath);
		}

		@Override
		public String toString() {
			return "BeanDef{" +
					"id='" + id + '\'' +
					", classPath='" + classPath + '\'' +
					", paramMap=" + paramMap +
					'}';
		}
	}
}

 

3、现在我们可以读取bean配置文件,然后解析成BeanDefinition注册到容器中,于是我们再准备一个容器

package context;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * 自定义容器实现
 */
public class MyApplicationContext extends AbstractApplicationContext {

	private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

	// 创建自定义bean解析器,传入bean注册器beanFactory
	private final MyBeanDefinitionReader definitionReader = new MyBeanDefinitionReader(beanFactory);

	public MyApplicationContext(String resourcePath) {
		this.definitionReader.loadBeanDefinitions(new ClassPathResource(resourcePath));
		this.refresh();
	}

	/**
	 * 设置父容器,比如spring mvc容器作为子容器,父容器是spring容器
	 */
	@Override
	public void setParent(@Nullable ApplicationContext parent) {
		super.setParent(parent);
		this.beanFactory.setParentBeanFactory(getInternalParentBeanFactory());
	}

	@Override
	public void setId(String id) {
		super.setId(id);
	}

	public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
		this.beanFactory.setAllowBeanDefinitionOverriding(allowBeanDefinitionOverriding);
	}

	public void setAllowCircularReferences(boolean allowCircularReferences) {
		this.beanFactory.setAllowCircularReferences(allowCircularReferences);
	}

	//---------------------------------------------------------------------
	// ResourceLoader / ResourcePatternResolver override if necessary
	//---------------------------------------------------------------------
	@Override
	public Resource getResource(String location) {
		return super.getResource(location);
	}

	@Override
	public Resource[] getResources(String locationPattern) throws IOException {
		return super.getResources(locationPattern);
	}

	@Override
	public void setClassLoader(@Nullable ClassLoader classLoader) {
		super.setClassLoader(classLoader);
	}

	@Override
	@Nullable
	public ClassLoader getClassLoader() {
		return super.getClassLoader();
	}


	//---------------------------------------------------------------------
	// Implementations of AbstractApplicationContext's template methods
	//---------------------------------------------------------------------

	@Override
	protected final void refreshBeanFactory() throws IllegalStateException {
		this.beanFactory.setSerializationId(getId());
	}

	@Override
	protected void cancelRefresh(BeansException ex) {
		this.beanFactory.setSerializationId(null);
		super.cancelRefresh(ex);
	}

	@Override
	protected final void closeBeanFactory() {
		this.beanFactory.setSerializationId(null);
	}

	@Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		return this.beanFactory;
	}

	public final DefaultListableBeanFactory getDefaultListableBeanFactory() {
		return this.beanFactory;
	}

	@Override
	public AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException {
		assertBeanFactoryActive();
		return this.beanFactory;
	}

	// 子类重写
	@Override
	public void postProcessBeanFactory(
			ConfigurableListableBeanFactory beanFactory) {
		if (beanFactory instanceof DefaultListableBeanFactory) {
			DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
			String[] beanDefinitionNames = factory.getBeanDefinitionNames();
			System.out.println("step4.1 postProcessBeanFactory子类重写,已经加载beanDefinitionNames " + Arrays.asList(beanDefinitionNames));
		}

	}

	// 子类重写
	@Override
	protected void onRefresh() throws BeansException {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
		System.out.println("step9.1 onRefresh子类重写,已经加载beanDefinitionNames " + Arrays.asList(beanDefinitionNames));

	}
}

 

4、测试一下bean能否正常注入到容器中

public class Main {

	public static void main(String[] args) {
		testMyApplicationContext();
	}

	private static void testMyApplicationContext() {
		String path = "/config/mycontext.txt";
		MyApplicationContext context = new MyApplicationContext(path);
		System.out.println(context.getBean("user"));
		context.close();
	}
}

 

参考资料:

《Spring5核心原理与30个类手写》作者 谭勇德

《Spring源码深度解析》作者 郝佳

《Spring技术内幕》作者 计文柯

posted on 2022-02-02 11:44  wzyy  阅读(184)  评论(0编辑  收藏  举报