【Spring】我抄袭了Spring,手写一套MySpring框架。。。

2023-05-16

这篇博客实现了一个简单版本的Spring,主要包括Spring的Ioc和Aop功能

文章目录

  • 这篇博客实现了一个简单版本的Spring,主要包括Spring的Ioc和Aop功能
    • 🚀@ComponentScan注解
    • ✈️@Component注解
    • 🚁在spring中ioc容器的类是ApplicationContext
    • 🚂测试类
    • 🚊MySpringConfig类,统一的配置类
    • 🚞OrderService类,一个普通的bean
      • 🚲来测试一下功能
    • 🚡@Scope注解
    • 🚟BeanDefinition 类
      • 🚠要实现我们的@scope注解,我们需要改造一下我们的代码
      • 🚜然后我们从beanDefinitionMap中实例化bean
      • 🚙我们来看一下效果
    • 🚘@Autowired注解
    • 🚗UserService类,orderservice注入该类
      • 🚗要使@Autowired注解生效,将bean实例化到一级缓存中方法需要改造一下
    • 🚕BeanPostProcessor接口
    • 🚖添加自己的BeanPostProcessor
    • 🚛Aop

🚀@ComponentScan注解

ComponentScan做的事情就是告诉Spring从哪里找到bean

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {

	String[] value() default {};

}

✈️@Component注解

@Component是spring中的一个注解,它的作用就是实现bean的注入

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {

	String value() default "";


}

🚁在spring中ioc容器的类是ApplicationContext

所以我们需要创建一个ApplicationContext,有参构造传入config的class

public class ApplicationContext {

    public ApplicationContext(Class configClass) {
    
    }
    
}

存放bean的map

public class ApplicationContext {

 	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 
    public ApplicationContext(Class configClass) {
    
    }
    
}

拿到ComponentScan的值

public class ApplicationContext {

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);


    public ApplicationContext(Class configClass) {
        //查看是否有ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String[] paths = componentScanAnnotation.value();
           
        }
    }
}

拿到该路径下所有的class文件

public class ApplicationContext {

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);


    public ApplicationContext(Class configClass) {
        //查看是否有ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String[] paths = componentScanAnnotation.value();
            for (String path : paths) {

                //拿到包路径
                ClassLoader classLoader = ApplicationContext.class.getClassLoader();
                URL resource = classLoader.getResource(path.replace(".", "/"));
                //拿到该路径下所有的class文件
                File file = new File(resource.getFile());
                if (file.isDirectory()) {
                    File[] files = file.listFiles();
                    for (File f : files) {
                     
                    }
                }
            }
        }
    }
}

注册有Component注解的bean

public class ApplicationContext {

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);


    public ApplicationContext(Class configClass) {
        //查看是否有ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String[] paths = componentScanAnnotation.value();
            for (String path : paths) {

                //拿到包路径
                ClassLoader classLoader = ApplicationContext.class.getClassLoader();
                URL resource = classLoader.getResource(path.replace(".", "/"));
                //拿到该路径下所有的class文件
                File file = new File(resource.getFile());
                if (file.isDirectory()) {
                    File[] files = file.listFiles();
                    for (File f : files) {
                        try {
                            String filePath = f.getPath();
                            //拿到com.masiyi.service.MySpringConfig
                            String sub = filePath.substring(filePath.indexOf("com"), filePath.indexOf(".class"));
                            String classes = sub.replace("\\", ".");
                            Class<?> aClass = classLoader.loadClass(classes);
                            //注册有Component注解的bean
                            if (aClass.isAnnotationPresent(Component.class)) {
                                Object bean = aClass.getDeclaredConstructor().newInstance();
                                Component component = aClass.getAnnotation(Component.class);
                                String beanName = component.value();
                                if ("".equals(beanName) || beanName == null) {
                                    singletonObjects.put(f.getName().split("\\.")[0], bean);
                                } else {
                                    singletonObjects.put(beanName, bean);
                                }
                            }
                        } catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

🚂测试类

现在我们最基础的spring的ioc已经基本实现了,我们新建一个测试类来测试

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ApplicationContext(MySpringConfig.class);

    }
}

🚊MySpringConfig类,统一的配置类

@ComponentScan("com.masiyi.service")
public class MySpringConfig {

}

🚞OrderService类,一个普通的bean

@Component
public class OrderService {
}

🚲来测试一下功能

拿到包路径

在这里插入图片描述

拿到该路径下所有的class文件

在这里插入图片描述

注册有Component注解的bean

在这里插入图片描述

注册进ioc

在这里插入图片描述

getBean方法

  public Object getBean(String beanName) {
        return this.singletonObjects.get(beanName);
    }

在这里插入图片描述

🚡@Scope注解

@Scope注解是 Spring IOC 容器中的一个作用域

🚟BeanDefinition 类

BeanDefinition 是定义 Bean 的配置元信息接口,可以理解为创建bean过程中的一个中间类,扩展bean,存储更多的信息

public class BeanDefinition {
    private String scope;
    private Class aClass;

    public String getScope() {
        return scope;
    }

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

    public Class getaClass() {
        return aClass;
    }

    public void setaClass(Class aClass) {
        this.aClass = aClass;
    }
}

🚠要实现我们的@scope注解,我们需要改造一下我们的代码

把createBean方法抽离出来

  private void createBean(String beanName,Class<?> aClass){
        //注册有Component注解的bean
        if (aClass.isAnnotationPresent(Component.class)) {

            Component component = aClass.getAnnotation(Component.class);
            BeanDefinition beanDefinition = new BeanDefinition();
            if (aClass.isAnnotationPresent(Scope.class)) {
                Scope scope = aClass.getAnnotation(Scope.class);
                beanDefinition.setScope(scope.value());
            } else {
                beanDefinition.setScope("singleton");
            }
            beanDefinition.setaClass(aClass);

            String value = component.value();
            if ("".equals(value)) {
                beanDefinitionMap.put(beanName, beanDefinition);
            } else {
                beanDefinitionMap.put(value, beanDefinition);
            }
        }
    }

扫描ComponentScan注解的方法体改一下

🚜然后我们从beanDefinitionMap中实例化bean


        //将bean实例化到一级缓存中
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            //单例
            if ("singleton".equals(beanDefinition.getScope())) {
                try {
                    Object bean = beanDefinition.getaClass().getDeclaredConstructor().newInstance();
                    singletonObjects.put(beanName, bean);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

getBean方法也需要改造

public Object getBean(String beanName) {
        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException();
        }
        if ("singleton".equals(beanDefinition.getScope())) {
            //如果是单例,直接返回缓存里的bean
            return this.singletonObjects.get(beanName);
        } else {
            try {
                //如果是多例,直接返回新的bean
                return beanDefinition.getaClass().getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

🚙我们来看一下效果

在这里插入图片描述

这是没有加scope注解

在这里插入图片描述
返回的bean都是一个对象

我们给bean加上scope注解

在这里插入图片描述
返回来的bean每个都不一样

在这里插入图片描述

🚘@Autowired注解

spring中实现依赖注入的注解

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {

}

🚗UserService类,orderservice注入该类

@Component("userService")
public class UserService {
}

@Component
public class OrderService {
    @Autowired
    private UserService userService;


    public UserService getUserService() {
        return userService;
    }
}

🚗要使@Autowired注解生效,将bean实例化到一级缓存中方法需要改造一下

在这里插入图片描述

新增populateBean方法,用来初始bean


    private void populateBean(Object bean, Class aClass) {
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(Autowired.class)) {
                declaredField.setAccessible(true);
                try {
                    declaredField.set(bean, getBean(declaredField.getName()));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

getBean方法也需要改造一下

在这里插入图片描述

这样我们的orderservice里面的userservice就有值了

在这里插入图片描述

🚕BeanPostProcessor接口

该接口在显示调用初始化方法的前后添加我们自己的逻辑

public interface BeanPostProcessor {
    /**
     * 之前
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 之后
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}

我们创建存储BeanPostProcessor的list

在这里插入图片描述

在扫描的时候添加BeanPostProcessor

在这里插入图片描述

🚖添加自己的BeanPostProcessor

@Component
public class OrderPostProcessor implements BeanPostProcessor {
    /**
     * 之前
     *
     * @param bean
     * @param beanName
     * @return
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println(beanName+"执行前");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    /**
     * 之后
     *
     * @param bean
     * @param beanName
     * @return
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println(beanName+"执行后");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

实例化的时候执行BeanPostProcessor逻辑

在这里插入图片描述

运行结果

在这里插入图片描述

🚛Aop

jdk的动态代理是基于接口生成的代理对象

public interface OrderInterface {
    void test();
}

在这里插入图片描述

在OrderPostProcessor类中加以改造

在这里插入图片描述

这样我们就可以动态代理切入我们的orderservice类

在这里插入图片描述
在这里插入图片描述
以上就是全部内容

实现了以下

  • 🚀@ComponentScan注解
  • ✈️@Component注解
  • 🚁ApplicationContext类
  • 🚡@Scope注解
  • 🚟BeanDefinition 类
  • 🚘@Autowired注解
  • 🚕BeanPostProcessor接口
  • 🚛Aop
    内容,完成了一个超级简单且基础的spring源码

项目源码

博客码了两天,创作不易,多多点赞

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Spring】我抄袭了Spring,手写一套MySpring框架。。。 的相关文章

随机推荐

  • GreenPlum 数据库创建用户、文件空间、表空间、数据库

    前几篇文章介绍了GreenPlum数据库的安装 启动 关闭 状态检查 登录等操作 xff0c 数据库已经创建好了 xff0c 接下来介绍如何使用数据库 按照习惯 xff0c 需要先创建测试用户 表空间 数据库 先创建测试用户dbdream
  • 将Lua嵌入到自己的程序中

    什么是Lua Lua是具有简单数据描述的扩展编程语言 动态解析语言 它提供了非常好的面向对象编程 xff0c 函数式编程 functional programming xff0c 数据驱动式编程 data driven programmin
  • OpenCV——像素数据类型总结<摘>

    1 Unsigned 8bits 一般的图像文件格式使用的大小 IplImage数据结构参数 xff1a IPL DEPTH 8U CvMat数据结构参数 xff1a CV 8UC1 xff0c CV 8UC2 xff0c CV 8UC3
  • 关于warning: Clock skew detected. Your build may be incomplete. 的解决方法

    今天发现电脑的系统时间不对 xff0c 因此将时钟进行了改动 xff0c 回头编译Linux kernel的时候 xff0c 提演示样例如以下的warning xff1a warning Clock skew detected Your b
  • 8-4.桶排序算法详解

    1 桶排序介绍 桶排序 Bucket sort 是一种基于计数的排序算法 xff0c 工作的原理是将数据分到有限数量的桶子里 xff0c 然后每个桶再分别排序 xff08 有可能再使用别的排序算法或是以递回方式继续使用桶排序进行排序 xff
  • 我的年终总结,作为研发,在2018年都有哪些进步、收获与成长?

    2018 结束了 部门开会总结了过去的工作与未来的展望 xff0c 也是个不错的机会去回顾 审视 思考自己的 2018 年 玄难说过人与人的差距来自于思考与总结 xff0c 我深深地认同这一点 我也把自己的一部分思考写下来 xff0c 在公
  • cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local ..

    在安装opencv的时候 xff0c 会用 xff1a cmake D CMAKE BUILD TYPE 61 RELEASE D CMAKE INSTALL PREFIX 61 usr local 很久这这句话什么意思 xff01 在bu
  • 谈谈项目售前的经验:(个人观点)

    谈谈自己多年做网络项目售前的经验 个人观点 1 售前要熟悉市场 特别是你从事项目的决策链 项目的商务价格 同类项目竞争对手的价格分析等等 2 售前要熟悉项目的运作方式 实施 管理 客户的需求的深度挖掘 熟悉技术营销和解决方案营销 3 售前要
  • 【拥抱开源】发布自己的项目到maven中央仓库

    文章目录 x1f388 第一步 xff0c 注册账号 x1f4bf 第二步 xff0c 登录 x1f4c0 第三步 xff0c 设置信息 x1f4be 第四步 xff0c 创建问题 x1f4f9 第五步 xff0c 验证信息 x1f3a5
  • c# ros

    class MK Stream connection TcpClient con public MK string ip con 61 new TcpClient con Connect ip 8728 connection 61 Stre
  • Ubuntu中使用Docker/LXC迅速启动一个桌面系统

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 2013年07月18日 标签 cloud container docker lxc 作者 xff1a vpsee Docker 是 dotCloud 最近几个月刚宣布的开源
  • 自己写的小程序

    题目是 xff1a 输入n个学生成绩 xff0c 计算平均分 xff0c 并且统计不及格成绩的学生人数 自己这个程序和教材上是相同的 xff08 不谋而合 xff09 xff0c 哈哈 这次写的程序与之前比较更明了 xff0c 同学一下子就
  • 【编程好习惯】判断函数的返回值

    调用一个函数后要检查函数的返回值 xff0c 以决定程序是继续应用逻辑处理还是出错处理 xff0c 这理应是一个常识 xff0c 但在现实中 xff0c 却存在大量不检查函数返回值的代码 既然是常识 xff0c 但却得不到重视 xff0c
  • 计算机视觉中的词袋模型(Bow,Bag-of-words)

    计算机视觉中的词袋模型 Bow Bag of words Bag of words 读 39 xw20084898的专栏 39 的blog Bag of words model in computer vision Bag of words
  • VR发展简史

    最初的起源 事实上 xff0c 虚拟现实由来已久 xff0c 其概念最早被提及应该追溯到Aldous Huxley xff08 阿道司 赫胥黎 xff09 1932年推出的长篇小说 美丽新世界 xff0c 这篇小说以26世纪为背景 xff0
  • Prometheus+Grafana可视化监控SpringBoot项目

    原文地址 xff1a https xeblog cn articles 7 Prometheus简介 简史 Prometheus受启发于Google的Brogmon监控系统 xff08 相似的Kubernetes是从Google的Brog系
  • Linux和程序员有关系吗,程序员必须了解的Linux基础(用户和权限)

    Linux用户和用户组 xff1a 登录Linux系统需要用户名和密码 xff0c 用户名就对应系统中的一个用户 xff1b 为了方便用户的管理 xff0c 加入了用户组的概念 xff0c 修改用户组的权限会对用户组内的所有用户生效 Lin
  • CSDN博客频道——“我的2011”年度征文活动火爆进行中!

    2011年即将过去 xff0c 在这一年里你可能毕业参加工作 可能跳槽到新公司 可能开始涉猎新的技术领域 可能在CSDN遇上技术牛人 可能参与了极富挑战的项目 可能升职做经理总监 可能和志同道合的朋友们创业 也有可能失业了 投资失败了 与恋
  • stm32定时器PWM模式和输出比较模式

    pwm模式是输出比较模式的一种特例 xff0c 包含于输出比较模式中 64 defgroup TIM Output Compare and PWM modes 64 define TIM OCMode Timing uint16 t 0x0
  • 【Spring】我抄袭了Spring,手写一套MySpring框架。。。

    这篇博客实现了一个简单版本的Spring xff0c 主要包括Spring的Ioc和Aop功能 文章目录 这篇博客实现了一个简单版本的Spring xff0c 主要包括Spring的Ioc和Aop功能 x1f680 64 Component