当前位置: 首页 > news >正文

对Spring的后置处理器BeanPostProcessor的使用

BeanPostProcessor官方定义为工厂钩子,我们也俗称后置处理器。它允许自定义修改新的bean实例,例如检查标记接口或用代理包装它们。应用程序上下文可以在其bean定义中自动检测BeanPostProcessor bean,并将它们应用于随后创建的任何bean。

一、BeanPostProcessor

BeanPostProcessor类是spring的原生接口

Factory hook that allows for custom modification of new bean instances
允许自定义修改新bean实例的工厂钩子
postProcessBeforeInitialization bean初始化之前执行的方法
postProcessAfterInitialization bean初始化之后执行的方法
public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

二、源码

在spring源码类org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory的方法initializeBean中,我们可以看到为什么会这样执行。

在这里插入图片描述

根据以上代码,我们得知,在invokeInitMethods的执行前后,spring会分别调用所有的BeanPostProcessor,执行其中的方法,那么invokeInitMethods的具体内容我们仍需要看下,发现此方法主要作用有两个:1、判断bean是否继承了InitializingBean,如果继承接口,执行afterPropertiesSet()方法,2、获得是否设置了init-method属性,如果设置了,就执行设置的方法

以上就是spring的后置处理器的简单的使用方法以及执行时机。
————————————————
版权声明:本文为CSDN博主「梵法利亚」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wg22222222/article/details/122879915

三、使用

BeanPostProcessor是接口,我们就可以创建一个类(比如SimpleBeanPostProcessor )来实现这个接口重写他的方法;

@Component
public class SimpleBeanPostProcessor implements BeanPostProcessor {
 @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("==========前置开启postProcessBeforeInitialization=========");
        System.out.println("=====================" + bean.getClass().getName() + " - " + beanName);
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("============后置开启postProcessBeforeInitialization==============");
        System.out.println("=====================" + bean.getClass().getName() + " - " + beanName);
        return null;
}

但想要我们自己定义的bean的后置处理器起作用,需要我们把其放入spring容器,所以记得加@Component注解

定义一个bean,为mybean

package com.example.flowdemo.pojo;

import lombok.Data;

/**
 * @author lc
 * @version 1.0
 * @date 2022/10/11 11:38
 */
@Data
public class MyBean {
    private String beanName;
    private String className;
    public MyBean() {
        System.out.println("MyBean constructor");
    }
    public void init() {
        System.out.println("MyBean is init");
    }
}



启动类

@SpringBootApplication
@EnableWebMvc
public class FlowdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(FlowdemoApplication.class, args);
    }
/**
     * 通过@Bean的方式可以指定initMethod
     * @return
     */
    @Bean(initMethod = "init")
    public MyBean mybean() {
        return new MyBean();
    }
}

启动会发现

在这里插入图片描述
从打打印结果可以看出,后置处理器在bean的构造方法执行之后执行。而且后置处理器的方法postProcessBeforeInitialization和postProcessAfterInitialization分别在Bean的init方法前后执行。并且BeanPostProcessor后置处理器会对spring中所有的bean起作用

四、实战

我们可以结合业务场景来设置,比如,每一个bean都要有不重复的唯一id;

首先自定义一个注解,可以定义一个value属性,作为隔离业务的标识:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface IdGeneratorClient {
    /**
     * ID 生成器名称
     *
     * @return
     */
    String value() default "DEFAULT";
}

定义 ID 生成器接口

public interface IdGenerator {
    String groupName();

    long nextId();
}

实现 ID 生成器接口,偷懒使用AtomicLong实现自增,同时考虑 ID 生成器是分组的,通过ConcurrentHashMap实现 ID 生成器的持有

class DefaultIdGenerator implements IdGenerator {
    private static final Map<String, AtomicLong> ID_CACHE = new ConcurrentHashMap<>(new HashMap<>());
    private final String groupName;

    DefaultIdGenerator(final String groupName) {
        this.groupName = groupName;
        synchronized (ID_CACHE) {
            ID_CACHE.computeIfAbsent(groupName, key -> new AtomicLong(1));
        }
    }

    @Override
    public String groupName() {
        return this.groupName;
    }

    @Override
    public long nextId() {
        return ID_CACHE.get(this.groupName).getAndIncrement();
    }
}



如前面设计的,我们需要一个工厂类来创建 ID 生成器

public enum IdGeneratorFactory {
    INSTANCE;

    private static final Map<String, IdGenerator> ID_GENERATOR_MAP = new ConcurrentHashMap<>(new HashMap<>());

    public synchronized IdGenerator create(final String groupName) {
        return ID_GENERATOR_MAP.computeIfAbsent(groupName, key -> new DefaultIdGenerator(groupName));
    }
}



前面都是属于基本操作,这里才是扩展的核心。我们的实现逻辑是:
扫描 bean 的所有属性,然后找到定义了IdGeneratorClient注解的属性
获取注解的value值,作为 ID 生成器的分组标识
使用IdGeneratorFactory这个工厂类生成 ID 生成器实例,这里会返回新建的或已经定义的实例
通过反射将 ID 生成器实例写入 bean

定义 BeanPostProcessor

public class IdGeneratorBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
        parseFields(bean);
        return bean;
    }

    private void parseFields(final Object bean) {
        if (bean == null) {
            return;
        }
        Class<?> clazz = bean.getClass();
        parseFields(bean, clazz);

        while (clazz.getSuperclass() != null && !clazz.getSuperclass().equals(Object.class)) {
            clazz = clazz.getSuperclass();
            parseFields(bean, clazz);
        }
    }

    private void parseFields(final Object bean, Class<?> clazz) {
        if (bean == null || clazz == null) {
            return;
        }

        for (final Field field : clazz.getDeclaredFields()) {
            try {
                final IdGeneratorClient annotation = AnnotationUtils.getAnnotation(field, IdGeneratorClient.class);
                if (annotation == null) {
                    continue;
                }

                final String groupName = annotation.value();

                final Class<?> fieldType = field.getType();
                if (fieldType.equals(IdGenerator.class)) {
                    final IdGenerator idGenerator = IdGeneratorFactory.INSTANCE.create(groupName);
                    invokeSetField(bean, field, idGenerator);
                    continue;
                }

                throw new RuntimeException("未知字段类型无法初始化,bean: " + bean + ",field: " + field);
            } catch (Throwable t) {
                throw new RuntimeException("初始化字段失败,bean=" + bean + ",field=" + field, t);
            }
        }
    }

    private void invokeSetField(final Object bean, final Field field, final Object param) {
        ReflectionUtils.makeAccessible(field);
        ReflectionUtils.setField(field, bean, param);
    }
}



测试用例,验证我们的实现是否生效

 <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>



import cn.hutool.core.lang.Assert;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringBeanPostProcessorApplicationTests {
    private IdGenerator defaultIdGenerator = new DefaultIdGenerator("defualt");
    private IdGenerator group1IdGenerator = new DefaultIdGenerator("group1");

    @Test
    void contextLoads() {
        Assert.notNull(defaultIdGenerator, "注入失败");
        System.out.println(defaultIdGenerator.groupName() + " => " + defaultIdGenerator.nextId());

        Assert.notNull(group1IdGenerator, "注入失败");
        for (int i = 0; i < 5; i++) {
            System.out.println(defaultIdGenerator.groupName() + " => " + defaultIdGenerator.nextId());
            System.out.println(group1IdGenerator.groupName() + " => " + group1IdGenerator.nextId());
        }
    }

}



相关文章:

  • 【day9】【洛谷算法题】-P2433小学数学N合一-刷题反思集[入门2分支结构]
  • halcon脚本-深度学习【目标检测】
  • 线上服务器内存飙升怎么排查?
  • xss-lab通关之路
  • FPGA手写一个动态方块视频,用来代替摄像头输入,私信我送代码
  • 海思3559万能平台搭建:DDR移植的一些问题
  • Baklib知识分享|制作网站FAQ需要注意哪些内容?
  • 第十四届蓝桥杯备赛模板题——蓝桥部队 (带权并查集)
  • [推荐系统] 1. 深度学习与推荐系统
  • 设计模式(合)
  • 【k8s】五、Pod生命周期(一)
  • C++彻底搞定面试--多态篇,博主呕心沥血之作,万字解析,弄透多态
  • 在小红书要怎样推广,才能使效果收益最大化?
  • C语言文件操作【续】【进阶】
  • 【重温Python】一、Python中的内置数据结构
  • @jsonView过滤属性
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • learning koa2.x
  • leetcode46 Permutation 排列组合
  • PAT A1050
  • Python - 闭包Closure
  • rabbitmq延迟消息示例
  • Spring Cloud Feign的两种使用姿势
  • tab.js分享及浏览器兼容性问题汇总
  • Unix命令
  • 基于组件的设计工作流与界面抽象
  • 将回调地狱按在地上摩擦的Promise
  • 警报:线上事故之CountDownLatch的威力
  • 理清楚Vue的结构
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • 2017年360最后一道编程题
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • #1015 : KMP算法
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • (39)STM32——FLASH闪存
  • (70min)字节暑假实习二面(已挂)
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (待修改)PyG安装步骤
  • (二)PySpark3:SparkSQL编程
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (剑指Offer)面试题34:丑数
  • (论文阅读26/100)Weakly-supervised learning with convolutional neural networks
  • (小白学Java)Java简介和基本配置
  • (学习日记)2024.03.25:UCOSIII第二十二节:系统启动流程详解
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • (原創) 未来三学期想要修的课 (日記)
  • (转)http协议
  • (转)可以带来幸福的一本书
  • **python多态
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .Net - 类的介绍
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题