Spring-手写模拟Spring底层原理

概述

模拟大致的底层原理,为学习Spring源码做铺垫。

实现的功能:扫描路径、依赖注入、aware回调、初始化前、初始化、初始化后、切面

未实现的功能:构造器推断、循环依赖

重点:BeanDefinition、BeanPostProcessor

学习Spring源码的重点:设计模式、编码规范、设计思想、扩展点

启动类:

md-end-block 复制代码
public class Yeah
{
    public static void main(String[] args)
    {
        GaxApplicationContext gaxApplicationContext = new GaxApplicationContext(AppConfig.class);
        UserInterface userService = (UserInterface) gaxApplicationContext.getBean("userService");
        userService.test();
    }
}

关键方法:

md-end-block 复制代码
public class GaxApplicationContext
{
    private Class configClass;
    
    private static final String SINGLETON = "singleton";
    
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    
    private Map<String, Object> singletonObjects = new HashMap<>();
    
    // Spring源码用的是LinkedList
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
    
    public GaxApplicationContext(Class configClass)
    {
        this.configClass = configClass;
        
        // 扫描指定路径,找到所有@Component注解的类,封装成beanDefinition保存再Map中
        scan(configClass);
        
        // 思考个问题:beanDefinitionMap.keySet()和beanDefinitionMap.entrySet()两种遍历的区别?选用哪个好?
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet())
        {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            if (SINGLETON.equals(beanDefinition.getScope()))
            {
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }
    }
    
    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()));
                }
            }
            
            // aware回调
            if (instance instanceof BeanNameAware)
            {
                ((BeanNameAware)instance).setBeanName(beanName);
            }
            
            // 初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList)
            {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }
            
            // 初始化
            if (instance instanceof InitializingBean)
            {
                ((InitializingBean)instance).afterPropertiesSet();
            }
            
            // 初始化后
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList)
            {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }
        }
        catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e)
        {
            e.printStackTrace();
        }
        
        return instance;
    }
    
    public Object getBean(String beanName)
    {
        if (!beanDefinitionMap.containsKey(beanName))
        {
            throw new NullPointerException();
        }
        
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (SINGLETON.equals(beanDefinition.getScope()))
        {
            Object singletonBean = singletonObjects.get(beanName);
            if (null == singletonBean)
            {
                singletonBean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, singletonBean);
            }
            return singletonBean;
        }
        else
        {
            // 原型Bean
            Object prototypeBean = createBean(beanName, beanDefinition);
            return prototypeBean;
        }
    }
    
    private void scan(Class configClass)
    {
        if (configClass.isAnnotationPresent(ComponentScan.class))
        {
            ComponentScan componentScanAnnotation = (ComponentScan)configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value();
            // path = com/gax/service
            path = path.replace(".", "/");
            
            ClassLoader classLoader = GaxApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            assert resource != null;
            File file = new File(resource.getFile());
            
            if (file.isDirectory())
            {
                for (File f : Objects.requireNonNull(file.listFiles()))
                {
                    String absolutePath = f.getAbsolutePath();
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                    // 类加载器入参需要的格式:com.gax.service.XXX
                    absolutePath = absolutePath.replace("\\", ".");
                    
                    try
                    {
                        Class<?> clazz = classLoader.loadClass(absolutePath);
                        if (clazz.isAnnotationPresent(Component.class))
                        {
                            if (BeanPostProcessor.class.isAssignableFrom(clazz))
                            {
                                BeanPostProcessor instance = (BeanPostProcessor)clazz.getConstructor().newInstance();
                                beanPostProcessorList.add(instance);
                            }
                            
                            Component componentAnnotation = clazz.getAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            if ("".equals(beanName))
                            {
                                // 默认beanName
                                beanName = Introspector.decapitalize(clazz.getSimpleName());
                            }
                            
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setType(clazz);
                            
                            if (clazz.isAnnotationPresent(Scope.class))
                            {
                                Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                beanDefinition.setScope(value);
                            }
                            else
                            {
                                // 默认单例Bean
                                beanDefinition.setScope(SINGLETON);
                            }
                            beanDefinitionMap.put(beanName, beanDefinition);
                        }
                    }
                    catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException
                        | InstantiationException | IllegalAccessException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

gitee地址:

md-end-block 复制代码
git clone https://gitee.com/seek6174/spring-seek.git
相关推荐
夜白宋32 分钟前
【word多文档docx合并】
java·word
@yanyu6661 小时前
idea中配置tomcat
java·mysql·tomcat
2501_916766541 小时前
【项目部署】JavaWeb、MavenJavaWeb项目部署至 Tomcat 的实现方式
java·tomcat
RoboWizard1 小时前
扩容刚需 金士顿新款Canvas Plus存储卡
java·spring·缓存·电脑·金士顿
lang201509281 小时前
Spring Boot 入门:5分钟搭建Hello World
java·spring boot·后端
失散131 小时前
分布式专题——47 ElasticSearch搜索相关性详解
java·分布式·elasticsearch·架构
serve the people2 小时前
LangChain 表达式语言核心组合:Prompt + LLM + OutputParser
java·langchain·prompt
想ai抽2 小时前
深入starrocks-多列联合统计一致性探查与策略(YY一下)
java·数据库·数据仓库
武子康2 小时前
Java-152 深入浅出 MongoDB 索引详解 从 MongoDB B-树 到 MySQL B+树 索引机制、数据结构与应用场景的全面对比分析
java·开发语言·数据库·sql·mongodb·性能优化·nosql
杰克尼2 小时前
JavaWeb_p165部门管理
java·开发语言·前端