手写模拟Spring扫描底层实现

1、ApplicationContext和AppConfig

我们首先需要模拟的是spring的自动扫描机制,我们来想一下,当spring容器启动,自动扫描的时候,首先是肯定不能缺少启动类的,也就是我们的ApplicationContext,所以我们先创建一个ApplicationContext。

回想一下,我们之前手动去配置spring.xml的时候,是要通过启动配置ApplicationContext将配置文件进行读取,所以我们先要写一个spring的配置类 AppConfig,当然,我们这里并不进行一些复杂的配置。

package com.sinosoft.gov.spring;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author WQ
 * @Classname WangqingApplycation
 * @Description TODO
 * @Date 2022/11/16 5:07 下午
 */
public class WangqingApplycationContxt {

    private Class configClass;


//    BeanNameAware
    /***
     * 用来存放bean 对象 key value
     */
    private Map beanDefinitionMap = new HashMap<>();
    /****
     * 用来存单例对象的
     *
     */
    private Map singletonObjects = new HashMap<>();
    /***
     * 用来存放实现了 BeanPostProcessor 的类
     */
    private List beanPostProcessors = new ArrayList<>();

    /***
     * 创建一个Spring 容器
     * @param configClass
     */
    public WangqingApplycationContxt(Class configClass) {
        this.configClass = configClass;
        // 扫描
        scan(configClass);
        // 创建
        for (Map.Entry entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            // 如果是单例对象,就直接从单例池里拿
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, beanDefinition);
            }
        }
    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getType();
        Object instance = null;
        try {
            instance = clazz.getConstructor().newInstance();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    field.set(instance, getBean(field.getName()));
                }
            }
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();

            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    private void scan(Class configClass) {
        // 判断传入的配置类上是否添加了ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            // 获取注解的信息
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            // 获取扫描路径(就是注解上是否添加的包路径) 如com.xx.service
            String path = componentScanAnnotation.value();
            // 处理路径字符串,获取class文件的绝对路径 如 com/xx/service
            path = path.replace(".", "/");
            // 获取容器的类加载器,获取class文件的绝对路径 如 com/xx/service
            ClassLoader classLoader = WangqingApplycationContxt.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            // 将class文件封装为一个文件或者是文件夹
            File file = new File(resource.getFile());

            if (file.isDirectory()) {
                // 拿到当前文件夹中的所有文件
                for (File f : file.listFiles()) {
                    // 拿到文件的绝对路径
                    String absolutePath = f.getAbsolutePath();
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                    absolutePath = absolutePath.replace("\\", ".");
                    try {
                        Class aClass = classLoader.loadClass(absolutePath);
                        // 判断当前类是否实现 BeanPostProcessor
                        // 如果我们想在Spring容器中完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理。我们需要定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IoC容器中。
                        if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                            BeanPostProcessor instance = (BeanPostProcessor) aClass.getConstructor().newInstance();
                            beanPostProcessors.add(instance);
                            
                            // 遍历实现了BeanPostProcessor的类,执行初始化方法
                            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {

                            }
                        }

                        //
                        if (aClass.isAnnotationPresent(Component.class)) {
                            // bean
                            // 判断传入的配置类上是否添加了Component注解
                            Component componentAnnotation = aClass.getAnnotation(Component.class);
                            // 获取注解的value值 service 的名称
                            String beanName = componentAnnotation.value();
                            if ("".equals(beanName)) {
                                // 如果没设置,默认按照类名来
                                beanName = Introspector.decapitalize(aClass.getSimpleName());
                            }

                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setType(aClass);

                            //判断传入的实例,是否用了Scope注解
                            if (aClass.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = aClass.getAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                beanDefinition.setScope(value);
                            } else {
                                // 没写 默认是单例
                                beanDefinition.setScope("singleton");

                            }
                            beanDefinitionMap.put(beanName, beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }

                }
            }

        }
    }


    public Object getBean(String beanName) {
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new NullPointerException();
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        // 如果是单例,直接从单例池里拿
        if (beanDefinition.getScope().equals("singleton")) {
            Object singletonBean = singletonObjects.get(beanName);
            if (singletonBean == null) {
                // 没有 直接创建bean
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
            return singletonBean;
        } else {
            // 圆形
            Object prototypeBean = createBean(beanName, beanDefinition);
            return prototypeBean;
        }

    }
}

BeanPostProcessor 介绍及使用

该接口我们也叫后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。接口的源码如下

  public interface BeanPostProcessor {
	
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

InitializingBean

1、InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。

2、spring初始化bean的时候,如果bean实现了InitializingBean接口,会自动调用afterPropertiesSet方法。

3、在Spring初始化bean的时候,如果该bean实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertieSet()方法,然后再调用init-method中指定的方法。

spring初始化bean有两种方式
第一:实现InitializingBean接口,继而实现afterPropertiesSet的方法
第二:反射原理,配置文件使用init-method标签直接注入bean。

相同点: 实现注入bean的初始化。

不同点:
(1)实现的方式不一致。
(2)接口比配置效率高,但是配置消除了对spring的依赖。而实现InitializingBean接口依然采用对spring的依赖。

@Component
public class MyInitializingBean implements InitializingBean {
    public MyInitializingBean() {
        System.out.println("我是MyInitializingBean构造方法执行...");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("我是afterPropertiesSet方法执行...");
    }
    @PostConstruct
    public void postConstruct() {
        System.out.println("我是postConstruct方法执行...");
    }
    public void init(){
        System.out.println("我是init方法执行...");
    }
    @Bean(initMethod = "init")
    public MyInitializingBean test() {
        return new MyInitializingBean();
    }
}

手写模拟Spring扫描底层实现_第1张图片

通过启动日志我们可以看出执行顺序优先级:构造方法 > postConstruct >afterPropertiesSet > init方法。
 

你可能感兴趣的:(redis,数据结构,spring,java,后端)