手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】之实现任务阶段 5- bean 后置处理器

news2024/11/24 14:48:03

😀前言
手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】的第五篇具体实现了任务阶段 5- bean 后置处理器

🏠个人主页:尘觉主页
在这里插入图片描述

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

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

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

文章目录

  • 🤩实现任务阶段 5- bean 后置处理器
    • ● 分析示意图
    • 创建InitializingBean接口
    • 修改MonsterService.java类
    • 修改WyxSpringApplicationContext类
    • 😋运行完成测试
    • 创建 BeanPostProcessor接口
    • 创建 WyxBeanPostProcessor类
    • 修改WyxSpringApplicationContext类
    • 😎运行完成测试
    • 😄总结

🤩实现任务阶段 5- bean 后置处理器

● 分析示意图

img

● 代码实现, 说明,整个实现思路,就是参考 Spring 规范

创建InitializingBean接口

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

修改MonsterService.java类

去实现 InitializingBean 接口

\1. afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用
2 即就是初始化方法

@Component//(value = "monsterService") //把MonsterService注入我们自己的spring容器中
@Scope(value = "prototype")
public class MonsterService implements InitializingBean {
    //这里我们使用自己的@Autowired来修饰属性
    //表示该属性,是通过容器完成依赖注入
    //说明: 我们实现按照名字来进行组装即可
    @Autowired
    private MonsterDao monsterDao;

    public void m1() {
        monsterDao.hi();
    }

    /**
     * 1. afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用
     * 2 即就是初始化方法
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MonsterService 初始化方法被调用 程序员在这里加入初始化的业务..");
    }
}

修改WyxSpringApplicationContext类

在创建好 Bean 实例后,判断是否需要进行初始化 【容器中常.否实现了某个接口,来判断是否要执行某个业务逻辑, 这里其实就是 java 基础的接口编程实际运用

 //先简单实现实现,后面在完善.
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        //得到 bean 的类型
        Class clazz = beanDefinition.getClazz();
        try {
            //使用反射得到实例
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //完成依赖注入
            for (Field declaredField : clazz.getDeclaredFields()) {
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    // 处理@Autowired 注解的属性 required, 很简单,自己完成
                    // Autowired annotation =declaredField.getAnnotation(Autowired.class);
                    // System.out.println(annotation.required());
                    //如果该属性有@Autowired, 就进行组装
                    Object bean = getBean(declaredField.getName());
                    declaredField.setAccessible(true);//因为属性是 private,需要暴破
                    declaredField.set(instance, bean);
                }
            }
            //这里还有其他,比如 Aware 回调. 不写了
            //这里调用初始化,如果 bean 实现了 InitializingBean
            System.out.println("======创建好了====" + instance);
            if (instance instanceof InitializingBean) {
                try {
                    ((InitializingBean) instance).afterPropertiesSet();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        //如果没有创建成功,返回 null
        return null;
    }

😋运行完成测试

img

创建 BeanPostProcessor接口

该接口可以参考原生 Spring 规范 , 注注意体会切面编程

  1. 参考原生Spring容器定义一个接口BeanPostProcessor

  2. 该接口有两个方法postProcessBeforeInitialization 和 postProcessAfterInitialization

  3. 这两个方法,会对Spring容器的所有Bean生效, 已经是切面编程的概念.

public interface BeanPostProcessor {

    /**
     * 1. postProcessBeforeInitialization在Bean的初始化方法前调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 1. postProcessAfterInitialization在Bean的初始化方法后调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}

创建 WyxBeanPostProcessor类

@Component
public class WyxBeanPostProcessor implements BeanPostProcessor {
    /**
     * 该方法时在 bean 创建好后,进行初始化前调用
     * @param bean : 创建好的 bean 对象
     * @param beanName 创建好的 bean 的名字
     * @return
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        //这里程序员来决定业务逻辑,spring 只是提供处理机制
        System.out.println("postProcessBeforeInitialization 被调用 " + beanName + " bean= " + bean.getClass());
        return bean;
    }
    /**
     * 该方法时在 bean 创建好后,初始化完成后调用
     * @param bean : 创建好的 bean 对象
     * @param beanName : 创建好的 bean 的名字
     * @return
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        //这里程序员来决定业务逻辑,spring 只是提供处理机制
        System.out.println("postProcessAfterInitialization 被调用 " + beanName + " bean= " + bean.getClass());
        return bean;
    }
}

修改WyxSpringApplicationContext类

注意:这里 createBean(String beanName, BeanDefinition beanDefinition) 需要增加入 参 beanName, 就会导致好几个位置错误,需要根据错误提示,对应解决即可.

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

    private void beanDefinitionsByscan(Class configClass) {
        this.configClass = configClass;
        ComponentScan componentScan = (ComponentScan)
                this.configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
        System.out.println("扫描路径 = " + path);
        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. 增 加 一 个 逻 辑 , 如 果 这 个 clazz 类 型 是 实 现 了BeanPostProcessor 接口, 说明是一个 bean 处理器,特殊处理
                        // 2. 注意不能使用 clazz instanceof BeanPostProcessor 判断因为 clazz 并不是一个实例对象, 而是一个类对象
                        // 3. 这里实现是为了方便获取 bean 处理器对象,所以放在一个 beanPostProcessorList, spring 底层源码,
                        // 还 是 走 的 createBean(),getBean(), 只 是 需 要 在singletonObjects, 增加代码处理, 
                            // 我这里主要讲的是 bean 处理器的工作机制,就不处理了,知道即可
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                // 创建一个实例对象
                                BeanPostProcessor beanPostProcessor =
                                        (BeanPostProcessor) clazz.newInstance();
                                // 放入到 beanPostProcessorList
                                beanPostProcessorList.add(beanPostProcessor);
                                continue;
                            // 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();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    }
                    System.out.println("=======================================");
                }
            }
        }
    }

        // 先简单实现实现,后面在完善. private Object createBean(String beanName, BeanDefinition beanDefinition) {
    // 得到 bean 的类型

    Class clazz = beanDefinition.getClazz();
    try
    {
        // 使用反射得到实例
        Object instance = clazz.getDeclaredConstructor().newInstance();
        // 完成依赖注入
        for (Field declaredField : clazz.getDeclaredFields()) {
            if (declaredField.isAnnotationPresent(Autowired.class)) {
            // 处理@Autowired 注解的属性 required, 很简单,自己完成
            // Autowired annotation = declaredField.getAnnotation(Autowired.class);
            // System.out.println(annotation.required());
            // 如果该属性有@Autowired, 就进行组装
                Object bean = getBean(declaredField.getName());
                declaredField.setAccessible(true);// 因为属性是 private,需要暴破
                declaredField.set(instance, bean);
            }
        }
        // 这里还有其他,比如 Aware 回调. 不写了
        // 说明
        // 1. 在 bean 初始化前调用所有 bean 处理器的 postProcessBeforeInitialization
        // 2. 调用时,不能保证顺序
        // 3. 可以通过加入@Order("值"), 来指定 bean 处理器调用顺序,同学们可以自行完成, 不难
        // 4. 如果希望指定对哪些 bean 进行初始化前处理 , 可以在处理器的postProcessBeforeInitialization()
        // 加入相关业务判断即可.比如:
        /**
         * @Override
         * public Object postProcessBeforeInitialization(Object bean, String
        beanName) {
         * if("monsterService".equalsIgnoreCase(beanName)) {
         * //这里程序员来决定业务逻辑,spring 只是提供处理机制
         * System.out.println("postProcessBeforeInitialization 被调用 " * + beanName + " bean= " + bean.getClass());
         * return bean;
         * }else {
         * return bean;
         * }
         * }
         */
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
        // 4. 也会返回一个对象,这个返回的对象是什么,由程序员在编写 bean 处理器决定,可能是原来的 bean, 也可能被改变了

            instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
        }
            // 这里调用初始化,如果 bean 实现了 InitializingBean
            if (instance instanceof InitializingBean) {
            try {
                ((InitializingBean) instance).afterPropertiesSet();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        // 说明
        // 1. 在 bean 初始化后调用所有 bean 处理器的 postProcessAfterInitialization
        // 2. 调用时,不能保证顺序
        // 3. 可以通过加入@Order("值"), 来指定 bean 处理器调用顺序,同学们可以自行完成, 不难
        // 4. 如 果 希 望 指 定 对 哪 些 bean 进 行 初 始 化 后 处 理 , 可 以 在 处 理 器 的postProcessAfterInitialization()
        // 加入相关业务判断即可
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
        // 4. 也会返回一个对象,这个返回的对象是什么,由程序员在编写 bean 处理器决定,可能是原来的 bean, 也可能被改变了
            instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
        }
        return instance;
    } catch(InstantiationException e){
        e.printStackTrace();
    } catch(IllegalAccessException e){
        e.printStackTrace();
    } catch(InvocationTargetException e){
        e.printStackTrace();
    } catch(NoSuchMethodException e){
        e.printStackTrace();
    }
        // 如果没有创建成功,返回 null
        return null;

😎运行完成测试

img

😄总结

本篇我们完成了bean 后置处理器是我们的手写进一步完善了手动实现 Spring 底层机制
                                
                                
😉手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】系列

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

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

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

手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】实现任务阶段 3- 初始化 bean 单例池 和 实现任务阶段 4- 完成依赖注入
                                
                                
😁热门专栏推荐
想学习vue的可以看看这个
java基础合集
数据库合集
redis合集
nginx合集
linux合集
等等等还有许多优秀的合集在主页等着大家的光顾感谢大家的支持

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

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

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

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

相关文章

ModaHub魔搭社区:从OpenAI实践看分工必要性,核心关注工作流相关的基础软件工具栈

从OpenAI实践看分工必要性,核心关注工作流相关的基础软件工具栈 参考海外OpenAI的率先尝试,工作流分工、点工具加持助力成功。一方面,OpenAI在《GPT-4 Technical Report》论文中[1]中披露了参与GPT 4开发的人员分工,共249人,角色分工明确,预训练、强化学习和对齐、部署等…

时间序列去趋势化和傅里叶变换

在计算傅里叶变换之前对信号去趋势是一种常见的做法&#xff0c;特别是在处理时间序列时。在这篇文章中&#xff0c;我将从数学和视觉上展示信号去趋势是如何影响傅里叶变换的。 这篇文章的目的是让介绍理解什么是常数和线性去趋势&#xff0c;为什么我们使用它们&#xff0c;…

CRM系统如何搭建?流程是什么样的?

CRM系统可以提高企业的销售效率和客户满意度&#xff0c;从而增加企业的收入和利润。但是&#xff0c;要想成功地上线CRM系统&#xff0c;需要经过一系列的步骤和流程&#xff0c;下面说说&#xff0c;企业如何上线CRM系统&#xff1f;CRM系统搭建流程。 1、需求分析 需求分析…

记一次物理机安装centos7遇到的问题

首先制作U盘镜像&#xff08;之前装windows的大白菜之类的就没用了&#xff09; 用的这个UltraISO制作U盘镜像 然后从U盘启动开始安装&#xff0c; 问题一 安装时报错 dracut-pre-udev[351]:modprobe :ERROR:could not insert ‘floppy’ dracut-pre-udev[351]:modprobe…

Nacos源码 (3) 注册中心

本文将从一个服务注册示例入手&#xff0c;通过阅读客户端、服务端源码&#xff0c;分析服务注册、服务发现原理。 使用的2.0.2的版本。 返回目录 客户端 创建NacosNamingService对象 NacosNamingService nacosNamingService new NacosNamingService(NACOS_HOST);NacosNami…

华为OD机试 - 最长的连续子序列 (Java 2022Q4 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#xff09;》…

ISIS技术(第三十七课)

1 分享一下华为官网上的一张地图 官网地址:https://support.huawei.com/hedex/hdx.do?docid=EDOC1000105967&id=ZH-CN_CONCEPT_0000001501534705 2 路由的分类 -直连路由 直接连接的路由,且配置了IP地址之后(在同一网段内),就是直连路由。 -非直连路由 -静态路由…

如何在金属制品业运用IPD?

金属制品行业是指以金属材料为原料&#xff0c;通过加工、制造、加工等工艺制造出各种金属制品的企业和产业。这些金属制品包括但不限于机械设备、工具、建筑材料、家具、电子产品、交通运输设备等。金属制品加工业是机械装备行业的一个子行业&#xff0c;包括结构性金属制品制…

SpringBoot对一个URL通过method(GET、POST、PUT、DELETE)实现增删改查操作

目录 1. rest风格基础2. 开启方法3. 实战练习 1. rest风格基础 我们都知道GET、POST、PUT、DELETE分别对应查、增、改、删除 虽然Postman这些工具可以直接发送GET、POST、PUT、DELETE请求。但是RequestMapping并不支持PUT和DELETE请求操作。需要我们手动开启 2. 开启方法 P…

ModaHub魔搭社区:Milvus Cloud素材集合帖,等你查收

Hi~Milvus Cloud 的各位朋友,这是一期 Milvus Cloud 素材弹药库的集中汇总帖。随着向量数据库的火爆,越来越多的伙伴开始关注到向量数据库并开始使用 Milvus Cloud 。 考虑到目前信息获取的渠道多且分散,我们专门为大家整理了一期 Milvus Cloud 信息集合帖,让大家可以在快…

iPhone苹果手机触屏失灵无法关机,如何强制重启

参考:https://zhuanlan.zhihu.com/p/615223121 1&#xff0c;只轻按一下音量上键后快速松开 2&#xff0c;只轻按一下音量下键后快速松开 3&#xff0c;只按住右侧电源键长按不松手&#xff0c;直到手机关机。

Tomcat的多实例和动静分离

目录 一、多实例 二、 nginxtomcat的负载均衡和动静分离 三、Tomcat 客户端->四层代理->七层代理->tomcat服务器 实验&#xff1a; 问题总结&#xff1a; tomcat日志文件&#xff1a;/usr/local/tomcat/logs/catalina.out 一、多实例 在一台服务器上有多个tomc…

python——案例19:九九乘法表

案例19&#xff1a;九九乘法表for i in range(1,10): #i是行&#xff0c;j是列for j in range(1,i1): #确保内循环中的列小于等于列print(%d*%d%2ld%(i,j,i*j),end ) #计算方法&#xff0c;并且确保内容连续print()

2023.8.14论文阅读

文章目录 ESPNet: Efficient Spatial Pyramid of Dilated Convolutions for Semantic Segmentation摘要本文方法实验结果 DeepFusion: Lidar-Camera Deep Fusion for Multi-Modal 3D Object Detection摘要本文方法实验结果 ESPNet: Efficient Spatial Pyramid of Dilated Convo…

【不限于联想Y9000P电脑关盖再打开时黑屏的解决办法】

不限于联想Y9000P电脑关盖再打开时黑屏的解决办法 问题的前言问题的出现问题拟解决 问题的前言 事情发生在昨天&#xff0c;更新了Win11系统后&#xff1a; 最惹人注目的三处地方就是&#xff1a; 1.可以查看时间的秒数了&#xff1b; 2.右键展示的内容变窄了&#xff1b; 3.按…

JimuReport积木报表 v1.6.0版本发布—免费的可视化报表

项目介绍 一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完成报…

腾讯会议:云上协奏,远程韶华

腾讯会议的原理及历史 摘要 本论文介绍了腾讯会议的原理和历史。腾讯会议是一款基于云计算和通信技术的在线会议平台,由腾讯公司推出。通过分析腾讯会议的工作原理和演进历史,我们可以深入了解该平台是如何实现高效、便捷、安全的远程协作和沟通的。 1. 引言 近年来,随着…

PostgreSql 备份恢复

一、概述 数据库备份一般可分为物理备份和逻辑备份&#xff0c;其中物理备份又可分为物理冷备和物理热备&#xff0c;下面就各种备份方式进行详细说明&#xff08;一般情况下&#xff0c;生产环境采取的定时物理热备逻辑备份的方式&#xff0c;均是以下述方式为基础进一步研发编…

Qt开发技术:Q3D图表开发笔记:Q3DSurface三维曲面图介绍、Demo以及代码详解

前言 qt提供了q3d进行三维开发&#xff0c;虽然这个框架没有得到大量运用也不是那么成功&#xff0c;性能上也有很大的欠缺&#xff0c;但是普通的点到为止的应用展示还是可以的。   其中就包括华丽绚烂的三维图表&#xff0c;数据量不大的时候是可以使用的。   前面介绍了…

心法利器[96] | 写了个向量检索的baseline

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会&#xff0c;与大家一起成长。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2022年新一版的文章合集已经发布&#xff0c;累计已经60w字了&#xff0c;获取方式看这里&…