Spring 自定义注解 为 BeanDefinition 添加 qualifier 信息 从而约束自动装配范围

为什么写这篇文章

        Spring 支持类型注入,并且可以通过Qualifier 或者Mate 调整类型注入的范围。但是通过自定义注解结合现有的 @Qualifier 使用起来有种种困难。

  • 将 @Qualifier 融合在自定义注解中,在使用 @AliasFor 遇到问题
  • 仅仅检查注解中的一部分内容是否匹配即可,Spring 不支持

添加自定义 Qualifier

package ann;

import java.lang.annotation.*;

/**
 * @author Jay
 */
@Target({ElementType.PARAMETER,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HandlerSelect {
    String value() default "";
}

组合 Qualifier 注解

package ann;

import java.lang.annotation.*;

/**
 * @author Jay
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MessageHandlerAnn {
    HandlerSelect qualifier();

    /**
     * 通过SpEL 表达式计算 #messageExt 的相关信息是否满足所有条件
     *
     * @return
     */
    String[] eLFilter() default "";

}

注册自定义限定符

    @Bean
    public CustomAutowireConfigurer customAutowireConfigurer() {
        CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
        configurer.setCustomQualifierTypes(Collections.singleton(HandlerSelect.class));
        return configurer;
    }

自定义注解处理工厂处理器

package config;

import ann.HandlerSelect;
import ann.MessageHandlerAnn;
import message.handler.MessageHandler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.stereotype.Component;

/**
 * @author Jay
 */
@Component
public class HandlerSelectQualifierProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 检查 实现了 MessageHandler 接口的 SpringBean 检查其是否标注有 MessageHandlerAnn 注解
        String[] beanNamesForType = beanFactory.getBeanNamesForType(MessageHandler.class);

        for (String beanName : beanNamesForType) {
            // 获取 Bean 定义对象
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            Class<?> beanClass = beanFactory.getType(beanName);

            // 检查是否有 MessageHandlerAnn 注解
            if (beanClass != null && beanClass.isAnnotationPresent(MessageHandlerAnn.class)) {
                // 创建并添加自定义的 qualifier 元素
                addCustomQualifier((AbstractBeanDefinition) beanDefinition, beanClass);
            }
        }
    }

    private static void addCustomQualifier(AbstractBeanDefinition beanDefinition, Class<?> beanClass) {
        // 创建 AutowireCandidateQualifier 实例(HandlerSelect 已经加入到 QualifierType 中)
        AutowireCandidateQualifier qualifier = new  AutowireCandidateQualifier(HandlerSelect.class);
        // 设置 qualifier 的值,例如:
        HandlerSelect qualifierValue = beanClass.getAnnotation(MessageHandlerAnn.class).qualifier();
        //  Attribute 的 Key 固定为 value, 其 value 值取自 HandlerSelect.qualifier.value
        qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, qualifierValue.value());

        // TODO 以上处理仅考虑 HandlerSelect 仅仅有一个限定描述符的场景。 如果需要拓展 HandlerSelect  的属性, 需要重新设计以上代码     

        // 添加限定符
        beanDefinition.addQualifier(qualifier);
    }
}

 MyMessageListener & MessageHandler

public interface MessageHandler {

}


public class MyMessageListener implements MessageListenerConcurrently, InitializingBean {

    final private List<MessageHandler<?>> messageHandlerList;

    public MyMessageListener(List<MessageHandler<?>> messageHandlerList) {
        this.messageHandlerList = messageHandlerList;
    }
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        for (MessageExt messageExt : msgs) {
        // ...
        }
    }
    
    @Override
    public void afterPropertiesSet() {
        System.out.println("MyMessageListener afterPropertiesSet");
        // messageHandlerList 不能为空
        if (ObjectUtils.isEmpty(this.messageHandlerList)) {
            log.error("messageHandlerList is empty");
            throw new RuntimeException("messageHandlerList is empty");
        } else {
            messageHandlerList.forEach(messageHandler -> {
                System.out.println(messageHandler.getClass().getName());
            });
        }
    }
}

配置&效果

待完善的地方 

  • 用于解析 @HandlerSelect 的 HandlerSelectQualifierProcessor 不能随着 HandlerSelect 的属性增加而 解析属性并增加对应的限定符
  • IDEA 工具无法预估 使用自定义的限定符号 还有哪些Bean

(截图红框显示为三个, 但实际myMessageListener0 依赖注入了T0T0、ToT1 而myMessageListener1 依赖注入了T1T0 )

相关博客: rocketmq Listener 还可以这样配置(基于SPEL\限定符号\自动连线)icon-default.png?t=N7T8http://t.csdnimg.cn/sE1Dk

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

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

相关文章

2024年北京通信展|北京国际信息通信展览会|北京PT展

2024年北京通信展|北京国际信息通信展览会|北京PT展 2024年中国国际信息通信展览会&#xff08;PTEXPO&#xff09;&#xff0c;是由工业和信息化部主办的ICT行业盛会&#xff0c;自1990年创办以来&#xff0c;已成功举办31届&#xff0c;是反映信息通信行业发展最新成果的重要…

最小质因数 == 最大质因数,不等式秒了!

起因&#xff1a; 在洛谷做题遇到了这道题~ 一看咿呀&#xff0c;又是道数学题~ 首先我们要了解一下&#xff0c;什么是质数&#xff1f; 我记得好像有年高考题的前几题好像考了这玩意来着&#xff0c;质数的概念好像在小学学过&#xff0c;上了初中后基本都没有用过了~ 质数就…

error lsof 0.1 does not meet the minimal requirement

很多小伙伴在linux虚拟机中采用Centos 7镜像安装TitanIDE时&#xff0c;会报错如下信息 error lsof 0.1 does not meet the minimal requirement 这是因为lsof依赖版本较低&#xff0c;只需要在命令行输入 sudo yum install lsof 按下回车以后&#xff0c;命令行会弹出提示命令…

C语言例1-7:以下程序段中执行循环的次数是

代码如下&#xff1a; x-2; do { xx*x; } while(!x); 执行循环次数是&#xff1a;1 先执行后判断 代码如下&#xff1a; #include<stdio.h> int main(void) {int x;x-2;do{ xx*x; printf("\n");printf("x %d\n",x);}while(!x);return 0; } 结果…

Linux - 第三节

改变用户类型 su 仅单纯的进行身份变化 依旧处于普通用户里面 su - 进行重新登录更改身份 退出用exit / ctrld su 用户名 改成成其他身份 对一条命令进行提权 sudo command r:可读 w:可写 x:可执行 -:对应的权限位置&#xff0c;没有权限 去掉所有权限 chmod u…

2024 ccfcsp认证打卡【汇总】

202312-1 仓库规划 202312-2 因子化简 202312-3 树上搜索 202309-1 坐标变换&#xff08;其一&#xff09; 202309-2 坐标变换&#xff08;其二&#xff09; 202305-1 重复局面 202305-2 矩阵运算 202303-1 田地丈量 202303-2 垦田计划 202212-1 现值计算 202212-2 训练计划 20…

Redis实战篇-利用逻辑过期解决缓存击穿问题

实战篇Redis 3.0 、利用逻辑过期解决缓存击穿问题 需求&#xff1a;修改根据id查询商铺的业务&#xff0c;基于逻辑过期方式来解决缓存击穿问题 思路分析&#xff1a;当用户开始查询redis时&#xff0c;判断是否命中&#xff0c;如果没有命中则直接返回空数据&#xff0c;不…

打工人神器! Raccoon 代码小浣熊

继这三个之后&#xff0c;今天又来了一个 [ Raccoon代码小浣熊 ] 核心精要与产品特点 全面支持多种编程语言和IDE&#xff1a;「代码小浣熊」支持超过90种主流编程语言&#xff0c;包括但不限于Python、Java、JavaScript、C、Go和SQL等。同时&#xff0c;它集成了市面上主流的…

基于jsp+mysql+Spring+hibernate+的SSH在线学习交流论坛平台

基于jspmysqlSpringhibernate的SSH在线学习交流论坛平台 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末…

Unreal的Quixel Bridge下载速度过慢、下载失败

从Quixel Bridge下载MetaHuman模型&#xff0c;速度非常慢&#xff0c;而且经常下载失败&#xff0c;从头下载。 可以从Quixel Bridge的右上角我的图标->Support->Show Logs打开日志目录 downloaded-assets目录下为下载的资源 bridge-plugin.log文件记录了下载URL和下载…

java(1)之环境部署

1、下载安装包 直接百度java 点击这个就可以&#xff0c;进去之后下载&#xff0c;根据自身情况&#xff0c;window就下Windows版本的记得下那个jdk别下别的&#xff08;用不了&#xff09;&#xff0c;然后下一个编译器可以是idea可以是eclipse都可以 2、环境搭建 分为两步…

如何确保实物档案的安全

确保实物档案的安全有以下几个关键点&#xff1a; 1. 建立完善的安全措施&#xff1a;为实物档案建立专门的存储区域&#xff0c;控制进出口&#xff0c;限制访问权限&#xff0c;并使用安全锁和监控设备等物理安保措施。 2. 规范档案管理制度&#xff1a;建立档案管理制度&…

算法学习——LeetCode力扣动态规划篇10

算法学习——LeetCode力扣动态规划篇10 583. 两个字符串的删除操作 583. 两个字符串的删除操作 - 力扣&#xff08;LeetCode&#xff09; 描述 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个…

使用 golang 以及 Gin 框架,将上传的图片在不保存至本地的情况下添加水印,并上传至阿里云 OSS

正如标题所述&#xff0c;使用golang对上传图片添加水印&#xff0c;以及将图片上传到阿里云OSS&#xff0c;网上一搜索&#xff0c;便有你想要的结果了&#xff0c;可是&#xff0c;他们却先将上传图片添加水印后保存在本地&#xff0c;而后再将添加了水印的图片上传到阿里云O…

【个人笔记】python界面美化

目录 标题栏美化 样例展示 代码 配套鼠标移动 完整展示 标题栏美化 样例展示 代码 import tkinter as tk from tkinter import ttk from PIL import Image, ImageTk import subprocess import sysdef open_buy_quantity():window.destroy()subprocess.run(["p…

网际协议 - IP

文章目录 目录 文章目录 前言 1 . 网际协议IP 1.1 网络层和数据链路层的关系 2. IP基础知识 2.1 什么是IP地址? 2.2 路由控制 3. IP地址基础知识 3.1 IP地址定义 3.2 IP地址组成 3.3 IP地址分类 3.4 子网掩码 IP地址分类导致浪费? 子网与子网掩码 3.5 CIDR与…

记录个人学习golang路线(如何学习golang,如何转golang)

最近好久没更&#xff0c;在看兔兔的博客&#xff0c;学习golang&#xff0c;兔兔的文章&#xff0c;有一定的编程经验 && 初学golang者&#xff0c;一定要看&#xff0c;如果是其他语言转golang&#xff0c;那就必须要看了&#xff0c;可以帮助你了解golang的语法&…

BC40056 Imports“SolidWorks.Interop.swconst”中指定的命名空间或类型不包含任何公共成员

BC40056 Imports“SolidWorks.Interop.swconst”中指定的命名空间或类型不包含任何公共成员&#xff0c;或者找不到该命名空间或类型。 问题描述原因分析 解决办法 ) 问题描述 严重性 代码 说明 项目 文件 行 警告 BC40056 Imports“SolidWorks.Interop.swconst”中指定的命名…

单链表就地逆置

算法思想&#xff1a;构建一个带头结点的单链表L&#xff0c;然后访问链表中的每一个数据结点&#xff0c;将访问到的数据结点依此插入到L的头节点之后。 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> typedef int ElemType; typedef s…

ElasticSearch开发指北和场景题分析

前言 本篇是ES系列的第二篇&#xff0c;继上次的理论篇ElasticSearch理论体系构建后&#xff0c;带来了实战篇。实战篇来自于我对常见操作以及场景的分析总结&#xff0c;详细到每个步骤和理由&#xff0c;下一篇将是性能优化篇。 常用操作 以下操作均使用ES的API进行展示&a…