实现BeanPostProcessor

2024-08-23 12:44
文章标签 实现 beanpostprocessor

本文主要是介绍实现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对象*/@Resourceprivate MonsterDao monsterDao;public void query() {log.info("MonsterService调用MonsterDao");monsterDao.query();}/*** 初始化方法*/@Overridepublic 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);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {log.info("SunBeanProcessor postProcessBeforeInitialization..." + beanName + "=>" + bean.getClass());// 返回空,不对bean进行处理return null;}@Overridepublic 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;// 初始化方法之前调用后置处理器 postProcessBeforeInitializationfor (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {Object bean1 = beanPostProcessor.postProcessBeforeInitialization(bean, name);// 如果beanPostProcessor返回的对象为空,则使用原来的对象if (bean1 != null) {current = bean1;}}// 初始化beaninit(current);// 初始化方法之后调用后置处理器 postProcessAfterInitializationfor (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.判断是否需要注入容器,查看有没有自定义的注解ComponentClass<?> aClass = null;try {aClass = classLoader.loadClass(fullPath);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}// 如果该类使用了注解Component则说明是一个spring beanif (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的idbeanName = 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);}}

这篇关于实现BeanPostProcessor的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1099417

相关文章

MySQL中查找重复值的实现

《MySQL中查找重复值的实现》查找重复值是一项常见需求,比如在数据清理、数据分析、数据质量检查等场景下,我们常常需要找出表中某列或多列的重复值,具有一定的参考价值,感兴趣的可以了解一下... 目录技术背景实现步骤方法一:使用GROUP BY和HAVING子句方法二:仅返回重复值方法三:返回完整记录方法四:

IDEA中新建/切换Git分支的实现步骤

《IDEA中新建/切换Git分支的实现步骤》本文主要介绍了IDEA中新建/切换Git分支的实现步骤,通过菜单创建新分支并选择是否切换,创建后在Git详情或右键Checkout中切换分支,感兴趣的可以了... 前提:项目已被Git托管1、点击上方栏Git->NewBrancjsh...2、输入新的分支的

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

关于集合与数组转换实现方法

《关于集合与数组转换实现方法》:本文主要介绍关于集合与数组转换实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Arrays.asList()1.1、方法作用1.2、内部实现1.3、修改元素的影响1.4、注意事项2、list.toArray()2.1、方

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

java实现docker镜像上传到harbor仓库的方式

《java实现docker镜像上传到harbor仓库的方式》:本文主要介绍java实现docker镜像上传到harbor仓库的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 前 言2. 编写工具类2.1 引入依赖包2.2 使用当前服务器的docker环境推送镜像2.2

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Java easyExcel实现导入多sheet的Excel

《JavaeasyExcel实现导入多sheet的Excel》这篇文章主要为大家详细介绍了如何使用JavaeasyExcel实现导入多sheet的Excel,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录1.官网2.Excel样式3.代码1.官网easyExcel官网2.Excel样式3.代码

python实现对数据公钥加密与私钥解密

《python实现对数据公钥加密与私钥解密》这篇文章主要为大家详细介绍了如何使用python实现对数据公钥加密与私钥解密,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录公钥私钥的生成使用公钥加密使用私钥解密公钥私钥的生成这一部分,使用python生成公钥与私钥,然后保存在两个文

浏览器插件cursor实现自动注册、续杯的详细过程

《浏览器插件cursor实现自动注册、续杯的详细过程》Cursor简易注册助手脚本通过自动化邮箱填写和验证码获取流程,大大简化了Cursor的注册过程,它不仅提高了注册效率,还通过友好的用户界面和详细... 目录前言功能概述使用方法安装脚本使用流程邮箱输入页面验证码页面实战演示技术实现核心功能实现1. 随机