目录
1.首先要创建一个maven项目,并导入相关依赖(dom4j)
2.在src\main\resources目录下创建一个spring.xml配置文件。
3.然后在src\main\java目录下编写相关类,详细说明已在注释中写出
3.4 ClasspathXmlApplicationContext 类
1.首先要创建一个maven项目,并导入相关依赖(dom4j)
可以通过 https://mvnrepository.com/ 获取相关依赖代码,复制到pom.xml文件中的<dependencies>标签里。
注意:因为是模拟实现spring,所以不需要导入spring相关依赖。
2.在src\main\resources目录下创建一个spring.xml配置文件。
3.然后在src\main\java目录下编写相关类,详细说明已在注释中写出
3.1 User 类
package com.funny.entity;
public class User {
public User() {
System.out.println("User.User");
}
public void show() {
System.out.println("User.show()方法被调用");
}
}
3.2 BeanDefinition 类
package com.funny.spring;
/**
* 用于存储 Bean 对象中的属性名和对应的值
*/
public class BeanDefinition {
private String id;
private String clazz;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}
3.3 ApplicationContext 接口
package com.funny.spring;
/**
* 定义 ApplicationContext 接口
*/
public interface ApplicationContext {
//通过字节码获取Bean
<T> T getBean(Class<T> clazz);
//通过bean名称获取Bean
Object getBean(String beanName);
}
3.4 ClasspathXmlApplicationContext 类
package com.funny.spring;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.*;
/**
* 用于解析 spring 配置文件的类
*/
public class ClasspathXmlApplicationContext implements ApplicationContext {
//利用List集合存放 Bean 对象属性的集合
private List<BeanDefinition> beanDefinitionList;
//单例池 利用Map集合存放
private Map<String,Object> singleton;
public ClasspathXmlApplicationContext() {
this("spring.xml");
}
public ClasspathXmlApplicationContext(String configName) {
this.beanDefinitionList = new ArrayList<>();
this.singleton = new HashMap<>();
//解析 xml 配置文件
parseXmlConfigurationFile(configName);
//实例化对象
instance();
}
//用于解析 xml 配置文件
private void parseXmlConfigurationFile(String name){
try {
//创建类装载器
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
//创建字节输入流对象
InputStream in = classloader.getResourceAsStream(name);
//创建SAXReader对象
SAXReader reader = new SAXReader();
//调用这个对象的read方法来读取文件
Document document = reader.read(in);
//获取根节点
Element root = document.getRootElement();
//获取根节点下的所有子节点
Iterator<Element> childElements = root.elementIterator();
//循环处理所有子节点
while (childElements.hasNext()){
Element childElement = childElements.next();
//创建 BeanDefinition 对象
BeanDefinition beanDefinition = new BeanDefinition();
for (Iterator<Attribute> it = childElement.attributeIterator(); it.hasNext();) {
//获取点前节点属性
Attribute attribute = it.next();
//判断如果属性名是id 就 setId,否则setClazz
if("id".equals(attribute.getName())){
beanDefinition.setId(attribute.getValue());
}else{
beanDefinition.setClazz(attribute.getValue());
}
}
beanDefinitionList.add(beanDefinition);
}
} catch (DocumentException e) {
throw new RuntimeException("读取配置文件出错,错误信息为"+e.getMessage());
}
}
//实例化对象
private void instance() {
for (BeanDefinition beanDefinition : beanDefinitionList) {
try {
//通过反射获取对象
Class<?> clazz = Class.forName(beanDefinition.getClazz());
//获取构造器对象
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true); //暴力反射
Object o = constructor.newInstance();
//把实例化对象放入单例池中
singleton.put(beanDefinition.getId(),o);
singleton.put(clazz.getSimpleName(),o);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//通过字节码获取Bean
@Override
public <T> T getBean(Class<T> clazz) {
if(singleton.containsKey(clazz.getSimpleName())){
return (T)singleton.get(clazz.getSimpleName());
}
throw new RuntimeException("bean不存在");
}
//通过名称获取Bean
@Override
public Object getBean(String beanName) {
if(singleton.containsKey(beanName)){
return singleton.get(beanName);
}
throw new RuntimeException("bean不存在");
}
}
3.4.1 关于为什么要是有暴力反射?
暴力反射是除了public的甚至是连private都可以获取。
如果一个类的构造方法是私有的,也就是private 修饰的,是不能在外部直接使用new 来创建对象。这个时候你要是使用反射会出错,暴力反射正好解决这个问题。当然不只是构造方法,其他方法,属性等也同样。
所以使用暴力反射是为了避免出现不必要的错误。
3.5 测试类 SpringTest
package com.funny.test;
import com.funny.entity.User;
import com.funny.spring.ApplicationContext;
import com.funny.spring.ClasspathXmlApplicationContext;
/**
* 测试类
*/
public class SpringTest {
public static void main(String[] args) {
ApplicationContext context = new ClasspathXmlApplicationContext("spring.xml");
//User user = (User) context.getBean("user");
User user = context.getBean(User.class);
user.show();
}
}