实现一个极简IOC容器

| 本文通过xml解析技术和反射技术手动实现Spring ioc容器通过xml配置文件初始化bean的过程,仅实现getBean方法

1、环境准备

1、创建一个maven项目
2、pom.xml中引入dom4jjaxen、lombok依赖

dom4j: xml解析组件
jaxen:Xpath表达式解析器
lombok:用于简化代码 比如setter、getter、构造器等等

<!--dom4j-->
<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>
<!-- jaxen-->
<dependency>
    <groupId>jaxen</groupId>
    <artifactId>jaxen</artifactId>
    <version>1.1.1</version>
</dependency>
<!-- lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
</dependency>

2、实现过程

1、实体类:Student
| 此处在setter方法中打印语句,便于观察最终结果来印证ioc容器注入属性的方式

package com.ioc.entity;
import lombok.*;
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private String name;
    private String id;
    private String sex;

    public void setName(String name) {
        System.out.println("IOC容器通过setter方法注入name属性:" + name);
        this.name = name;
    }

    public void setId(String id) {
        System.out.println("IOC容器通过setter方法注入id属性:" + id);
        this.id = id;
    }

    public void setSex(String sex) {
        System.out.println("IOC容器通过setter方法注入sex属性:" + sex);
        this.sex = sex;
    }
}

2、xml配置文件:applicationContext.xml
| 配置文件结构和spring保持一致

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="s1" class="com.ioc.entity.Student">
        <property name="name" value="张三"/>
        <property name="id" value="1928722031"/>
        <property name="sex" value=""/>
    </bean>
</beans>

3、ApplicationContext抽象类

public interface ApplicationContext {
    public Object getBean(String beanId);
}

4、ClassPathXmlApplicationContext:Spring context实现类

package com.ioc.context;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ClassPathXmlApplicationContext implements ApplicationContext {
    //使用Map集合
    Map<String, Object> iocContainer = new HashMap<>();

    public ClassPathXmlApplicationContext() {
        try {
            //获取配置文件的路径
            String filepath = this.getClass().getResource("/applicationContext.xml").getPath();
            //将路径用UTF-8解码,防止中文路径乱码
            filepath = new URLDecoder().decode(filepath, "UTF-8");
            //用于解析XML文件
            SAXReader reader = new SAXReader();
            //将xml文件解析成对象 便于操作
            Document document = reader.read(new File(filepath));
            //使用Xpath表达式选择所有bean元素 每个bean元素对应一个对象
            List<Node> beans = document.getRootElement().selectNodes("bean");
            for (Node e : beans) {
                //将bean节点强制转化为Element 很关键
                Element element = (Element) e;
                //获取bean标签的属性值:id class
                String id = element.attributeValue("id");
                String eClass = element.attributeValue("class");
                //获取class属性代表的类的Class对象
                Class c = Class.forName(eClass);
                //利用反射 通过空构造器实例化bean
                //注:Class对象.newInstance()已被废弃
                Object o = c.getDeclaredConstructor().newInstance();
                //使用Xpath表达式选择bean元素的所有属性值 property节点
                //每个property节点代表对象的一个属性
                List<Node> propertys = element.selectNodes("property");
                for (Node p : propertys) {
                    Element property = (Element) p;
                    //获取property标签的属性值:name value
                    String name = property.attributeValue("name");
                    String value = property.attributeValue("value");
                    //此处采用字符串拼接构建属性对应的setter方法名
                    String setMethodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                    //通过方法名和Class对象得到对应的Method类 便于后续反射调用
                    Method method = c.getMethod(setMethodName, String.class);
                    //通过反射 利用属性的setter方法对属性赋值
                    method.invoke(o, value);
                }
                //将创建好的bean放入IOC容器(Map)中
                iocContainer.put(id, o);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("ioc容器已初始化");
    }

    @Override
    public Object getBean(String beanId) {
        return iocContainer.get(beanId);
    }
}

5、测试类:Application

package com.ioc.context;
import com.ioc.entity.Student;
public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext();
        Student s1 = (Student) context.getBean("s1");
        System.out.println(s1);
    }
}

运行结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值