实现BeanPostProcessor

文章目录

    • 1.实现初始化方法
        • 1.目录
        • 2.InitializingBean.java
        • 3.MonsterService.java 实现初始化接口
        • 4.SunSpringApplicationContext.java 调用初始化方法
        • 5.测试
    • 2.实现后置处理器
        • 1.目录
        • 2.BeanPostProcessor.java 后置处理器接口
        • 3.SunBeanProcessor.java 自定义后置处理器
        • 4.SunSpringApplicationContext.java 具体实现
          • 1.定义一个存储后置处理器的List
          • 2.在扫描bean的时候将后置处理器放到List中
          • 3.初始化bean前后使用调用后置处理器进行处理
          • 4.测试
          • 5.完整代码

1.实现初始化方法

1.目录

CleanShot 2024-08-06 at 12.28.31@2x

2.InitializingBean.java
package com.sunxiansheng.sunspring.processor;

/**
 * Description: Bean的初始化接口
 * @Author sun
 * @Create 2024/8/5 16:35
 * @Version 1.0
 */
public interface InitializingBean {

    /**
     * 在Bean的所有属性设置完成之后调用
     */
    void afterPropertiesSet();

}
3.MonsterService.java 实现初始化接口
package com.sunxiansheng.sunspring.compent;

import com.sunxiansheng.sunspring.annotation.Component;
import com.sunxiansheng.sunspring.annotation.Resource;
import com.sunxiansheng.sunspring.annotation.Scope;
import com.sunxiansheng.sunspring.annotation.myenum.MyScope;
import com.sunxiansheng.sunspring.processor.InitializingBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Description: 怪物服务
 * @Author sun
 * @Create 2024/8/4 16:31
 * @Version 1.0
 */
// 自定义注解:将MonsterService类交给自定义Spring容器管理
// 如果指定value属性,则使用value属性值作为bean的id,否则使用类名首字母小写作为bean的id
@Component
@Scope(value = MyScope.PROTOTYPE) // 指定bean的作用域为多例
public class MonsterService implements InitializingBean {

    private static final Logger log = LoggerFactory.getLogger(MonsterService.class);

    /**
     * 自定义的Resource注解,用于自动注入MonsterDao对象
     */
    @Resource
    private MonsterDao monsterDao;

    public void query() {
        log.info("MonsterService调用MonsterDao");
        monsterDao.query();
    }

    /**
     * 初始化方法
     */
    @Override
    public void afterPropertiesSet() {
        log.info("MonsterService初始化完成...");
    }

}
4.SunSpringApplicationContext.java 调用初始化方法

CleanShot 2024-08-06 at 12.33.56@2x

5.测试

CleanShot 2024-08-06 at 12.34.18@2x

2.实现后置处理器

1.目录

CleanShot 2024-08-06 at 13.35.31@2x

2.BeanPostProcessor.java 后置处理器接口
package com.sunxiansheng.sunspring.processor;

/**
 * Description: 自定义BeanPostProcessor接口
 * @Author sun
 * @Create 2024/8/6 12:36
 * @Version 1.0
 */
public interface BeanPostProcessor {

    /**
     * 该方法在bean的初始化方法之前执行
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 该方法在bean的初始化方法之后执行
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }

}
3.SunBeanProcessor.java 自定义后置处理器
package com.sunxiansheng.sunspring.compent;

import com.sunxiansheng.sunspring.annotation.Component;
import com.sunxiansheng.sunspring.processor.BeanPostProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Description:
 * @Author sun
 * @Create 2024/8/6 12:39
 * @Version 1.0
 */
@Component
public class SunBeanProcessor implements BeanPostProcessor {

    private static final Logger log = LoggerFactory.getLogger(SunBeanProcessor.class);

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        log.info("SunBeanProcessor postProcessBeforeInitialization..." + beanName + "=>" + bean.getClass());
        // 返回空,不对bean进行处理
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        log.info("SunBeanProcessor postProcessAfterInitialization..." + beanName + "=>" + bean.getClass());
        // 返回空,不对bean进行处理
        return null;
    }

}
4.SunSpringApplicationContext.java 具体实现
1.定义一个存储后置处理器的List

CleanShot 2024-08-06 at 13.36.53@2x

2.在扫描bean的时候将后置处理器放到List中

CleanShot 2024-08-06 at 13.37.17@2x

3.初始化bean前后使用调用后置处理器进行处理

CleanShot 2024-08-06 at 13.37.53@2x

4.测试

CleanShot 2024-08-06 at 13.38.49@2x

5.完整代码
package com.sunxiansheng.sunspring.ioc;

import com.sunxiansheng.sunspring.annotation.Component;
import com.sunxiansheng.sunspring.annotation.ComponentScan;
import com.sunxiansheng.sunspring.annotation.Resource;
import com.sunxiansheng.sunspring.annotation.Scope;
import com.sunxiansheng.sunspring.annotation.myenum.MyScope;
import com.sunxiansheng.sunspring.processor.BeanPostProcessor;
import com.sunxiansheng.sunspring.processor.InitializingBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Description: 自定义Spring容器
 * @Author sun
 * @Create 2024/8/4 16:35
 * @Version 1.0
 */
public class SunSpringApplicationContext {

    private static final Logger log = LoggerFactory.getLogger(SunSpringApplicationContext.class);

    /**
     * bean定义的map
     */
    private ConcurrentHashMap<String, BeanDefintion> beanDefintionMap = new ConcurrentHashMap<>();

    /**
     * 单例池
     */
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * bean的后置处理器
     */
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    // 构造器,接收配置类的class对象
    public SunSpringApplicationContext(Class<?> configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 完成bean的扫描,将bean的信息记录到beanDefintionMap中
        beanDefinitionByScan(configClass);
        // 初始化单例池
        initSingletonObjects();
    }

    /**
     * 给某个bean对象完成依赖注入
     */
    private void populateBeans(Object bean) {
        // 扫描beanDefintionMap中的bean信息,对bean对象中的属性进行依赖注入
        // 获取Class对象
        Class<?> clazz = bean.getClass();
        // 获取所有字段
        Field[] fields = clazz.getDeclaredFields();
        // 判断字段上是否有@Resource注解
        for (Field field : fields) {
            if (field.isAnnotationPresent(Resource.class)) {
                // 获取字段名
                String fieldName = field.getName();
                // 根据字段名获取bean对象
                Object beanObject = null;
                // 从beanDefintionMap中获取bean对象
                BeanDefintion beanDefintion = beanDefintionMap.get(fieldName);
                try {
                    // 根据bean的定义信息创建bean对象
                    beanObject = getBean(fieldName);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                // 设置字段可访问
                field.setAccessible(true);
                try {
                    // 依赖注入
                    field.set(bean, beanObject);
                    log.info("依赖注入成功:{} => {}.{}", beanObject.getClass(), clazz, fieldName);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 初始化单例池
     */
    private void initSingletonObjects() {
        // 将beanDefintionMap中的bean信息创建成bean对象放到单例池中
        beanDefintionMap.forEach((beanName, beanDefintion) -> {
            try {
                // 根据bean的定义信息创建bean对象
                Object bean = createBean(beanDefintion);
                if (bean != null) {
                    // 将bean对象放到单例池中
                    singletonObjects.put(beanName, bean);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        // 打印单例池中的bean对象
        log.info("根据bean定义信息初始化单例池:{}", singletonObjects);
    }

    // 返回容器中的对象
    public Object getBean(String name) throws Exception {
        BeanDefintion beanDefintion = beanDefintionMap.get(name);
        if (beanDefintion == null) {
            throw new NullPointerException("在bean定义中没有找到bean对象");
        }
        // 根据单例和多例来获取bean对象
        MyScope scope = beanDefintion.getScope();
        Object bean = null;
        if (scope == MyScope.SINGLETON) {
            log.info("getBean单例对象:{}", singletonObjects.get(name));
            // 单例就直接从单例池中获取对象
            bean = singletonObjects.get(name);
        } else {
            // 多例就创建一个新的对象
            bean = createProtoTypeBean(beanDefintion);
        }
        // 给bean对象完成依赖注入
        populateBeans(bean);
        // 记录当前对象,因为后置处理器可能会返回一个新的对象
        Object current = bean;
        // 初始化方法之前调用后置处理器 postProcessBeforeInitialization
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            Object bean1 = beanPostProcessor.postProcessBeforeInitialization(bean, name);
            // 如果beanPostProcessor返回的对象为空,则使用原来的对象
            if (bean1 != null) {
                current = bean1;
            }
        }
        // 初始化bean
        init(current);
        // 初始化方法之后调用后置处理器 postProcessAfterInitialization
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            bean = beanPostProcessor.postProcessAfterInitialization(current, name);
            // 如果beanPostProcessor返回的对象为空,则使用原来的对象
            if (bean == null) {
                bean = current;
            }
        }
        log.info("getBean多例对象:{}", bean);
        return bean;
    }

    /**
     * 初始化bean
     * @param bean
     */
    public void init(Object bean) {
        if (bean instanceof InitializingBean) {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

    /**
     * 根据bean的定义信息创建bean对象(单例bean)
     * @param beanDefintion
     * @return
     * @throws Exception
     */
    private Object createBean(BeanDefintion beanDefintion) throws Exception {
        // 得到bean的类型
        Class<?> clazz = beanDefintion.getClazz();
        // 根据bean的作用域创建bean对象,多例就不创建了,单例就创建
        if (beanDefintion.getScope() == MyScope.PROTOTYPE) {
            return null;
        }
        Object bean = clazz.getDeclaredConstructor().newInstance();
        return bean;
    }

    /**
     * 创建多例bean
     * @param beanDefintion
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     */
    private static Object createProtoTypeBean(BeanDefintion beanDefintion) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        // 多例就创建一个新的对象
        Class<?> clazz = beanDefintion.getClazz();
        Object bean = clazz.getDeclaredConstructor().newInstance();
        return bean;
    }

    /**
     * 完成bean的扫描,将bean的信息记录到beanDefintionMap中
     * @param configClass
     * @throws ClassNotFoundException
     */
    private void beanDefinitionByScan(Class<?> configClass) {
        // 传进来一个配置类的Class对象
        // 一、获取要扫描的包
        // 1.首先反射获取类的注解信息
        ComponentScan componentScan = configClass.getDeclaredAnnotation(ComponentScan.class);
        // 2.通过注解来获取要扫描的包的路径
        String path = componentScan.packagePath();
        log.info("扫描的包路径:{}", path);
        // 二、得到要扫描包的.class文件对象,从而得到全路径进行反射
        // 1.获取App类加载器
        ClassLoader classLoader = SunSpringApplicationContext.class.getClassLoader();
        // 2.获取要扫描包的真实路径,默认刚开始在根目录下
        path = path.replace(".", "/");
        URL resource = classLoader.getResource(path);
        // 3.由该路径创建一个文件对象,可使用resource.getFile()将URL类型转化为String类型
        File file = new File(resource.getFile());
        // 4.遍历该文件夹下的所有.class文件对象
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                // 反射注入容器
                // 1.获取所有文件的绝对路径
                String absolutePath = f.getAbsolutePath();
                // 只处理class文件
                if (absolutePath.endsWith(".class")) {
                    // 2.分割出类名
                    String className = extractClassName(absolutePath);
                    // 3.得到全路径
                    String fullPath = path.replace("/", ".") + "." + className;
                    // 4.判断是否需要注入容器,查看有没有自定义的注解Component
                    Class<?> aClass = null;
                    try {
                        aClass = classLoader.loadClass(fullPath);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                    // 如果该类使用了注解Component则说明是一个spring bean
                    if (aClass.isAnnotationPresent(Component.class)) {
                        log.info("扫描到Spring Bean:{}", aClass);

                        // 将Bean的后置处理器加入到beanPostProcessorList中
                        // 判断Class对象是否实现了BeanPostProcessor接口
                        if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                            Object o = null;
                            try {
                                o = aClass.getDeclaredConstructor().newInstance();
                            } catch (Exception e) {
                                log.info("BeanPostProcessor实例化失败:{}", e);
                            }
                            if (o instanceof BeanPostProcessor) {
                                beanPostProcessorList.add((BeanPostProcessor) o);
                            }
                            log.info("BeanPostProcessor实例化成功:{}", o);
                            // 直接跳过,不需要将BeanPostProcessor加入到beanDefintionMap中
                            continue;
                        }

                        // 将bean的信息记录到beanDefintionMap中
                        BeanDefintion beanDefintion = new BeanDefintion();
                        // 1.获取Scope注解的value值
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope scope = aClass.getDeclaredAnnotation(Scope.class);
                            MyScope value = scope.value();
                            // 放到beanDefintion中
                            beanDefintion.setScope(value);
                        } else {
                            // 如果没有指定作用域,则默认为单例
                            beanDefintion.setScope(MyScope.SINGLETON);
                        }
                        beanDefintion.setClazz(aClass);

                        // 2.获取Component注解的value值
                        Component component = aClass.getDeclaredAnnotation(Component.class);
                        String beanName = component.value();
                        if ("".equals(beanName)) {
                            // 如果没有指定value属性,则使用类名首字母小写作为bean的id
                            beanName = className.substring(0, 1).toLowerCase() + className.substring(1);
                        }

                        // 3.将bean的id和bean的信息放到beanDefintionMap中
                        beanDefintionMap.put(beanName, beanDefintion);
                    } else {
                        log.info("这不是一个Spring Bean={}", aClass);
                    }
                }
            }
        }
        // 打印beanDefintionMap中的bean信息
        log.info("将bean定义信息放到beanDefintionMap:{}", beanDefintionMap);
    }

    /**
     * 分割出类名
     * 类似于 com/sunxiansheng/sunspring/compent/MonsterService.class 的类名
     * @param filePath
     * @return
     */
    private String extractClassName(String filePath) {
        // 获取最后一个 '/' 的位置
        int lastSlashIndex = filePath.lastIndexOf('/');
        // 获取最后一个 '.' 的位置
        int lastDotIndex = filePath.lastIndexOf('.');
        // 提取两者之间的字符串作为类名
        return filePath.substring(lastSlashIndex + 1, lastDotIndex);
    }

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ZMQ请求应答模型

案例一 这个案例的出处是ZMQ的官网。请求段发送Hello&#xff0c;应答端回复World。 ZMQ Request(client) #include <string> #include <iostream> #include <zmq.hpp>using namespace std; using namespace zmq; // 使用 zmq 命名空间int main() {// ini…

PHP轻创推客集淘客地推任务平台于一体的综合营销平台系统源码

&#x1f680;轻创推客&#xff0c;营销新纪元 —— 集淘客与地推任务于一体的全能平台&#x1f310; &#x1f308;【开篇&#xff1a;营销新潮流&#xff0c;轻创推客引领未来】 在瞬息万变的营销世界里&#xff0c;你还在为寻找高效、全面的营销渠道而烦恼吗&#xff1f;&…

【STM32】C语言基础补充

学习过程中发现自己好些需要用到的C语言语法、特征都不太熟练了&#xff0c;特意记录一下&#xff0c;免得忘记了&#xff0c;以后遇到了新的也会继续更新 目录 1 全局变量 2 结构体 3 静态变量 4 memset()函数 5 使用8位的存储器存16位的数 1 全局变量…

汽车冷却液温度传感器

1、冷却液温度传感器的功能 发动机冷却液温度传感器&#xff0c;也称为ECT&#xff0c;是帮助保护发动机&#xff0c;提高发动机工作效率以及帮助发动机稳定运行的非常重要的传感器之一。 发动机冷却液温度 &#xff08;ECT&#xff09; 传感器用于测量发动机的冷却液温度&…

Vue项目创建和使用

快速上手 | Vue.js (vuejs.org) nodejs.org/ vue项目实质上是index.html页面和多个js文件的集合&#xff0c;最终解析后的html和js代码可以由浏览器解析运行&#xff1a; vue项目的创建&#xff0c;需要脚手架工具来搭建&#xff1b; 在编译的源码阶段&#xff0c;文件格式为.…

集团数字化转型方案(十二)

集团数字化转型方案致力于通过构建一个集成化的数字平台&#xff0c;全面应用大数据分析、人工智能、云计算和物联网等前沿技术&#xff0c;推动业务流程、管理模式和决策机制的全面升级。该方案将从业务流程的数字化改造开始&#xff0c;优化资源配置&#xff0c;提升运营效率…

MySQL的源码安装及基本部署(基于RHEL7.9)

这里源码安装mysql的5.7.44版本 一、源码安装 1.下载并解压mysql , 进入目录: wget https://downloads.mysql.com/archives/get/p/23/file/mysql-boost-5.7.44.tar.gz tar xf mysql-boost-5.7.44.tar.gz cd mysql-5.7.44/ 2.准备好mysql编译安装依赖: yum install cmake g…

牛客网习题——通过C++实现

一、目标 实现下面4道练习题增强C代码能力。 1.求123...n_牛客题霸_牛客网 (nowcoder.com) 2.计算日期到天数转换_牛客题霸_牛客网 (nowcoder.com) 3.日期差值_牛客题霸_牛客网 (nowcoder.com) 4.打印日期_牛客题霸_牛客网 (nowcoder.com) 二、对目标的实现 1.求123...n_…

杰发科技AC7840——CAN通信简介(8)_通过波特率和时钟计算SEG_1/SEG_2/SJW/PRESC

通过公式计算 * 波特率和采样点计算公式&#xff1a;* tSeg1 (S_SEG_1 2); tSeg2 (S_SEG_2 1).* BandRate (SCR_CLK / (S_PRESC 1) / ((S_SEG_1 2) (S_SEG_2 1))), SCR_CLK 为CAN 模块源时钟* SamplePoint (tSeg1 / (tSeg1 tSeg2)). {0x0D, 0x04, 0x04, 0x3B},…

[创业之路-140] :生产 - 生产流程概述

目录 前言&#xff1a; 一、生产流程的基本框架 二、生产流程的关键要素 前言&#xff1a; 生产流程是一个广泛的概念&#xff0c;它涵盖了从原材料投入到成品产出的全过程。 这个过程在不同行业和领域中有所不同&#xff0c;但通常都包括一系列有序的步骤和环节。 以下是…

WIN 10 添加右键菜单(VSCode 打开当前目录)

WIN 10 添加右键菜单&#xff08;VSCode 打开当前目录&#xff09; 前言最终效果操作步骤 前言 每次打开代码都需要先打开 VSCode&#xff0c;再选择最近打开的项目或者浏览打开项目&#xff0c;感觉比较难找。所以自己添加了右键命令。 最终效果 操作步骤 cmd 打开注册表 找…

C语言程序设计(初识C语言后部分)

十七&#xff0c;数组 1.一维数组的创建和初始化 1&#xff09;数组的创建 数组是一组相同类型元素的集合 数组的创建方式&#xff1a; type_t arr_name [const_n]; //type_t 是指数组的元素类型 //const_n 是一个常量表达式&#xff0c;用来指定数组大小 …

R语言统计分析——回归分析的改进措施

参考资料&#xff1a;R语言实战【第2版】 如果在回归诊断中发现了问题&#xff0c;我们该如何做&#xff1f;有四种方法可以处理违背回归假设的问题&#xff1a; ①删除观测点&#xff1b; ②变量变换&#xff1b; ③添加或删除变量&#xff1b; ④使用其他回归方法。 1、…

C语言 ——— 常见的动态内存错误(下篇)

目录 使用free释放一块动态开辟内存的一部分 对同一块动态内存多次释放 动态开辟内存忘记释放&#xff08;内存泄漏&#xff09; 使用free释放一块动态开辟内存的一部分 代码演示&#xff1a; // 动态开辟10个整型空间 int* p (int*)malloc(sizeof(int) * 10);// 判断是…

Pod基础使用

POD基本操作 1.Pod生命周期 在Kubernetes中&#xff0c;Pod的生命周期经历了几个重要的阶段。下面是Pod生命周期的详细介绍&#xff1a; Pending&#xff08;待处理&#xff09;: 调度: Pod被创建后&#xff0c;首先进入“Pending”状态。此时&#xff0c;Kubernetes的调度器…

机器视觉运动控制一体机VPLC532E在汽车胶带缠绕的开放式CNC应用

市场应用背景 在汽车线束和零配件中&#xff0c;胶带缠绕是一种常见且重要的加工工艺&#xff0c;主要用于线束/零配件的捆扎、固定、绝缘保护等应用。在缠绕头控制下&#xff0c;胶带均匀缠绕在汽车线束/零配件表面&#xff0c;完成缠绕后&#xff0c;系统自动执行切割。汽车…

【CSS】使用 CSS 自定义属性(变量)-- var()

自定义属性&#xff08;有时候也被称作CSS 变量或者级联变量&#xff09;是由 CSS 作者定义的&#xff0c;它包含的值可以在整个文档中重复使用。由自定义属性标记设定值&#xff08;比如&#xff1a; --main-color: black;&#xff09;&#xff0c;由 var() 函数来获取值&…

qtsql连接达梦数据库

odbc window和linux都有odbc的中间件&#xff0c;可以通过odbc中间件配合qtsql连接数据库 windows下配置odbc linux配置odbc apt install unixodbc unixodbc-dev /etc/odbcinst.ini配置 [DM8 ODBC DRIVER] DescriptionDM8 ODBC Driver DRIVER/opt/dmdbms/bin/libdodbc.so/et…

安装打印机提示“打印后台程序服务没有启动”的解决方法

1、在桌面选中“我的电脑”&#xff0c;鼠标右键选择“管理”&#xff1b; 2、在“管理”窗口中&#xff0c;选取“服务和应用程序”扩展项目下的“服务”选项&#xff08;如图&#xff09;&#xff0c;在右侧出现系统服务列表中找到“Print Spooler”服务&#xff0c;双击进入…

JavaWeb基础 -- Servlet

JavaWeb基础 – Servlet 1.Servlet简介 1.1 Servlet是什么 Servlet本身是用Java编写的&#xff0c;运行在Web服务器上的应用程序&#xff0c;并作为Web浏览器和其他HTTP客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。Servlet可以收集来自网页表单输入的数据…