手写Spring

news2024/11/15 13:32:28

简单实现Spring基于注解配置

 

ComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}

相当于component-scan 

HspSpringConfig

@ComponentScan(value = "spring.write.component")
public class HspSpringConfig {
}

替代bean.xml文件,添加@ComponentScan,获得扫描的包

AppMain

public class AppMain {
    public static void main(String[] args) {
        HspApplicationContext ioc = new HspApplicationContext(HspSpringConfig.class);
        Object userDao = ioc.getBean("userDao");
        System.out.println(userDao);
    }
}

HspApplicationContext

public class HspApplicationContext {
    private Class configClass;
    private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();

    public HspApplicationContext(Class configClass) {
        //1、获得扫描包路径
        //得到config文件的.class类型
        this.configClass = configClass;
        //反射得到ComponentScan注解
        ComponentScan componentScan =
                (ComponentScan)configClass.getDeclaredAnnotation(ComponentScan.class);
        //获取注解的value就是扫描的包路径
        String path = componentScan.value();
        System.out.println(path);
        //spring.write.component





        //2、得到包下的所有.class
        //得到类的加载器,获取实际目录下的(out),
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();

        //URL必须按照斜杠/来写
        //path = spring.write.component
        path = path.replace(".","/");
        URL resource = classLoader.getResource(path);
        System.out.println(resource);
        //file:/D:/Atest/spring/out/production/spring/spring/write/component

        //对路径下的文件进行遍历
        File file = new File(resource.getFile());
        if(file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                //只处理.class文件(java文件)
                String fileAbsolutePath = f.getAbsolutePath();
                if(fileAbsolutePath.endsWith(".class")) {
                    System.out.println(fileAbsolutePath);
                    //D:\Atest\spring\out\production\spring\spring\write\component\UserDao.class
                    //前面已经有了path,还需要具体的类名进行反射,放入容器
                    String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
                    String classFullName = path.replace("/", ".") + "." + className;
                    //判断该类是不是需要注入到容器,有无注解
                    try {
                        Class<?> aClass = classLoader.loadClass(classFullName);
                        //判断该类的实例是否有注解
                        if(aClass.isAnnotationPresent(Component.class) ||
                                aClass.isAnnotationPresent(Controller.class) ||
                                aClass.isAnnotationPresent(Service.class) ||
                                aClass.isAnnotationPresent(Repository.class)) {
                            //查看是否指定了id
                            if(aClass.isAnnotationPresent(Component.class)) {
                                //只演示了Component
                                Component component = aClass.getDeclaredAnnotation(Component.class);
                                String id = component.value();
                                if(! "".endsWith(id)) {
                                    className = id;
                                }
                            }
                            //放入容器
                            Class<?> clazz = Class.forName(classFullName);
                            Object instance = clazz.newInstance();
                            //类名首字母小写存入,springframework下的工具类
                            ioc.put(StringUtils.uncapitalize(className),instance);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    public Object getBean(String name) {
        return ioc.get(name);
    }
}

Spring整体架构分析

没有加@Scope(value="prototype")就是单例
ioc.getBean("name"),先到BeanDefinition Map获取,未找到异常处理,
如果是单例(single)就从单例Bean Map获取
如果是多例(prototype)就创建bean对象并返回(到BeanDefinition Map中,得到Bean的clazz对象,使用反射,创建bean返回)

1、实现扫描包,得到bean的class对象

Spring基于注解配置已经写过

HspApplicationContext

public class HspApplicationContext {
    private Class configClass;
    public HspApplicationContext(Class configClass) {
        this.configClass = configClass;
        ComponentScan componentScan =
                (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
        System.out.println(path);//com.spring.component

        ClassLoader classLoader =
                HspApplicationContext.class.getClassLoader();
        path = path.replace(".", "/");
        System.out.println(path);//com/spring/component

        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();
                if(fileAbsolutePath.endsWith(".class")) {
                    String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
                    String classFullName = path.replace("/", ".") + "." + className;
                    try {
                        Class<?> clazz = classLoader.loadClass(classFullName);
                        if(clazz.isAnnotationPresent(Component.class)) {
                            System.out.println("是Spring bean【class对象】= " + clazz + " 【类名】 = " + className);
                        } else {
                            System.out.println("不是Spring bean【class对象】= " + clazz + " 【类名】 = " + className);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

2、扫描bean信息,封装到BeanDefinition对象并放入Map

增加@Scope注解来判断单例(single)或者多例(prototype)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    String value() default "";
}

BeanDefinition用于封装/记录Bean信息
1、单例/多例
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 +
                '}';
    }
}

HspApplicationContext

private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();
//单例对象类型不确定,用Object
private ConcurrentHashMap<String, Object> singletonObejcts =
            new ConcurrentHashMap<>();

如果有@Component,就放入beanDefinitionMap中

可以将1、2步写成一个方法,在构造器里调用

3、初始化bean单例池并完成getBean、createBean方法

public class HspApplicationContext {
    private Class configClass;
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();
    //单例对象类型不确定,用Object
    private ConcurrentHashMap<String, Object> singletonObejcts =
            new ConcurrentHashMap<>();
    public HspApplicationContext(Class configClass) {
        //完成扫描指定包
        beanDefinitionScan(configClass);
        //遍历所有的beanDefinition对象
        Enumeration<String> keys = beanDefinitionMap.keys();
        while(keys.hasMoreElements()) {
            String beanName = keys.nextElement();
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                Object bean = createBean(beanDefinition);
                singletonObejcts.put(beanName,bean);
            }
        }
        System.out.println("singletonObejcts单例池=" + singletonObejcts);
        System.out.println("beanDefinitionMap=" + beanDefinitionMap);
    }
    public void beanDefinitionScan(Class configClass) {}

    public Object createBean(BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
    public Object getBean(String name) {
        //判断beanName是否存在
        if(beanDefinitionMap.containsKey(name)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            if("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                return singletonObejcts.get(name);
            } else {
                return createBean(beanDefinition);
            }
        } else {
            throw new NullPointerException("没有该Bean");
        }
    }
}

4、依赖注入

增加@Autowired注解,这里只用名字来进行匹配

 

    public Object createBean(BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //遍历所有字段,
            for(Field field : clazz.getDeclaredFields()) {
                //是否有Autowired
                if(field.isAnnotationPresent(Autowired.class)) {
                    String name = field.getName();
                    //通过getBean获取要组装的对象
                    Object bean = getBean(name);
                    //进行组装,private需要暴破
                    field.setAccessible(true);
                    field.set(instance,bean);//将 bean 赋值给 instance 的 field
                }
            }
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

5、bean后置处理器实现

原生Spring接口InitializingBean(初始化),自己实现这个接口,完成初始化方法
接口中有afterPropertiesSet()方法,在Bean的setter方法执行完毕后,被spring容器调用,就是初始化方法

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

应用:选择MonsterService来实现这个接口

@Component("monsterService")
@Scope(value = "property")
public class MonsterService implements InitializingBean {
    @Autowired
    private MonsterDao monsterDao;
    public void m1() {
        monsterDao.hi();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("monsterService初始化方法被调用");
    }
}

在创建好Bean实例后,判断是否需要初始化
容器中常用的一个方法是:根据该类是否实现了某个接口,来判断是否要执行某个业务(接口编程)

在HspApplicationContext的createBean返回instance之前加上判断是否是这个接口的子类型,是的话就执行初始化方法

判断如果返回为null,不变化
加上beanName

public Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //遍历所有字段,
            for(Field field : clazz.getDeclaredFields()) {
                //是否有Autowired
                if(field.isAnnotationPresent(Autowired.class)) {
                    String name = field.getName();
                    //通过getBean获取要组装的对象
                    Object bean = getBean(name);
                    //进行组装,private需要暴破
                    field.setAccessible(true);
                    field.set(instance,bean);//将 bean 赋值给 instance 的 field
                }
            }
            System.out.println("======创建好实例======" + instance);
            //初始化方法前,调用before方法,可以对容器的bean实例进行处理,然后返回处理后的bean
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                Object current = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
                if(current != null)
                    instance = current;
            }
            if(instance instanceof InitializingBean) {
                try {
                    ((InitializingBean) instance).afterPropertiesSet();//将instance转成InitializingBean类型,调用方法
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //初始化方法后,调用after方法,可以对容器的bean实例进行处理,然后返回处理后的bean
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                Object current = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
                if(current != null)
                    instance = current;
            }
            return instance;

后置处理器接口BeanPostProcessor

对容器中的所有bean生效

public interface BeanPostProcessor {
    //bean的初始化前调用
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }
    //bean的初始化后调用
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}

自己写一个类实现后置处理器,通过Component注入到容器中

实现类可以不止一个,放在Arraylist中

private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

@Component
public class HspBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("后置处理器before()");
         return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("后置处理器after()");
        return bean;
    }
}

在HspApplicationContext的扫描方法中扫描Component,再判断是否实现了后置处理器接口

6、AOP机制实现 

原生Spring实现方法
A接口写方法,B类实现A接口,给B类加上@Component注入到容器
切面类加上@Component和@Aspect
B类执行后置处理器的After方法后,变成代理对象

public interface SmartAnimalable {
    float getSum(float i, float j);
    float getSub(float i, float j);
}
@Component(value = "smartDog")
public class SmartDog implements SmartAnimalable {
    public float getSum(float i, float j) {
        float res = i + j;
        System.out.println("SmartDog-getSum-res=" + res);
        return res;
    }

    public float getSub(float i, float j) {
        float res = i - j;
        System.out.println("SmartDog-getSub-res=" + res);
        return res;
    }
}
public class SmartAnimalAspect {
    public static void showBeginLog() {
        System.out.println("前置通知..");
    }
    public static void showSuccessLog() {
        System.out.println("返回通知..");
    }
}

在HspBeanPostProcessor的after方法中实现AOP(写死的方法)

public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("后置处理器after()");
        if("smartDog".equals(beanName)) {
            Object proxyInstance = Proxy.newProxyInstance(HspBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Object result = null;
                    if("getSum".equals(method.getName())) {
                        SmartAnimalAspect.showBeginLog();
                        result = method.invoke(bean,args);
                        SmartAnimalAspect.showSuccessLog();
                    } else {
                        result = method.invoke(bean,args);
                    }
                    return result;
                }
            });
            return proxyInstance;
        }
        return bean;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2151380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

初始泛型【超级详细哦~】

初始泛型【超级详细哦~】 1、包装类1.1 基本数据类型和对应的包装类1.2 装箱和拆箱1.3 自动装箱和拆箱 2、泛型2.1 什么是泛型2.2 泛型的语法2.3泛型的使用2.4 泛型的上界2.4.1 语法2.4.2 示例 1、包装类 1.1 基本数据类型和对应的包装类 1.2 装箱和拆箱 int i10;//装箱操作&a…

聊聊Thread Local Storage

聊聊ThreadLocal 为什么需要Thread Local StorageThread Local Storage的实现PThread库实现操作系统实现GCC __thread关键字实现C11 thread_local实现JAVA ThreadLocal实现 Thread Local Storage 线程局部存储&#xff0c;简称TLS。 为什么需要Thread Local Storage 变量分为全…

元学习的简单示例

代码功能 模型结构&#xff1a;SimpleModel是一个简单的两层全连接神经网络。 元学习过程&#xff1a;在maml_train函数中&#xff0c;每个任务由支持集和查询集组成。模型先在支持集上进行训练&#xff0c;然后在查询集上进行评估&#xff0c;更新元模型参数。 任务生成&…

STM32G431RBT6(蓝桥杯)串口(发送)

一、基础配置 (1) PA9和PA10就是串口对应在单片机上的端口 注意&#xff1a;一定要先选择PA9的TX和PA10的RX&#xff0c;再去打开异步的模式 (2) 二、查看单片机的端口连接至电脑的哪里 &#xff08;1&#xff09;此电脑->右击属性 &#xff08;2&#xff09;找到端…

AI视觉算法盒是什么?如何智能化升级网络摄像机,守护全方位安全

在智能化浪潮席卷全球的今天&#xff0c;以其创新技术引领行业变革&#xff0c;推出的集高效、智能、灵活于一体的AI视觉算法盒。这款革命性的产品&#xff0c;旨在通过智能化升级传统网络摄像机&#xff0c;为各行各业提供前所未有的安全监控与智能分析能力&#xff0c;让安全…

SpringCloud构建工程

一、新建数据库和表&#xff0c;并填写测试数据 二、创建父级工程 1、创建maven工程 2、工程名字OfficeAutomation 3、pom.xml文件中添加依赖 <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.encodin…

领域驱动DDD三种架构-分层架构、洋葱架构、六边形架构

博主介绍&#xff1a; 大家好&#xff0c;我是Yuperman&#xff0c;互联网宇宙厂经验&#xff0c;17年医疗健康行业的码拉松奔跑者&#xff0c;曾担任技术专家、架构师、研发总监负责和主导多个应用架构。 技术范围&#xff1a; 目前专注java体系&#xff0c;以及golang、.Net、…

第二十节:学习Redis缓存数据库实现增删改查(自学Spring boot 3.x的第五天)

这节记录下如何使用redis缓存数据库。 第一步&#xff1a; 先在服务器端安装redis&#xff0c; 下载地址&#xff1a;Releases tporadowski/redis GitHub。 第二步&#xff1a; 安装redis客户端可视化管理软件redisDesktopmanager Redis Desktop Manager - Download 第…

GAMES101(13节Ray Tracing)

Ray Tracing 基本原理&#xff1a; 我们知道为什么会看到物体的颜色&#xff0c;因为光线照射物体&#xff0c;未被吸收的光线反射到人眼&#xff0c;因此&#xff0c;我们看到的颜色&#xff0c;就是光的一部分&#xff0c;光线追踪就是模拟这个过程 光线假设&#xff1a; …

DHCP协议原理(网络协议)

DHCP简介 定义 DHCP&#xff08;动态主机配置协议&#xff09;是一种网络管理协议&#xff0c;能够自动为局域网中的每台计算机分配IP地址及其他网络配置参数&#xff0c;包括子网掩码、默认网关和DNS服务器等。这一机制极大简化了网络管理&#xff0c;尤其在大型局域网中&am…

聊聊AUTOSAR:基于Vector MICROSAR的TC8测试开发方案

技术背景 车载以太网技术作为汽车智能化和网联化的重要组成部分&#xff0c;正逐步成为现代汽车网络架构的核心&#xff0c;已广泛应用于汽车诊断&#xff08;如OBD&#xff09;、ECU软件更新、智能座舱系统、高清摄像头环视泊车系统等多个领域。 在这个过程中&#xff0c;ET…

CSS 的元素显示模式简单学习

目录 1. 元素显示模式 1.1 概述 1.2 块元素 1.3 行元素 1.4 行内块元素 1.5 元素显示模式总结 2. 元素显示模式转换 3. 单行文字垂直居中 4. 案例演示 1. 元素显示模式 1.1 概述 1.2 块元素 1.3 行元素 1.4 行内块元素 1.5 元素显示模式总结 2. 元素显示模式转换 3. 单…

通过markdown表格批量生成格式化的word教学单元设计表格

素材&#xff1a; 模板&#xff1a; 代码&#xff1a; import pandas as pd from python_docx_replace import docx_replace,docx_get_keys from docx import Document from docxcompose.composer import Composerdef parse_markdown_tables(file_path):with open(file_path,…

DOCKER 数据库管理软件自己开发--———未来之窗行业应用跨平台架构

- 数据异地容灾服务--未来之窗智慧数据服务 DATA REMOTE DISASTER RECOVERY SERVICE -CyberWin Future Docker-数据查看 CyberWin DATA Viewer 1.docker 样式 mysqli://root:密码172.17.0.2:端口/数据库 阿雪技术观 拥抱开源与共享&#xff0c;见证科技进步奇迹&#xff0c;…

AMD小胜!锐龙7 9700X VS. i7- 14700K网游对比

一、前言&#xff1a;两款高端处理器的网游对比测试 半个月前&#xff0c;我们做了锐龙5 9600X与i5-14600K的网游帧率测试&#xff0c;结果有点意外&#xff0c;几款游戏平均下来&#xff0c;锐龙5 9600X比i5-14600K竟然强了19%之多。 今天我们将会对锐龙7 9700X和i7-14700K进行…

【高阶数据结构】二叉搜索树的插入、删除和查找(精美图解+完整代码)

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《高阶数据结构》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多《高阶数据结构》点击专栏链接查看&a…

【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上)

文章目录 前言一、ArkTS基本介绍1、 ArkTS组成2、组件参数和属性2.1、区分参数和属性的含义2.2、父子组件嵌套 二、装饰器语法1.State2.Prop3.Link4.Watch5.Provide和Consume6.Observed和ObjectLink代码示例&#xff1a;示例1&#xff1a;&#xff08;不使用Observed和ObjectLi…

Windows11家庭版修改用户密码策略为永不过期。

今天有个朋友找到我说&#xff0c;他的电脑密码老是过期然后需要修改&#xff0c;让我帮忙改一下密码策略&#xff0c;改为永不过期。 下面就来操作一下吧。 这里有个小小的坑&#xff0c;就是win11的家庭版是没有 gpedit.msc的&#xff0c;也就不能直接cmd打开本地策略便器&…

【WebGis开发 - Cesium】获取视野中心点,并设置顶视图视角

引言 项目开发过程中遇到一个需求&#xff0c;通过一个按钮切换视角为顶视图。 分析了一下这个模糊的需求&#xff0c;首先没有给出切换顶视图后俯视的区域范围&#xff0c;其次没有给出俯视点的高度。 这里可以粗略的认为当前的侧俯视的角度下观看的范围即为俯视的区域范围&am…

视频美颜SDK核心功能解析:打造高效直播美颜工具方案详解

随着直播行业的迅猛发展&#xff0c;用户对于直播画质和个人形象的要求越来越高。视频美颜SDK作为一项关键技术&#xff0c;已经成为各大直播平台和短视频应用的重要组成部分。通过实时美颜技术&#xff0c;用户能够在直播过程中呈现出更加理想的形象&#xff0c;从而提升直播体…