A simple spring IOC container

Spring is a Java development framework. Its core content includes spring IOC, spring AOP and other components. It supports object inversion of control and automatic dependency injection

Learning Spring is something every Java developer has to do. It's just the saying that reading thousands of books is better than walking thousands of miles. So let's start practicing and improving a Spring framework. Today, we first simulate a simple ioc container implementation process. When Spring is doing ioc, first determine the scope of the container's classes, and then load all marked classes within this scope into the container, Finally, initialize these classes, generate objects and store them in containers
Steps:

  1. Get packet scan path
  2. Scan the path and store all class files under the path
  3. Initialize the object and store it in the container

All systems have an entry. The Java program is the main method of the startup class. We can add annotations on the startup class,
Use annotations to record the path of package scanning

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    //Packet scan path
    String value() default "";
}

First, create a new container ApplicationContext. When creating the container, initialize the container with the class object of the startup class as a parameter

public class ApplicationContext {
    
    private Class<?> clazz;
    
    public ApplicationContext(Class<?> clazz) {
        this.clazz = clazz;
    }

    public void run() {
        //1. Get scan path
        ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);
        String basePackage = componentScan.value();
    }
}

Add @ Component to the startup class. After startup, detect the value of the annotation and resolve it to the package path

@ComponentScan(value = "com.spring")
public class SpringApplication {

    public static void main(String[] args) {
        ApplicationContext context = new ApplicationContext(SpringApplication.class);
        context.run();
        Scanner sc = new Scanner(System.in);
        sc.next();
    }
}

We need to load all marked classes under this path into the container. The tag used here is the annotation @ Component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    //alias
    String value() default "";
}

Load the class file under the package path

public class ApplicationContext {
    
    ...

    //class object that stores the bean
    private final Map<String, Class<?>> beanClassMap = new ConcurrentHashMap<>();
    //Container for storing bean s
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    
    public void run() {
        //Scan package files
        //1. Get scan path
        ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);
        String basePackage = componentScan.value();
        String path = basePackage.replace('.', '/');
        try {
            //Get the files in this path
            Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(path);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                //Gets the File object of the package path
                File file = new File(filePath);
                File[] files = file.listFiles();
                //In order to use breadth first traversal, you need to add the folder to be scanned
                List<File> fileList = new ArrayList<>();
                for (File file1 : files) {
                    fileList.add(file1);
                }
                //breadth-first search 
                for (int i = 0; i < fileList.size(); i++) {
                    File file1 = fileList.get(i);
                    if (file1.isDirectory()) {
                        File[] files1 = file1.listFiles();
                        if (files1==null) {
                            continue;
                        }
                        for (File file2 : files1) {
                            //If there are sub files, they are directly added to the traversal queue
                            fileList.add(file2);
                        }
                        continue;
                    }else {
                        //Scan. class files
                        if (file1.getName().endsWith(".class")) {
                            String className = file1.getName().substring(0, file1.getName().length() - 6);
                            try {
                                String absolutePath = file1.getAbsolutePath();
                                int i1 = absolutePath.indexOf(path.replace("/", "\\"));
                                String substring = absolutePath.substring(i1);
                                substring = substring.replace('\\', '.').substring(0, substring.length() - 6);
                                //This is because it is found that. class files ending in $1 will appear during use, so handle it
                                if (substring.endsWith("$1")) {
                                    continue;
                                }
                                Class<?> clazz = Class.forName(substring);
                                beanClassMap.put(clazz.getCanonicalName(), clazz);
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            log.error("Packet scanning error: ", e);
        }
        //Inject class into container
        Set<Map.Entry<String, Class<?>>> entries = beanClassMap.entrySet();
        for (Map.Entry<String, Class<?>> entry : entries) {
            String beanName = entry.getKey();
            Class<?> beanClass = entry.getValue();
            //Check whether the @ Component annotation is included
            if (beanClass.isAnnotationPresent(Component.class)) {
                if (!singletonObjects.containsKey(beanName)) {
                    Object bean = null;
                    try {
                        //Simple and rough
                        bean = beanClass.newInstance();
                    } catch (InstantiationException | IllegalAccessException e) {
                        log.error("reflex bean An error occurred, beanName: {}", beanName, e);
                    }
                    singletonObjects.put(beanName, bean);
                }
            }
        }
    }
}

Then we create two new classes in the com.spring directory, one with annotations and the other without annotations

@Component
public class Test1 {
}
public class Test2 {
}

After running, you can see that beanClassMap scans all. class objects under com.spring, and then injects the object containing @ Component annotation into the container

A simple spring IOC container

Spring is a java development framework. Its core content includes spring IOC, spring AOP and other components. It supports object control inversion, dependency automatic injection and learning that spring is every Java
What developers have to do is just the saying that reading thousands of books is better than traveling thousands of miles. So let's practice and improve a Spring framework. Today, first simulate the implementation process of a simple ioc container
When performing ioc, Spring must first determine the scope of classes contained in the container, then load all marked classes within this scope into the container, and finally initialize these classes, generate objects and store them in the container
Steps:

  1. Get packet scan path
  2. Scan the path and store all class files under the path
  3. Initialize the object and store it in the container

All systems have an entry. The Java program is the main method of the startup class. We can add annotations on the startup class,
Use annotations to record the path of package scanning

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    //Packet scan path
    String value() default "";
}

First, create a new container ApplicationContext. When creating the container, initialize the container with the class object of the startup class as a parameter

public class ApplicationContext {
    
    private Class<?> clazz;
    
    public ApplicationContext(Class<?> clazz) {
        this.clazz = clazz;
    }

    public void run() {
        //1. Get scan path
        ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);
        String basePackage = componentScan.value();
    }
}

Add @ Component to the startup class. After startup, detect the value of the annotation and resolve it to the package path

@ComponentScan(value = "com.spring")
public class SpringApplication {

    public static void main(String[] args) {
        ApplicationContext context = new ApplicationContext(SpringApplication.class);
        context.run();
        Scanner sc = new Scanner(System.in);
        sc.next();
    }
}

We need to load all marked classes under this path into the container. The tag used here is the annotation @ Component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    //alias
    String value() default "";
}

Load the class file under the package path

public class ApplicationContext {
    
    ...

    //class object that stores the bean
    private final Map<String, Class<?>> beanClassMap = new ConcurrentHashMap<>();
    //Container for storing bean s
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    
    public void run() {
        //Scan package files
        //1. Get scan path
        ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);
        String basePackage = componentScan.value();
        String path = basePackage.replace('.', '/');
        try {
            //Get the files in this path
            Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(path);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                //Gets the File object of the package path
                File file = new File(filePath);
                File[] files = file.listFiles();
                //In order to use breadth first traversal, you need to add the folder to be scanned
                List<File> fileList = new ArrayList<>();
                for (File file1 : files) {
                    fileList.add(file1);
                }
                //breadth-first search 
                for (int i = 0; i < fileList.size(); i++) {
                    File file1 = fileList.get(i);
                    if (file1.isDirectory()) {
                        File[] files1 = file1.listFiles();
                        if (files1==null) {
                            continue;
                        }
                        for (File file2 : files1) {
                            //If there are sub files, they are directly added to the traversal queue
                            fileList.add(file2);
                        }
                        continue;
                    }else {
                        //Scan. class files
                        if (file1.getName().endsWith(".class")) {
                            String className = file1.getName().substring(0, file1.getName().length() - 6);
                            try {
                                String absolutePath = file1.getAbsolutePath();
                                int i1 = absolutePath.indexOf(path.replace("/", "\\"));
                                String substring = absolutePath.substring(i1);
                                substring = substring.replace('\\', '.').substring(0, substring.length() - 6);
                                //This is because it is found that. class files ending in $1 will appear during use, so handle it
                                if (substring.endsWith("$1")) {
                                    continue;
                                }
                                Class<?> clazz = Class.forName(substring);
                                beanClassMap.put(clazz.getCanonicalName(), clazz);
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            log.error("Packet scanning error: ", e);
        }
        //Inject class into container
        Set<Map.Entry<String, Class<?>>> entries = beanClassMap.entrySet();
        for (Map.Entry<String, Class<?>> entry : entries) {
            String beanName = entry.getKey();
            Class<?> beanClass = entry.getValue();
            //Check whether the @ Component annotation is included
            if (beanClass.isAnnotationPresent(Component.class)) {
                if (!singletonObjects.containsKey(beanName)) {
                    Object bean = null;
                    try {
                        //Simple and rough
                        bean = beanClass.newInstance();
                    } catch (InstantiationException | IllegalAccessException e) {
                        log.error("reflex bean An error occurred, beanName: {}", beanName, e);
                    }
                    singletonObjects.put(beanName, bean);
                }
            }
        }
    }
}

Then we create two new classes in the com.spring directory, one with annotations and the other without annotations

@Component
public class Test1 {
}
public class Test2 {
}

After running, you can see that beanClassMap scans all. class objects under com.spring, and then injects the object containing @ Component annotation into the container

A simple ioc container is complete

Keywords: Java Spring Algorithm leetcode

Added by enterume on Thu, 02 Dec 2021 00:01:39 +0200