Design of IoC container (implemented using reflection, annotation and factory pattern)

1. Experimental requirements

  1. Design a simple IoC container using annotations, reflection and factory patterns
  2. The IoC container contains 3 annotations and an IoC container class (AnnotationConfigApplicationContext), which is defined as follows:

annotation:

annotation meaning
@Component Annotation Bean
@Autowired Annotate objects that need to be injected
@Configuration Annotated as a configuration class
@ComponentScan annotation scanner

IoC container class:

        3. Customize two business classes Group and User, create a test class Test, and test the IoC container:

2. Experiment ideas

This experiment requires us to simulate and implement the basic functions of the spring framework IoC container class through the design of reflection and custom annotations. The annotations to be implemented include @Component, @Autowired, @Configuration and @ComponentScan

First of all, we need to clarify what the functions of these annotations are in the spring framework:

  1. @Component: Instantiate the entity class into the container
  2. @ComponentScan: configure the package path, go to the path to find beans
  3. @Autowired: Automatic assembly (defining a variable to receive injected classes)
  4. @Configuration: identified as a configuration class

So in this experiment, we need to customize four annotations, then annotate the Group and User classes with @Component, create an instantiated object of the Group class in the User class and set it to automatic assembly, so that it can be used in the User class Call the method of the Group class;

Then we need to implement an IoC container class by ourselves to handle the basic logic of custom annotations;

Next, instantiate the IoC container in the test class, get the bean from it, and call its method

3. Experiment code

The pom file depends on the configuration:

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.14.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>

jdk version: 1.8

Development tools: IDEA

3.1 Custom annotations

@Autowired:

//自定义注解:标注需要被注入的对象
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}

@Component:

//自定义注解:标注Bean
@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value() default "";
}

@ComponentScan:

//自定义注解 注解扫描器
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}

@Configuration:

//自定义注解 标注为配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {
}

3.2 Define Entity Classes

Group:

@Component
public class Group {
    public String getBeanName()
    {
        return this.getClass().getSimpleName();
    }
}

User:

@Component
public class User {
    @Autowired
    private Group group;

    public void print()
    {
        System.out.println("group name as follow:");
        System.out.println(group.getBeanName());
    }

}

3.3 Define configuration class

MyConfig:

@Configuration
@ComponentScan("org.example")
public class MyConfig {
}

3.4 Define the IoC container class

Interface: ApplicationContext

public interface ApplicationContext {
    Object getBean(Class clazz);
}

Implementation class: AnnotationApplicationContext

public class AnnotationApplicationContext implements ApplicationContext {

    //定义Map,用于存放bean对象
    private Map<String, Object> beanFactory = new HashMap<>();

    //创建IOC容器类时执行的方法
    public AnnotationApplicationContext(Class configClass) throws InstantiationException, IllegalAccessException {
        //处理@Configuration的逻辑(没有实质性的内容,只是要求必须有配置类)
        try {
            //获取@Configuration类型的对象
            Configuration configuration = (Configuration) configClass.getAnnotation(Configuration.class);
            //如果对象为空,则引发异常
            if (configuration.annotationType().toGenericString().equals("")) {
                //没有具体逻辑
            }
        } catch (Exception e) {
            System.out.println("找不到配置类");
            System.exit(-1);
        }

        //处理@ComponentScan的逻辑
        /*
        总的来说,就是扫描某一路径下的所有类,如果有的类被Component注解,则创建其实例(一个bean),加入beanFactory中
        如果有的类中的对象(一个Field类型的数据)被@Autowired注解标注,则用beanFactory中同类型的bean替代该对象
        */
        try {
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);

            //根据componentScanValue的值来获取要扫描的路径(默认值处理的不是很好)
            String componentScanValue = componentScan.value().toString();
            String path = "";
            if (!componentScanValue.equals(""))
            {
                String[] splitValue = componentScanValue.split("\\.");
                for (int i = 0; i < splitValue.length; i++) {
                    path += splitValue[i] + "/";
                }
                path = "classpath*:" + path + "**/*.class";
            }
            else {
                path = "classpath*:org/example/**/*.class";
            }
            //扫描路径,获取所有的class
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources(path);
            for (Resource res : resources) {
                //获取类名
                String clsName = new SimpleMetadataReaderFactory().getMetadataReader(res).getClassMetadata().getClassName();
                //获取类对象
                Class thisClass = Class.forName(clsName);

                //判断当前类是否被Component注解标注
                Component component = (Component) thisClass.getAnnotation(Component.class);
                if (component != null) //说明该类被Component注解标注
                {
                    Constructor c = thisClass.getConstructor();
                    Object o = c.newInstance(); //创建该类的实例
                    beanFactory.put(o.getClass().getSimpleName(), o);//加入beanFactory中
                }

                //处理@Autowired注解
                for (Object bean : beanFactory.values()) { //查看beanFactory中所有的bean
                    Class beanClass = bean.getClass();
                    Field[] beanFields = beanClass.getDeclaredFields(); //获取bean的fields
                    for (Field field : beanFields) {
                        if (field.isAnnotationPresent(Autowired.class)) { //如果被Autowired注解标注
                            Object beanD = beanFactory.get(field.getType().getSimpleName()); //获取beanFactory中的bean
                            field.setAccessible(true); //关闭安全检查
                            field.set(bean, beanD); //用beanFactory中的bean来代替当前bean
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("没有ComponentScan注解");
            System.exit(-1);
        }
    }

    //getBean方法,获取某个bean(通过class)
    @Override
    public Object getBean(Class beanClass) {
        return beanFactory.get(beanClass.getSimpleName());
    }

}

3.5 Test class

public class test {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        //创建IOC容器,传入的参数是当前的配置类
        //因此可以根据@Configuration和@ComponentScan("org.example")来走接下来的逻辑
        AnnotationApplicationContext context = new AnnotationApplicationContext(MyConfig.class);
        //从容器中获得user对象
        User user = (User) context.getBean(User.class);
        //执行对象方法
        //可以看到通过user对象执行了group类的方法getBeanName()
        //但user类中,我们并没有实例化group对象,可以看到是通过注解来实现的
        user.print();
    }
}

4. Experimental results

The method of the group class is successfully called in the user class;

5. Source code

The above code is complete, and the project file is attached below:

https://download.csdn.net/download/qq_51235856/87944324

Guess you like

Origin blog.csdn.net/qq_51235856/article/details/131365575