Spring Framework (8) - two hundred lines of code for simple IoC container

I. Introduction

Spring has two important functions, one AOP (Aspect Oriented Programming), the main use of dynamic agent technology

Java Basics (9) - reflecting the dynamic agent

Second IOC (Inversion of Control) technique. Inversion of Control will be created rights object to the Spring IOC container to manage, developers only need to focus on to create a top-level class, without regard to the various dependencies underlying class create a relationship, but they are handed over to Spring IOC to create.

The technique used herein, reflective mode parsing techniques + dom4j profile + xml simple operation of the IOC, the IOC analog manner using setter injection method.

II. Timing Diagram

Finally, example operation of obtaining the reflection are achieved by XmlApplicationContext; BeanUtils for parsing xml configuration, and configuring the mapping and Property of Bean Good
Here Insert Picture Description

III. Code implementation

  • Code the overall structure of
    the core code is located XmlApplicationContext and BeanUtils, a total of more than two hundred lines of code
    Here Insert Picture Description

  • beans bag
    beans bean bag to store the required management by the IOC

/**
 * @Auther: ARong
 * @Date: 2020/2/13 12:51 下午
 * @Description: Person类
 */
@Data
@ToString
public class Person {
    private String name;
    private Student student;
    public Person() {
        System.out.println(new Date() + ":Person类被创建");
    }
}


/**
 * @Auther: ARong
 * @Date: 2020/2/13 12:55 下午
 * @Description: Student类
 */
@Data
@ToString
public class Student {
    private String name;
    public Student() {
        System.out.println(new Date() + ":Student类被创建");
    }
}
  • xml packet
    stored under the applicationContext.xml xml package, IOC container where all classes and arranged to register
<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean name="person" class="spring_demo.my_ioc.beans.Person" scope="prototype">
        <properties>
            <property name="name" value="ARong"/>
            <property name="student" ref="student"/>
        </properties>
    </bean>

    <bean name="student" class="spring_demo.my_ioc.beans.Student">
        <properties>
            <property name="name" value="XiaoMing"/>
        </properties>
    </bean>

</beans>
  • utils package
    the package defines utils Property and Bean, are required to describe the bean class information and the attribute information, the program uses a reflective easily created instance bean

  • Bean

/**
 * @Auther: ARong
 * @Date: 2020/2/13 1:00 下午
 * @Description: 描述对象实例的抽象Bean
 */
@Data
@ToString
public class Bean {
    private String name;// 类名
    private String classpath;// 类全限定路径
    private String scope = "singleton";// 默认单例
    private List<Property> properties = new ArrayList<>();// 所含属性
}
  • Property
/**
 * @Auther: ARong
 * @Date: 2020/2/13 1:03 下午
 * @Description: 描述类所含属性的抽象
 */
@Data
@ToString
public class Property {
    private String name;// 属性名
    private String value;// 属性值
    private String ref;// 引用对象
}

Further, BeanUtils for packaging configuration for the parsed information xml

  • BeanUtils
/**
 * @Auther: ARong
 * @Date: 2020/2/13 1:09 下午
 * @Description: Bean操作的工具类
 */
public class BeanUtils {
    public static final BeanUtils DEFAULT = new BeanUtils();
    private BeanUtils() {}

    /*
     * @Author ARong
     * @Description 使用dom4j解析xml文件内容,封装对应的bean与相应属性
     * @Date 2020/2/13 1:12 下午
     * @Param [xmlPath]
     * @return java.util.Map<java.lang.String,spring_demo.my_ioc.utils.Bean>
     **/
    public Map<String, Bean> getBeanConfig(String xmlPath) {
        HashMap<String, Bean> map = new HashMap<>();
        SAXReader saxReader = new SAXReader();
        Document document = null;
        try {
            document = saxReader.read(xmlPath);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        Iterator<Element> beans = document.getRootElement().elementIterator();
        while (beans.hasNext()) {
            Element beanElement = beans.next();
            // 封装bean
            Bean bean = new Bean();
            bean.setName(beanElement.attributeValue("name"));
            bean.setClasspath(beanElement.attributeValue("class"));
            String scope = beanElement.attributeValue("scope");
            if (scope != null) {
                bean.setScope(scope);
            }
            // 封装属性
            ArrayList<Property> proList = new ArrayList<>();
            Iterator<Element> properties = beanElement.element("properties").elementIterator();
            while (properties.hasNext()) {
                Element propertyElement = properties.next();
                Property property = new Property();
                property.setName(propertyElement.attributeValue("name"));
                String value = propertyElement.attributeValue("value");
                String ref = propertyElement.attributeValue("ref");
                if (value != null) {
                    property.setValue(value);
                }
                if (ref != null) {
                    property.setRef(ref);
                }
                proList.add(property);
            }
            bean.setProperties(proList);
            map.put(bean.getName(), bean);
            // System.out.println(bean);
        }
        return map;
    }

}
  • context packet
    context package defines XmlApplicationContext BeanFactory interface and implementation class, for instance as obtained by the IOC container and method class getBean
  • BeanFactory
public interface BeanFactory {
    /*
     * @Author ARong
     * @Description 根据name获取对象的实例
     * @Date 2020/2/13 1:57 下午
     * @Param [name]
     * @return java.lang.Object
     **/
    Object getBean(String name);
}
  • XmlApplicationContext
/**
 * @Auther: ARong
 * @Date: 2020/2/13 2:03 下午
 * @Description:
 */
@Data
public class XmlApplicationContext implements BeanFactory {
    private Map<String, Bean> beanMap;
    private static final Map<String, Object> context = new HashMap<>();

    private XmlApplicationContext() {
    }

    public XmlApplicationContext(String xmlPath) {
        beanMap = BeanUtils.DEFAULT.getBeanConfig(xmlPath);
        // 将单例对象先创建并放入context中
        Set<Map.Entry<String, Bean>> entries = beanMap.entrySet();
        for (Map.Entry<String, Bean> entry : entries) {
            String key = entry.getKey();
            Bean value = entry.getValue();
            if ("singleton".equals(value.getScope())) {
                context.put(key, createBean(value));
            }
        }
    }

    /*
     * @Author ARong
     * @Description 通过beanName获取Object
     * @Date 2020/2/13 2:08 下午
     * @Param [name]
     * @return java.lang.Object
     **/
    @Override
    public Object getBean(String name) {
        Object instance = context.get(name);
        if (instance == null) {
            // 未创建的多例对象,每次都新创建
            instance = createBean(beanMap.get(name));
        }
        return instance;
    }

    /*
     * @Author ARong
     * @Description 通过反射创建相应的对象
     * @Date 2020/2/13 2:13 下午
     * @Param [name, beanMap, context]
     * @return java.lang.Object
     **/
    public Object createBean(Bean bean) {
        // 获取类全限定名
        String classpath = bean.getClasspath();
        Class<?> beanClass = null;
        try {
            beanClass = Class.forName(classpath);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // 创建class对象实例
        Object beanInstance = null;
        try {
            beanInstance = beanClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // 获得Bean的属性,将其注入
        if(bean.getProperties() != null){
            for(Property prop : bean.getProperties()){
                // 获得要注入的元素名称
                String proName = prop.getName();
                // 根据属性名称获得对应属性的set方法
                Method setMethod = getSetMethod(beanInstance, proName);

                Object param = null;
                if(prop.getValue() != null){
                    // value属性注入
                    param = prop.getValue();

                }
                if(prop.getRef() != null){
                    // bean引用注入
                    //要注入其他bean到当前bean中,先从容器中查找,当前要注入的bean是否已经创建并放入容器中
                    Object existBean = context.get(prop.getRef());
                    if(existBean == null){
                        // 容器中不存在要注入的bean,创建该bean
                        existBean = createBean(beanMap.get(prop.getRef()));
                        // 将创建好的单例bean放入容器中
                        if("singleton".equals(beanMap.get(prop.getRef()).getScope())) {
                            context.put(prop.getRef(), existBean);
                        }
                    }
                    param = existBean;
                }

                try {
                    // 调用set方法注入该属性
                    setMethod.invoke(beanInstance, param);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        return beanInstance;
    }

    /*
     * @Author ARong
     * @Description 通过反射获取到实例的变量setter方法
     * @Date 2020/2/13 2:32 下午
     * @Param [beanInstance, proName]
     * @return java.lang.reflect.Method
     **/
    private Method getSetMethod(Object beanInstance, String proName) {
        Class<?> beanClass = beanInstance.getClass();
        Method setterMathod = null;
        // 先获取方法参数类型
        Class<?> type = null;
        try {
            type = beanClass.getDeclaredField(proName).getType();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        String begin = proName.substring(0, 1).toUpperCase();
        String methodName = "set" + begin + proName.substring(1);
        // 获取setter方法
        try {
            setterMathod  = beanClass.getDeclaredMethod(methodName, type);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return setterMathod;
    }
}
  • test package
    for simple testing IOC
  • TestIOC
/**
 * @Auther: ARong
 * @Date: 2020/2/13 1:24 下午
 * @Description: 测试IOC容器
 */
public class TestIoC {
    @Test
    public void testIOC() {
        // 1.初始化IOC容器
        System.out.println("==========1.初始化IOC容器==========");
        XmlApplicationContext context = new XmlApplicationContext("/Users/arong/MyFile/Project/Algorithm/src/main/java/spring_demo/my_ioc/xml/applicationContext.xml");

        // 2.测试注入在Student中注入name属性
        System.out.println("==========2.测试注入在Student中注入name属性==========");
        testGetBean1(context);

        // 3.测试在Person中注入student引用以及name属性
        System.out.println("==========3.测试在Person中注入student引用以及name属性==========");
        testGetBean2(context);

        // 4.测试Person对象是否为多例、Student对象是否为单例
        System.out.println("==========4.测试Person对象是否为多例、Student对象是否为单例==========");
        testGetBean3(context);
    }



    private void testGetBean1(XmlApplicationContext context) {
        Student student = (Student) context.getBean("student");
        System.out.println(student+"\n");
    }

    private void testGetBean2(XmlApplicationContext context) {
        Person person = (Person) context.getBean("person");
        System.out.println(person+"\n");
    }

    private void testGetBean3(XmlApplicationContext context) {
        Student student1 = (Student) context.getBean("student");
        Student student2 = (Student) context.getBean("student");
        System.out.println("student1 == student2:" + (student1 == student2));
        Person person1 = (Person) context.getBean("person");
        Person person2 = (Person) context.getBean("person");
        System.out.println("person1 == person2:"+(person1 == person2));
    }

The output display, IOC basic function is normal, two hundred lines of code is the core code of the IOC. Bean concrete example of how to manage the life cycle? And how to configure annotations scanning method is further refined ~
Here Insert Picture Description

Published 309 original articles · won praise 205 · Views 300,000 +

Guess you like

Origin blog.csdn.net/pbrlovejava/article/details/104297340