[Framework source code] Handwritten Spring framework IOC container core process

insert image description here

If we want to understand the bottom layer of Spring IOC, we must first understand what IOC is.

IOC is the inversion of control, and the process of object creation and calling between objects is handed over to Spring for management.

The purpose of using IOC is that we used to create objects in the way of new before. This method has a disadvantage. Once the position of the class of the created object is changed, new will report an error, and the relationship between classes is too large. , in order to reduce the degree of coupling , the creation of objects is handed over to the IOC container for processing.

The underlying principle of IOC is also the use of main technologies including: xml parsing, factory design pattern, and reflection.

IOC mainly parses the class attribute corresponding to the id attribute of the configuration file through the internal factory class, uses the reflection Class.forName() method, the parameter is the class attribute value, and goes back and forth to the corresponding class to obtain the object.

Next, let's implement the core functions of Spring IOC in our own way.

The core functions of Spring IOC are as follows:

  • Support reading xml files and creating multiple beans
  • Provide an interface to get the container bean according to the id
  • According to the xml configuration, the object injection function needs to be supported
  • Support judging whether there is a bean in the container and obtaining the class type
  • Using appropriate interface extraction and encapsulation, the usage is basically similar to the spring framework

This is how we create beans in the Spring framework:

insert image description here

Next, we will customize a simple IOC framework according to Spring's method of creating beans.

Create a maven project.

insert image description here

First, we add a dependency, and then create some environment test classes, OrderMapper, OrderService, OrderServiceImpl, beans.xml.

		<!--JDOM 是一种使用 XML 的独特 Java 工具包,用于快速开发 XML 应用程序。JDOM 是一个开源项目,它基于树形结构,利用纯 Java 的技术对 XML 文档实现解析、生成、序列化及多种操作。-->
		<dependencies>
        <dependency>
            <groupId>org.jdom</groupId>
            <artifactId>jdom</artifactId>
            <version>2.0.2</version>
        </dependency>
    </dependencies>
public class OrderMapper {
    
    

    public void saveOrder(String orderNo){
    
    
        System.out.println("当前订单:"+orderNo+" 已经落入数据库。");
    }

}
public interface OrderService {
    
    

    void saveOrder(String orderNo);
}
public class OrderServiceImpl implements OrderService {
    
    

  	//注意这个OrderMapper 的 set、get方法一定要写,我们一会用反射的机制去设置属性。
    private OrderMapper orderMapper;

    public OrderMapper getOrderMapper() {
    
    
        return orderMapper;
    }

    public void setOrderMapper(OrderMapper orderMapper) {
    
    
        this.orderMapper = orderMapper;
    }

    @Override
    public void saveOrder(String orderNo) {
    
    
        orderMapper.saveOrder(orderNo);
    }
}
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="orderMapper" class="com.lixiang.mapper.OrderMapper">
    </bean>

    <bean id="orderService" class="com.lixiang.service.impl.OrderServiceImpl">
        <property name="orderMapper" bean="orderMapper" />
    </bean>
</beans>

ok, after preparing the test class, let's define the BeanFactory, and define several methods in the way of Spring.

/**
 * @author lixiang
 * @date 2023/5/22 08:26
 */
public interface BeanFactory {
    
    

    /**
     * 根据名称获取bean
     * @param name
     * @return
     */
    Object getBean(String name);

    /**
     * 判断bean是否在容器里面
     * @param name
     * @return
     */
    boolean containsBean(String name);

    /**
     * 根据名称获取bean的类型
     * @param name
     * @return
     */
    Class<?> getType(String name) ;

}

Then create ClassPathXmlApplicationContext, a context object for loading xml files under resources.

/**
 * @author lixiang
 * @date 2023/5/22 08:29
 */
public class ClassPathXmlApplicationContext implements BeanFactory{
    
    


    /**
     * 定义map用于存放bean
     */
    private Map<String,Object> beans = new HashMap<>();

    public ClassPathXmlApplicationContext() throws Exception {
    
    
        //init 方法 做 解析和注入
        init();
    }

    /**
     * 初始化,核心就在这个
     */
    private void init() throws Exception {
    
    

        SAXBuilder saxBuilder = new SAXBuilder();
        //获取资源路径的xml文件
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(fileName);
        //将xml文件构建成Document
        Document document = saxBuilder.build(resourceAsStream);
        //获取根结点的Element,对应的就是beans
        Element rootElement = document.getRootElement();
        //获取下面的bean节点
        List<Element> elements = rootElement.getChildren("bean");
        //遍历
        for (int i = 0; i < elements.size(); i++) {
    
    
            Element element = elements.get(i);
            //获取id属性
            String id = element.getAttributeValue("id");
            //获取class信息
            String clazz = element.getAttributeValue("class");
            //反射创建对象
            Object obj = Class.forName(clazz).getDeclaredConstructor().newInstance();
            //放到map中存储
            beans.put(id, obj);
            //判断属性中是否有需要注入的对象
            for(Element propertyElement: element.getChildren("property")){
    
    
                //获取名称
                String name = propertyElement.getAttributeValue("name");
                //获取bean的定义
                String bean = propertyElement.getAttributeValue("bean");
                //获取bean
                Object beanObject = beans.get(bean);
                //设置set方法
                String methodName = "set" + name.substring(0,1).toUpperCase() + name.substring(1);
                Class<?> aClass = beanObject.getClass();
                //反射将属性设置进去
                Method m = obj.getClass().getMethod(methodName,aClass);
                m.invoke(obj, beanObject);
            }
        }
    }

    @Override
    public Object getBean(String name) {
    
    
        return beans.get(name);
    }

    @Override
    public boolean containsBean(String name) {
    
    
        return beans.containsKey(name);
    }

    @Override
    public Class<?> getType(String name) {
    
    
        if(beans.containsKey(name)){
    
    
            return beans.get(name).getClass();
        }
        return null;
    }
}

Create the test main function, written in the way Spring created before.

/**
 * @author lixiang
 * @date 2023/5/22 08:11
 */
public class Main {
    
    
    public static void main(String[] args) throws Exception {
    
    

        String fileName= "beans.xml";
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(fileName);

        OrderService orderService = (OrderService)applicationContext.getBean("orderService");
        orderService.saveOrder("202388474754758834");
        boolean containsBean = applicationContext.containsBean("orderService");
        System.out.println(containsBean);
        Class<?> type = applicationContext.getType("orderService");
        System.out.println(type.getName());

    }
}

Let's look at the running results first, there is no problem, the object is created, and the method is called.

insert image description here

Then let's look at debug to see how it is executed. The core logic lies in the init method.

insert image description here
insert image description here
insert image description here
insert image description here
Top-level classes in SpringBeanFactory

  • One of the core interfaces, mainly responsible for creating and managing bean object instances, including defining, loading, instantiating and maintaining dependencies between beans, etc.
  • main effect
    • Load Bean configuration information: BeanFactory constructs Bean instances according to the Bean information defined in the XML file, and loads them into the container.
    • Instantiate the Bean: After the Bean definition information is loaded, the BeanFactory uses the Java reflection mechanism to instantiate the Bean, and assembles the Bean instance according to the dependency relationship.
    • Maintain dependencies between beans: BeanFactory can automatically identify dependencies between beans and implement dependency injection of beans
    • Provide a unified configuration method: BeanFactory can put together the configuration information of all beans and provide a unified configuration entry.
    • Scope management of beans: BeanFactory is responsible for managing the scope of beans, such as: single case, multiple cases, etc.

BeanFactory is just an interface, not a specific implementation of the IOC container. The Spring container provides many implementations.

  • ClassPathXmlApplicationContextUse XML configuration
  • AnnotationConfigApplicationContextUse annotation configuration
  • ApplicationContextConfigurableApplicationContext
  • BeanFactoryListableBeanFactory
  • Summarize
    • BeanFactory is the top-level interface, which defines most of the most basic APIs, called [basic container]
    • Corresponding to the subclass ApplicationContext of BeanFactory, more functions can be expanded based on different requirements, called [advanced container]
    • This design prevents all functions from being concentrated in one class and distributed to different interfaces. When implementing, you can choose according to your needs.

insert image description here

Guess you like

Origin blog.csdn.net/weixin_47533244/article/details/130807401