首先我们要模拟spring,先搞配置文件,并配置bean
创建我们需要的类,beandefito,这个类是用来装解析后的bean,主要三个字段,id,class,scop,对应xml配置的属性
package org.zhw.ezspring.domain;
//解析后的bean
public class BeanDefinition {private String id;private String className;private String scope;public BeanDefinition(String id, String className, String scope) {this.id = id;this.className = className;this.scope = scope;}public BeanDefinition() {}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getClassName() {return className;}public void setClassName(String className) {this.className = className;}public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}
}
然后配置下我们要用的beandemo,这个后面我没用,但是懂的都懂,让大家更好理解下,xml中class,就是我们bean的路径,因为我们要用到反射。
package org.zhw.ezspring.domain;public class DemoBean {private String Name;private String age;public String getName() {return Name;}public void setName(String name) {Name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public DemoBean() {}public DemoBean(String name, String age) {Name = name;this.age = age;}
}
对了,我们还要使用一个jar包,这个可以用来解析xml文件,非常好用
然后我们开始模拟手写简易版spring,由于我已经全部写完了,就先把核心代码写在下面。
以防大家看不清,我把文件目录展示下
package org.zhw.ezspring.factory;import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.zhw.ezspring.domain.BeanDefinition;
import org.zhw.ezspring.domain.DemoBean;import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;public class BeanFactory {
// 用来装beandefiton 的map
static ConcurrentHashMap<String, BeanDefinition> beanDiMap = new ConcurrentHashMap<>();
// 用来装单例bean的map
static HashMap<String, DemoBean> objectObjectHashMap = new HashMap<>();
// 实例化的时候,就加载beanpublic BeanFactory(){
// 解析xml配置文件this("E:\\ideaspace\\ez-spring\\src\\resouces\\application.xml");}public BeanFactory(String path) {SAXReader reader = new SAXReader();try {// 读取XML文件Document document = reader.read(new File(path));// 获取根元素Element rootElement = document.getRootElement();// 遍历元素List<Element> elements = rootElement.elements();for (Element element : elements) {BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setClassName(element.attributeValue("className"));beanDefinition.setId(element.attributeValue("id"));beanDefinition.setScope(element.attributeValue("scope"));String scope = element.attributeValue("scope");
// 默认为单例if(scope!=null&&!"".equals(scope)){beanDefinition.setScope("singleton");}System.out.println(beanDefinition);
// 放入beandefitionmapbeanDiMap.put(beanDefinition.getId(),beanDefinition);iniSingletonObjects();}} catch (DocumentException e) {e.printStackTrace();}}private void iniSingletonObjects() {
// 1.遍历map
// 2.判断是不是单例,是就放入它Iterator<Map.Entry<String, BeanDefinition>> iterator = beanDiMap.entrySet().iterator();while (iterator.hasNext()){Map.Entry<String, BeanDefinition> entry = iterator.next();BeanDefinition value = entry.getValue();String scope = value.getScope();String id = value.getId();if (scope.equals("singleton")){DemoBean demoBean = new DemoBean();demoBean.setName("z");objectObjectHashMap.put(id,demoBean);}}}public Object getBean(String id ) throws DocumentException {
//根据id,作为key去beandifitionmap中遍历,判断是单例还是多例,是单例就去单例map中找,不是则直接反射。
// BeanFactory beanFactory = new BeanFactory();BeanDefinition beanDefinition = beanDiMap.get(id);if (Objects.isNull(beanDefinition)){throw new RuntimeException("beanFactory is null");}if ("singleton".equals(beanDefinition.getScope())){BeanDefinition beanDefinition1 = beanDiMap.get(id);return beanDefinition1;}
// 如何是多例,则使用反射创建beantry {Class<?> aClass = Class.forName(beanDefinition.getClassName());DemoBean o = (DemoBean) aClass.newInstance();} catch (Exception e) {throw new RuntimeException("获取bean失败");}return null;}
// 通过class,来获取beanpublic <T>T getBean(Class<T> classz){String name = classz.getName();Set<Map.Entry<String, BeanDefinition>> entries = beanDiMap.entrySet();Map.Entry<String, BeanDefinition> first = entries.stream().filter(x ->name.equals(x.getValue().getClassName())).findFirst().get();BeanDefinition value = first.getValue();if (value.getScope().equals("singleton")){return (T) beanDiMap.get(value.getId());}
// 不是单例就通过反射创建新的else {try {String id = value.getId();BeanDefinition beanDefinition = beanDiMap.get(id);Class<?> aClass = Class.forName(beanDefinition.getClassName());DemoBean o = (DemoBean) aClass.newInstance();} catch (Exception e) {throw new RuntimeException("获取bean失败");}}return null;}public static void main(String[] args) {BeanFactory beanFactory = new BeanFactory();try {Object demo1 = beanFactory.getBean("demo1");Object demo2 = beanFactory.getBean("demo1");System.out.println(demo1==demo2);} catch (Exception e) {throw new RuntimeException(e);}}}
这是我们的结果,免得说我失败了,两个单例bean的地址完全相同
ok,让我们开始解析我手写的代码,非常简单易懂,哈哈
首先我们需要new,两个map,一个用来装解析bean后的beandefitonmap,一个用来装单例bean,因为spring原理,bean单例,就是要从单例map里面找得嘛。
然后我们首先要解析xml,new个工厂,在new工厂的过程中,我们就要对bean进行解析,包装为beandefiton,放到beandefitonmap中去。同时要默认为单例bean,所以没有加scope的bean要写为单例bean。还要对beandefinitionmap进行遍历,把单例bean,放入单例beanmap中
为什么我们可以遍历xml,是因为我们用了dom4j,百度ai直接搜,dom4j解析xml,我直接粘贴复制过来的
一个有参,一个无参,这个应该看得懂撒。
private void iniSingletonObjects() {
// 1.遍历map
// 2.判断是不是单例,是就放入它Iterator<Map.Entry<String, BeanDefinition>> iterator = beanDiMap.entrySet().iterator();while (iterator.hasNext()){Map.Entry<String, BeanDefinition> entry = iterator.next();BeanDefinition value = entry.getValue();String scope = value.getScope();String id = value.getId();if (scope.equals("singleton")){DemoBean demoBean = new DemoBean();demoBean.setName("z");objectObjectHashMap.put(id,demoBean);}}}
public BeanFactory(){
// 解析xml配置文件this("E:\\ideaspace\\ez-spring\\src\\resouces\\application.xml");}public BeanFactory(String path) {SAXReader reader = new SAXReader();try {// 读取XML文件Document document = reader.read(new File(path));// 获取根元素Element rootElement = document.getRootElement();// 遍历元素List<Element> elements = rootElement.elements();for (Element element : elements) {BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setClassName(element.attributeValue("className"));beanDefinition.setId(element.attributeValue("id"));beanDefinition.setScope(element.attributeValue("scope"));String scope = element.attributeValue("scope");
// 默认为单例if(scope!=null&&!"".equals(scope)){beanDefinition.setScope("singleton");}System.out.println(beanDefinition);
// 放入beandefitionmapbeanDiMap.put(beanDefinition.getId(),beanDefinition);iniSingletonObjects();}} catch (DocumentException e) {e.printStackTrace();}}
然后我们就来写关于获取bean的方式,一个根据id获取,一个根据class获取。
根据id,先去beandefitonmap中去找,根据id,然后判断是不是空,还是单例bean,单例bean就去单例map中去找,多例则是用反射创建bean。
public Object getBean(String id ) throws DocumentException {
//根据id,作为key去beandifitionmap中遍历,判断是单例还是多例,是单例就去单例map中找,不是则直接反射。
// BeanFactory beanFactory = new BeanFactory();BeanDefinition beanDefinition = beanDiMap.get(id);if (Objects.isNull(beanDefinition)){throw new RuntimeException("beanFactory is null");}if ("singleton".equals(beanDefinition.getScope())){BeanDefinition beanDefinition1 = beanDiMap.get(id);return beanDefinition1;}
// 如何是多例,则使用反射创建beantry {Class<?> aClass = Class.forName(beanDefinition.getClassName());DemoBean o = (DemoBean) aClass.newInstance();} catch (Exception e) {throw new RuntimeException("获取bean失败");}return null;}
根据class获取bean,先用class获取bean的名字,然后我们去beandefitonmap中去找bean,然后判断是不是单例bean,这里我脑子有点混乱了,从beandftionmap中已经获取了bean,然后还要去单例map中获取,这不是多此一举吗?如果不是,自己通过反射,new一个新的bean
关于我通过class,获取bean,我感觉我写的很混乱,写的不是很好,如果错了,不要怪我哈,以供借鉴提醒。
public <T>T getBean(Class<T> classz){String name = classz.getName();Set<Map.Entry<String, BeanDefinition>> entries = beanDiMap.entrySet();Map.Entry<String, BeanDefinition> first = entries.stream().filter(x ->name.equals(x.getValue().getClassName())).findFirst().get();BeanDefinition value = first.getValue();if (value.getScope().equals("singleton")){return (T) beanDiMap.get(value.getId());}
// 不是单例就通过反射创建新的else {try {String id = value.getId();BeanDefinition beanDefinition = beanDiMap.get(id);Class<?> aClass = Class.forName(beanDefinition.getClassName());DemoBean o = (DemoBean) aClass.newInstance();} catch (Exception e) {throw new RuntimeException("获取bean失败");}}return null;}