IOC of handwritten spin framework core source code (basic chapter)

IOC of handwritten spin framework core source code

1. Use Spring framework
2. Use reflection mechanism
IOC controls the permission of reverse of controller to create objects. The objects needed by Java programmers are no longer created by programmers themselves, but are created by IOC containers

Simulate the use of IOC containers

1. Use POM XML file information

<dependencies>
        <!-- introduce Servlet rely on -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

        <!-- Import Spring rely on -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.3</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
    </dependencies>

    <!-- set up Maven of JDK The default version is 5. It needs to be manually changed to 8 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

2. Create a servlet file

/**
 * Demonstration of the return value of the specification of testing mvc pattern
 * @author liugang
 * @version 1.0
 * @date 2021/6/21 14:54
 */
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {

    private HelloService helloService = new HelloServiceImpl();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("hello  spring:"+helloService.findAll());
    }
}

3. Deploy to tomcat
4.servlet,service,dao when our requirements change, we may need to modify the Java code frequently, which is very inefficient

Static factory mode

public class BeanFactory {
    public static HelloDao getDao(){
        return new HelloDaoImpl();
    }
}

private HelloDao helloDao = BeanFactory.getDao();

The above methods can not solve our problems. When the requirements change, we still need to modify the code. How can we achieve class switching without changing the Java code?
In the way of external configuration files, Java programs only need to read configuration files, such as XML, yaml, properties and JSON

1. Define external profile

helloDao=com.southwind.dao.impl.HelloDaoImpl

The Java program reads this configuration file

/**
 * Create a static factory class
 * For our programs, we often put some things that change a lot, and we often hope to put them in
 * In some configuration files, so that we can better reduce the possibility of coupling
 * @author liugang
 * @version 1.0
 * @date 2021/6/21 15:22
 */
public class BeanFactory {

    //Configure the contents of its corresponding mapping file
    private static Properties properties;

    //Define a cache to store our instantiated objects
    private static Map<String,Object> cache  = new HashMap<>();

    static {
        properties  = new Properties();
        try {
            properties.load(BeanFactory.class.getClassLoader().getResourceAsStream("factory.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*Produce objects through factory mode*/
    public static Object getDao(String beanName){

        //First, determine whether there are bean s in the cache
        if (!cache.containsKey(beanName)){
            //At this time, we use the lock mechanism to control the related containers
            synchronized (BeanFactory.class){
                //First, judge whether there are beans in the cache. If there are no beans, we will store their corresponding beans in the corresponding cache
                if (!cache.containsKey(beanName)){
                    try {
                        String value = properties.getProperty("helloDao");
                        //Create objects using reflection mechanism
                        Class clazz = Class.forName(value);
                        Object object = clazz.getConstructor().newInstance(null);
                        cache.put(beanName,object);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return cache.get(beanName);
    }

}

3. Modify service

private HelloDao helloDao = (HelloDao) BeanFactory.getDao();

In this way, the program changes from tight coupling / strong dependence to loose coupling / weak dependence. It can still be modified after compilation, which makes the program have good expansibility
You give up the permission to create objects and give the permission to create objects to BeanFactory. The idea of giving control to others is control inversion

Use of Spring Ioc

xml and annotation. xml has been eliminated. At present, the mainstream is annotation based, and spring boot is also annotation based

/**
 * Injecting entity classes into containers
 * @author liugang
 * @version 1.0
 * @date 2021/6/22 17:04
 */
@Data
@Component
public class Account {

    @Value("1")
    private Integer id;
    @Value("liugang")
    private String name;
    @Value("20")
    private int age;

}

/**
 * @author liugang
 * @version 1.0
 * @date 2021/6/22 17:27
 */
public class SpringTest {

    public static void main(String[] args) {
        //Get the ioc container. Here is the form of control over the annotation method. Get the container under the relevant package
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.zzuli.spring.entity");
        //The names of all JavaBean s in the Spring container, and the return type is a string array.
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        //Gets the number of bean s in the Spring container
        System.out.println(applicationContext.getBeanDefinitionCount());
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
            //Get such an instantiated bean object
            System.out.println(applicationContext.getBean(beanDefinitionName));
        }
    }
}

IOC annotation based execution principle

Idea of handwritten code:
1. Customize a MyAnnotationConfigApplicationContext and pass in a package to be scanned in the constructor
2. Get all classes under this package
3. Traverse these classes, find the class with @ Component annotation, obtain its corresponding class and corresponding beanName, package it into a beanDefinition and store it in the Set set. This opportunity is the raw materials automatically loaded by IOC
4. Traverse the Set, create objects through the reflection mechanism, and detect whether the @ Value object is added to the attribute. If so, it is also necessary to assign a Value to the attribute. Then, these dynamically created objects are stored in the cache in the form of k-v
5. Provide getBean and other methods to get the corresponding bean through beanName

code implementation

An object collection of entity classes encapsulated by each annotation class and container

/**
 * The collection of objects for the container's entity classes
 * @author liugang
 * @version 1.0
 * @date 2021/6/23 9:36
 */
@Data
@AllArgsConstructor
public class BeanDefinition {
    private String beanName;
    private Class beanClass;
}

/**
 * Here are the auto loading annotations
 * @author liugang
 * @version 1.0
 * @date 2021/6/24 14:36
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {

}

/**
 * Custom container annotations
 * @author liugang
 * @version 1.0
 * @date 2021/6/23 20:41
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
     String value() default "";    //"Default" means that the corresponding default value is empty. At this time, the annotation of component can not add a value
}

/**
 * Annotation loaded by name
 * @author liugang
 * @version 1.0
 * @date 2021/6/24 14:41
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier {
    String value();
}

/**
 * @author liugang
 * @version 1.0
 * @date 2021/6/23 22:38
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
    String value();   //The default value is not added here, so that we can assign this value. When we use this annotation, we are required to assign it manually
}

Core writing

/**
 * @author liugang
 * @version 1.0
 * @date 2021/6/23 20:10
 */
public class MyAnnotationConfigApplicationContext {

    //Define a cache container
    private Map<String,Object> ioc = new HashMap<>();


    /*Construction method to encapsulate the data we want*/
    public MyAnnotationConfigApplicationContext(String pack) {
         //Traverse the package to find the target class (raw material)
         Set<BeanDefinition> beanDefinitions =  findBeanDefinitions(pack);
         /*Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
         while (iterator.hasNext()){
             BeanDefinition beanDefinition = iterator.next();
             System.out.println(beanDefinition);
         }*/
         //Create bean s from raw materials
        createObject(beanDefinitions);
        //Automatic loading function
        autowireObject(beanDefinitions);
    }

    /*Automatic loading function*/
    public void autowireObject(Set<BeanDefinition> beanDefinitions){
        Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
        while (iterator.hasNext()){
            BeanDefinition beanDefinition = iterator.next();
            //Get its corresponding bytecode file
            Class aClass = beanDefinition.getBeanClass();
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                //Traverse each element information corresponding to it
                Autowired annotation = declaredField.getAnnotation(Autowired.class);
                if (annotation!=null){
                    //Get the qualifier annotation information
                    Qualifier qualifier = declaredField.getAnnotation(Qualifier.class);
                    if (qualifier!=null){
                        //byName
                        try {
                            String beanName = qualifier.value();
                            Object bean = getBean(beanName);   //Order object corresponding to account
                            String fieldName = declaredField.getName();
                            String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
                            Method method = aClass.getMethod(methodName, declaredField.getType());
                            Object object = getBean(beanDefinition.getBeanName()); //account object
                            method.invoke(object, bean);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else {
                        //byType
                    }
                }
            }
        }
    }



    /*Method to get ioc container name*/
    public Object getBean(String beanName){
        return ioc.get(beanName);
    }



    /*Create bean s from raw materials*/
    public void createObject(Set<BeanDefinition> beanDefinitions){
        Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
        while (iterator.hasNext()){
            BeanDefinition beanDefinition = iterator.next();
            //Get the class attribute of the corresponding raw material and related objects
            Class aClass = beanDefinition.getBeanClass();
            String beanName = beanDefinition.getBeanName();
            try {
                Object object = aClass.getConstructor().newInstance();
                //Before putting the raw material into the container, we must complete the corresponding assignment operation
                //Get each variable element
                Field[] fields = aClass.getDeclaredFields();
                for (Field field : fields) {
                    Value valueAnnotation = field.getAnnotation(Value.class);
                    if (valueAnnotation!=null){
                        //Get the corresponding value
                        String value = valueAnnotation.value();
                        String fieldName = field.getName();
                        String methodName = "set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
                        Method method = aClass.getMethod(methodName, field.getType());
                        //Because the types of data formats are different, there will be many errors. Here you can complete the relevant type conversion
                        Object val = null;
                        switch (field.getType().getName()){
                            case "java.lang.Integer":
                                val = Integer.parseInt(value); break;
                            case "java.lang.String":
                                val = value; break;
                            case "java.lang.Float":
                                val = Float.parseFloat(value); break;
                        }
                        method.invoke(object,val);
                    }
                }

                ioc.put(beanName,object); //Here, the corresponding raw material object is stored in the ioc container
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /*Obtain raw materials*/
    private Set<BeanDefinition> findBeanDefinitions(String pack) {
        //1. Traverse all classes under the package
        Set<Class<?>> classes = MyTools.getClasses(pack);
        //Define a collection containing BeanDefinition
        Set<BeanDefinition> beanDefinitions =new HashSet<>();
        Iterator<Class<?>> iterator = classes.iterator();
        while (iterator.hasNext()){
            //2. Traverse these classes to find the annotated classes
            Class<?> aClass = iterator.next();
            Component componentAnno = aClass.getAnnotation(Component.class);
            if (componentAnno!=null){
                //Gets the value of the Component annotation
                String beanName = componentAnno.value();
                if ("".equals(beanName)){
                    //If the value of the annotation is empty, the lowercase value of the class name is obtained
                    String className = aClass.getName().replaceAll(aClass.getPackage().getName() + ".", "");
                    beanName = className.substring(0,1).toLowerCase()+className.substring(1);
                }
                //3. Encapsulate these into bean definitions and store them in the collection
                beanDefinitions.add(new BeanDefinition(beanName,aClass));
            }
        }

        return beanDefinitions;
    }
}

Test class writing and demonstration effect

public class MyspringTest {

    public static void main(String[] args) {
        //As soon as you start, you can see the corresponding constructor to access
        MyAnnotationConfigApplicationContext myAnnotationConfigApplicationContext = new MyAnnotationConfigApplicationContext("com.zzuli.myspring.entity");
        //Get the corresponding bean
        System.out.println(myAnnotationConfigApplicationContext.getBean("account"));
        System.out.println(myAnnotationConfigApplicationContext.getBean("myOrder"));
    }


}

The more you know, the more you don't understand. Clarify your goals and specify plans. I believe that tears will fill your eyes when others realize their dreams. I'm Xiao Liu. I'll see you next time

Keywords: Spring Framework source code

Added by The Stewart on Sun, 23 Jan 2022 04:24:06 +0200