手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器,实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象

前言
手动实现 Spring 底层机制的第三篇实现了编写自己 Spring 容器,实现扫描包, 得到 bean 的 class 对象 实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象

个人主页:尘觉主页

个人简介:大家好,我是尘觉,希望我的文章可以帮助到大家,您的满意是我的动力

在csdn获奖荣誉: csdn城市之星2名
⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ Java全栈群星计划top前5
⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣  端午大礼包获得者

欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,感谢大家的观看
如果文章有什么需要改进的地方还请大佬不吝赐教 先在次感谢啦

文章目录

  • 手动实现Spring机制
    • Spring 整体架构分析
  • 实现任务阶段 1
    • 知识扩展:类加载器
    • 说明: 编写自己 Spring 容器,实现扫描包, 得到 b得到 bean的class对象
      • ● 分析示意图
      • 创建ComponentScan.java注解
      • 创建Component注解
      • 创建 WyxSpringConfig类
      • 创建 MonsterService.java类
      • 创建MonsterDao类
      • 创建WyxSpringApplicationContext类
      • 创建AppMain
      • 完成测试,输出效果
  • 实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象, 并放入到 Map
    • ● 分析示意图
      • 创建Scope.java注解
      • 修改,增加@ScopeMonsterService.java类
      • 创建BeanDefinition类
      • 修改WyxSpringApplicationContext类
      • 完成测试,输出效果
    • 总结

手动实现Spring机制

Spring 整体架构分析

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器,实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象_第1张图片

实现任务阶段 1

编写自己 Spring 容器,实现扫描包, 得到 bean 的 class 对象

知识扩展:类加载器

● java 的类加载器 3 种
Bootstrap 类加载器--------------对应路径 jre/lib
Ext 类加载器--------------------对应路径 jre/lib/ext
App 类加载器-------------------对应路径 classpath

● classpath 类路径,就是 java.exe 执行时,指定的路径,比如

img

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器,实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象_第2张图片

说明: 编写自己 Spring 容器,实现扫描包, 得到 b得到 bean的class对象

● 分析示意图

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器,实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象_第3张图片

创建ComponentScan.java注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    //通过value可以指定要扫描的包
    String value() default "";
}

创建Component注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    //通过value可以给注入的bean/对象指定名字
    String value() default "";
}

创建 WyxSpringConfig类

@ComponentScan(value = "com.wyxdu.spring.component")
public class WyxSpringConfig {
}

创建 MonsterService.java类

@Component("monsterService")
public class MonsterService {
}

创建MonsterDao类

@Component("monsterDao")
public class MonsterDao {
}

创建WyxSpringApplicationContext类

\1. 解析配置类

\2. 获取到配置类的 @ComponentScan(“com.Wyxedu.spring.component”)

\3. 获取扫描路径下所有的类文件

​ 3.1 先得到类加载器, 使用 App 方式来加载.

​ ClassLoader classLoader=WyxSpringApplicationContext.class.getClassLoader();

​ 3.2 将 path 转成 形式为 com/Wyxedu/spring/component

​ 通过类加载器获取来类文件的 Clazz 对象
​ 先 得 到 类 的 完 整 类 路 径 形 式 为com.Wyxedu.spring.component.MonsterService

public class WyxSpringApplicationContext {
    private Class configClass;
    public WyxSpringApplicationContext(Class configClass) {
        this.configClass = configClass;
        //1. 解析配置类
        //2. 获取到配置类的 @ComponentScan("com.Wyxedu.spring.component")
        ComponentScan componentScan = (ComponentScan)
                this.configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
        System.out.println("扫描路径 = " + path);
            //3. 获取扫描路径下所有的类文件
           //(1) 先得到类加载器, 使用 App 方式来加载. ClassLoader classLoader = WyxSpringApplicationContext.class.getClassLoader();
             //在获取某个包的 d 对应的 URL 时,要求是 com/Wyxedu/spring/component
            //URL resource = classLoader.getResource("com/Wyxedu/spring/component");
            //(2) 将 path 转成 形式为 com/Wyxedu/spring/component
        path = path.replace(".", "/");
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());
        if(file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                String fileAbsolutePath = f.getAbsolutePath();
                System.out.println("=======================================");
                System.out.println("文件绝对路径 = " + fileAbsolutePath);
                if(fileAbsolutePath.endsWith(".class")) {//说明是类文件
                //通过类加载器获取来类文件的 Clazz 对象
                // 先 得 到 类 的 完 整 类 路 径 形 式 为com.Wyxedu.spring.component.MonsterService
                    String className =
                            fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\")
                                    + 1, fileAbsolutePath.indexOf(".class"));
                    String classFullPath = path.replace("/", ".") + "." + className;
                    System.out.println("类名 = " + className);
                    System.out.println("类的全路径 = " + classFullPath);
                    try {
                            //获取到扫描包下的类的 clazz 对象
                            Class<?> clazz = classLoader.loadClass(classFullPath);
                         
                            if(clazz.isAnnotationPresent(Component.class)) {
                                  //如果这个类有@Commponent, 说明是一个 spring bean
                                System.out.println("是一个 bean = " + clazz);
                            } else {
                               //如果这个类没有@Commponent, 说明不是一个 spring bean
                            System.out.println("不是一个 bean = " + clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                    System.out.println("=======================================");
                }
            }
        }
    }
    public Object getBean(String name) {
        return null;
    }
}

创建AppMain

public class AppMain {
    public static void main(String[] args) {
            //创建我们的 spring 容器对象
        WyxSpringApplicationContext wyxSpringApplicationContext =
                new WyxSpringApplicationContext(WyxSpringConfig.class);
    }
}

完成测试,输出效果

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器,实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象_第4张图片

实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象, 并放入到 Map

● 分析示意图

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器,实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象_第5张图片

创建Scope.java注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    //通过value可以指定singleton,prototype
    String value() default "";
}

修改,增加@ScopeMonsterService.java类

@Component("monsterService")
@Scope("prototype")
public class MonsterService{}

创建BeanDefinition类

/**
* BeanDefinition 用于封装/记录Bean的信息[
1. scope 
2 Bean对应的Class对象, 反射可以生对应的对象]
 */
public class BeanDefinition {
    private String scope;
    private Class clazz;
    //可以根据需求,进行扩展

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    @Override
    public String toString() {
        return "BeanDefinition{" +
                "scope='" + scope + '\'' +
                ", clazz=" + clazz +
                '}';
    }
}

修改WyxSpringApplicationContext类

将扫描到的 Bean 信息封装到 BeanDefinition 对象中,并保存到map中

public class WyxSpringApplicationContext {
    private Class configClass;
    //如果 bean 是单例的,就直接放在这个 单例 bean 对象池
    private ConcurrentHashMap<String,Object> singletonObjects =
new ConcurrentHashMap<>();
    //将 bean 的定义,放在这个 beanDefinitionMap 集合
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();
    public WyxSpringApplicationContext(Class configClass) {
        //通过扫描,得到 beanDefinition 的 map
        beanDefinitionsByscan(configClass);
        System.out.println(beanDefinitionMap);
    }
    private void beanDefinitionsByscan(Class configClass) {
        this.configClass = configClass;
        //1. 解析配置类
        //2. 获取到配置类的 @ComponentScan("com.Wyxedu.spring.component")
        ComponentScan componentScan = (ComponentScan)
                this.configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
        System.out.println("扫描路径 = " + path);
        //3. 获取扫描路径下所有的类文件
        //(1) 先得到类加载器, 使用 App 方式来加载. ClassLoader classLoader = WyxSpringApplicationContext.class.getClassLoader();
        //在获取某个包的 d 对应的 URL 时,要求是 com/Wyxedu/spring/component
        //URL resource = classLoader.getResource("com/Wyxedu/spring/component");
        //(2) 将 path 转成 形式为 com/Wyxedu/spring/component
        path = path.replace(".", "/");
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());
        if(file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                String fileAbsolutePath = f.getAbsolutePath();
                System.out.println("=======================================");
                System.out.println("文件绝对路径 = " + fileAbsolutePath);
                if(fileAbsolutePath.endsWith(".class")) {//说明是类文件
                    //通过类加载器获取来类文件的 Clazz 对象
                    // 先 得 到 类 的 完 整 类 路 径 形 式 为
                    com.wyxedu.spring.component.MonsterService
                    String className =
                            fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\")
                                    + 1, fileAbsolutePath.indexOf(".class"));
                    String classFullPath = path.replace("/", ".") + "." + className;
                    System.out.println("类名 = " + className);
                    System.out.println("类的全路径 = " + classFullPath);
                    try {
                        //获取到扫描包下的类的 clazz 对象
                        Class<?> clazz = classLoader.loadClass(classFullPath);
                        if(clazz.isAnnotationPresent(Component.class)) {
                            //如果这个类有@Commponent, 说明是一个 spring bean
                            System.out.println("是一个 bean = " + clazz);
                            //解读
                            //1. 因为这里不能直接将 bean 实例放入 singletonObjects
                            //2. 原因是如果 bean 是prototype是需要每次创建新的 bean对象
                            //3. 所以,Spring 底层是这样设计的: 将 bean 信息封装到
                            BeanDefinition 对象中, 便于 getBean 的操作
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            //获取 bean 的 name
                            Component componentAnnotation =
                                    clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            //获取 bean 的 scope
                            if(clazz.isAnnotationPresent(Scope.class)) { // 如 果 有
                                @Scope
                                Scope scopeAnnotation =
                                        clazz.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else { //如果没有@Scope, 默认是 singleton
                                beanDefinition.setScope("singleton");
                            }
                            //放入到 beanDefinitionMap
                            beanDefinitionMap.put(beanName, beanDefinition);
                        } else {
                            //如果这个类没有@Commponent, 说明不是一个 spring bean
                            System.out.println("不是一个 bean = " + clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                    System.out.println("=======================================");
                }
            }
        }
    }
    public Object getBean(String name) {
        return null;
    }
}

完成测试,输出效果

img

总结

本文完成了实现任务阶段 一 编写自己 Spring 容器,实现扫描包, 得到 bean 的 class 对象和实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象, 并放入到 Map下一阶段为 初始化 bean

手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】系列

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【1】

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【2】

热门专栏推荐
想学习vue的可以看看这个
java基础合集
数据库合集
redis合集
nginx合集
linux合集
等等等还有许多优秀的合集在主页等着大家的光顾感谢大家的支持

欢迎大家加入我的社区 尘觉社区

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力

你可能感兴趣的:(#,手写机制,spring,后端,java,tomcat,spring,cloud,spring,boot,java-ee)