• 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技术内幕》作者 计文柯

  • 相关阅读:
    RHEL5.8配置开机自动挂载磁盘
    RHEL5.8配置NFS服务
    Linux环境下Oracle数据库启动停止命令
    使用的组件:Layui
    使用的组件:Jcrop
    使用的组件:ckeditor
    使用的组件:Web Uploader
    WebSphere试用过期问题处理
    webpack学习笔记(一)
    如何用node命令和webpack命令传递参数 转载
  • 原文地址:https://www.cnblogs.com/wwzyy/p/15860179.html
Copyright © 2020-2023  润新知