简单地模拟实现Spring解析配置文件并实例化对象

目录

1.首先要创建一个maven项目,并导入相关依赖(dom4j)

2.在src\main\resources目录下创建一个spring.xml配置文件。

3.然后在src\main\java目录下编写相关类,详细说明已在注释中写出

3.1 User 类

3.2 BeanDefinition 类

3.3 ApplicationContext 接口

3.4 ClasspathXmlApplicationContext 类

3.4.1 关于为什么要是有暴力反射?

3.5 测试类 SpringTest 

4. 执行结果


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();
    }
}

4. 执行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值