手写一个简易版的仿Spring框架

Spring框架很庞大和复杂,为了更好的理解其实现,尝试去模仿其功能手写一个简易版本,暂时只包括简略的几个功能,为了看情况更新。

1. 包含功能

  1. @ComponentScan包体扫描
  2. @Autowire依赖注入
  3. @Scope单例实现
  4. BeanPostProcessor后置处理器
  5. InitializingBean初始化bean接口
  6. BeanNameAware名字资源设置
  7. BeanDefinition定义
  8. SingletonObjects单例池
  9. BeanDefinitionMap池
  10. 基于注解配置的ApplicationContext

2.逻辑

public class main {

    public static void main(String[] args) {
        EndwasApplicationContext context = new EndwasApplicationContext(AppConfig.class);
        EndwasService endwasService = (EndwasService) context.getBean("endwasService");
        endwasService.execute();
    }
}

和普通Spring容器使用一样,需要创建容器并传入配置类,然后去获取bean。

    public EndwasApplicationContext(Class<?> configClass) {
        if (configClass == null) {
            throw new EndwasException("configClass can not null!");
        }
        this.configClass = configClass;
        init();
    }

而容器的初始化分为三步

  • 获取配置类@ComponetScan扫描的包体 例如com.endwas.context,如果没设置就扫描该类所在包体。
  • 扫描当前文件夹和子文件夹,进行BeanDefinition创建和保存元数据。
  • 对singleton的bean创建保存进单例池singletonObjects。
  private void init() {
        // 初始化 1、扫描 -> beanDefinitionMap 2、创建到singletonObjects
        String packageName = getPackagePath();
        // 扫描
        scan(packageName);
        // 创建bean
        createBean();
    }

获取bean,如果是单例的bean只需要在单例池直接拿就好了,而非单例则获取的时候创建。两者在创建的时候都是调用createBean方法。

 private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class<?> clazz = beanDefinition.getClazz();
        Object bean = null;
        try {
            // 1.实例bean
            bean = clazz.getDeclaredConstructor().newInstance();

            // 2.依赖注入
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Object value = getBean(field.getName());
                    field.setAccessible(true);
                    field.set(bean, value);
                }
            }

            // 3.Aware回调
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            // 如果是BeanPostProcessor则不调用before/after
            if (bean instanceof BeanPostProcessor) {
                initBean(clazz, bean);
                return bean;
            }
            // 4.beanPostProcessor::postProcessBeforeInitialization
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
            }
            initBean(clazz, bean);

            // 6.beanPostProcessor::postProcessAfterInitialization aop动态代理
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
            }


        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return bean;

    }

该方法包括

  • 调用默认的构造方法创建。
  • 依赖注入
  • 如果实现了aware回调
  • 调用beanPostProcessor::postProcessBeforeInitialization
  • 初始化bean(执行postConstruct、InitializingBean)
  • 调用beanPostProcessor::postProcessAfterInitialization

这里要说明一点postConstruct是在后置处理器初始化前执行的,这里放到和afterPropertiesSet一起执行

3.源码

上传到了github上,还在不定时完善。 https://github.com/Endwas/EndwasApplicationContext

4.next

后续打算

  1. 支持resource、inject等注解。
  2. PostConstruct修改实现位置。
  3. 支持ComponentScan多路径。
end
  • 作者:Endwas(联系作者)
  • 发表时间:2021-12-26 22:27
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转博主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者名字和博客地址
  • 评论

    北斗七点半
    不错