首先我们要模拟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<>();
// 实例化的时候,就加载bean
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);
// 放入beandefitionmap
beanDiMap.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;
}
// 如何是多例,则使用反射创建bean
try {
Class<?> aClass = Class.forName(beanDefinition.getClassName());
DemoBean o = (DemoBean) aClass.newInstance();
} catch (Exception e) {
throw new RuntimeException("获取bean失败");
}
return null;
}
// 通过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;
}
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);
// 放入beandefitionmap
beanDiMap.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;
}
// 如何是多例,则使用反射创建bean
try {
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;
}