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
相关推荐
qq_25183645713 分钟前
springboot vue3 开发实现 拼豆管理系统
java·开发语言·ai编程
caoerzhong33 分钟前
JeeWMS 开源仓库管理系统全景解读:一套 Java WMS 如何把 WMS/OMS/BMS/TMS 装进同一个系统
java·开源
曹牧43 分钟前
Java集合框架:List、ArrayList、Map 和 HashMap
java
顾城猿1 小时前
STOMP 协议
java
+VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue旅游管理系统(源码+数据库+文档)
java·开发语言·vue.js·spring boot·课程设计
需要8262 小时前
MySQL 索引 B+ 树与“查询为什么变慢了
java·spring boot·spring·spring cloud·tomcat
一 乐2 小时前
二手交易平台|基于springboot + vue二手交易平台(源码+数据库+文档)
java·数据库·vue.js·spring boot·小程序
万年咸鱼2 小时前
编译Java源程序
java
Wang's Blog2 小时前
Java 项目实战: 外卖平台-删除分类的关联校验与自定义业务异常
java·服务器·项目开发
hai_android2 小时前
Kotlin / Android 常用函数使用示例手册
android·java·kotlin