Spring 自定义 CustomQualifier

为什么写这篇文章

        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/493554.html

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

相关文章

Linux系统使用Docker部署Jupyter Notebook结合内网穿透实现公网访问本地笔记

文章目录 1. 选择与拉取镜像2. 创建容器3. 访问Jupyter工作台4. 远程访问Jupyter工作台4.1 内网穿透工具安装4.2 创建远程连接公网地址4.3 使用固定二级子域名地址远程访问 本文主要介绍如何在Ubuntu系统中使用Docker本地部署Jupyter Notebook&#xff0c;并结合cpolar内网穿透…

YOLOv9改进策略:IoU优化 | Wasserstein Distance Loss,助力小目标涨点

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文独家改进&#xff1a;基于Wasserstein距离的小目标检测评估方法 Wasserstein Distance Loss | 亲测在多个数据集能够实现涨点&#xff0c;对小目标、遮挡物性能提升明显 &#x1f4a1;&#x1f4a1;&#x1f4a1;MS COCO和PASC…

【Linux】模拟实现shell(bash)

目录 常见的与shell互动场景 实现代码 全部代码 homepath()接口 const char *getUsername()接口 const char *getHostname()接口 const char *getCwd()接口 int getUserCommand(char *command, int num)接口 void commandSplit(char *in, char *out[])接口 int execut…

TCP重传机制详解——02SACK

文章目录 TCP重传机制详解——02 SACKSACK是什么&#xff1f;为什么要有SACK&#xff1f;实际场景抓包具体显示信息流程 实战抓包讲解SACK关闭场景下&#xff0c;三次重复ACK后会快速重传SACK打开但是不携带SACK块信息场景下&#xff0c;三次重复ACK也不会快速重传SACK打开并且…

P8649 [蓝桥杯 2017 省 B] k 倍区间:做题笔记

目录 思路 代码思路 代码 推荐 P8649 [蓝桥杯 2017 省 B] k 倍区间 思路 额嗯&#xff0c;这道题我刚上来是想到了前缀和&#xff0c;但是还要判断每个子序列&#xff0c;我就两层for嵌套&#xff0c;暴力解了题。就是我知道暴力肯定过不了但是写不出来其他的[留下了苦…

Transformer的前世今生 day09(Transformer的框架概述)

前情提要 编码器-解码器结构 如果将一个模型分为两块&#xff1a;编码器和解码器那么编码器-解码器结构为&#xff1a;编码器负责处理输入&#xff0c;解码器负责生成输出流程&#xff1a;我们先将输入送入编码器层&#xff0c;得到一个中间状态state&#xff0c;并送入解码器…

搜索与图论——Dijkstra

最短路算法 稠密图与稀疏图 n为点数&#xff0c;m为边数。m远小于n的平方为稀疏图&#xff0c;m接近n的平方为稠密图。 稀疏图用邻接表存&#xff0c;稠密图用邻接矩阵存 朴素版dijkstra时间复杂度为O(n^2),对于稠密图可以ac&#xff0c;但遇到稀疏图时会TLE。 dijkstra函数实…

核工业核级树脂的应用

#核工业核级树脂的应用 ​压水堆核电厂核能级树脂主要用于反应堆冷却剂净化系统、乏燃料池冷却水净化系统、蒸汽发生器排污水处理、硼回收系统、放射性废水排放系统等。 核级树脂是压水堆核电厂一回路及其辅助系统的主要水处理材料&#xff0c;用于净化运行过程中产生的裂变产物…

【LaTeX】7实现章节跳转

使用 LaTeX 实现章节跳转 写在最前面1. 引入 hyperref 包2. 标记章节3. 引用章节示例代码注意 小技巧总结 &#x1f308;你好呀&#xff01;我是 是Yu欸 &#x1f30c; 2024每日百字篆刻时光&#xff0c;感谢你的陪伴与支持 ~ &#x1f680; 欢迎一起踏上探险之旅&#xff0c;…

C语言牛客网BC-37 牛牛的圆(求面积)

题目如下 代码实现 #include<stdio.h> int main() { float r 0;float s 0;scanf("%f",&r);s 3.14*r*r;printf("%.2f",s);return 0; } 创作不易&#xff0c;点点关注&#xff0c;感谢支持&#xff01;&#xff01;&#xff01;

leetcode代码记录(平衡二叉树

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 给定一个二叉树&#xff0c;判断它是否是 平衡二叉树 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;…

Codeforces Round 842 (Div. 2) D. Lucky Permutation

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18, maxm 4e4 5; c…

相位解包裹前识别有效区域和无效区域(条纹和背景区域区分)

对于不连续场进行相位解包的时候,首先要识别出图象中的哪些部分为有效数据,哪些部分为非有效数据"。这不仅关乎着相位解包算法的速度,更影响着解包算法的精度。因此在解包之前,对有效区域和无效区域的判断必须是首先要做的一件事情。下面就来介绍一下什么是有效区域和…

AI智能分析网关智慧食安监管系统方案

3.15晚会刚过不久&#xff0c;淀粉肠的“屈辱”终于得以洗清&#xff0c;但某些品牌奶茶、梅菜扣肉、预制菜等等&#xff0c;生产过程仍是触目惊心。如何提升食品安全管理水平&#xff0c;保障食品从生产到消费环节的质量和安全&#xff1f;TSINGSEE青犀智利用智能分析网关V4Ea…

主流公链 - Solana

探索Solana区块链&#xff1a;下一代高性能区块链平台 1. Solana简介 Solana是一个高性能的区块链平台&#xff08;TPS能达到10W级别&#xff09;&#xff0c;旨在实现高吞吐量和低延迟的区块链交易处理。它采用了一系列创新技术&#xff0c;其中包括Proof of History (PoH)&a…

【进程控制】超详细讲解wait和waitpid的原理(结合代码)

文章目录 前言waipid函数参数option什么叫非阻塞等待&#xff1f;参数status wait 函数 前言 在了解了进程状态这一概念之后&#xff0c;我们明白了什么叫做僵尸进程&#xff1a;子进程退出&#xff0c;父进程“不管不顾”。而一旦存在僵尸进程&#xff0c;势必也会存在内存泄…

介绍部署esxi8.0产品的方式

什么是esxi esxi的中文叫裸机虚拟机管理器 ESXi是由VMware公司开发的一种裸机虚拟机管理器&#xff0c;全称为VMware ESXi。 ESXi是一种虚拟化技术&#xff0c;专门设计用于在物理服务器上运行虚拟机&#xff0c;它的主要特点是能够最大限度地降低硬件配置要求并简化部署过程…

【深度学习】深度学习md笔记总结第2篇:TensorFlow介绍,学习目标【附代码文档】

深度学习笔记完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;深度学习课程&#xff0c;深度学习介绍要求,目标,学习目标,1.1.1 区别,学习目标,学习目标。TensorFlow介绍&#xff0c;2.4 张量学习目标,2.4.1 张量(Tensor),2.4.2 创建张量的指令,2.4.3 张量…

基于 Linux 的更新版 MaxPatrol VM 可扫描 Windows

&#x1f47e; MaxPatrol VM 2.1 是俄罗斯唯一一款可以安装在 Linux 上并以审计和五重测试模式扫描 Windows 主机&#xff08;甚至是旧版本&#xff09;的漏洞管理产品。 让我们告诉你更新后的 MaxPatrol VM 还有哪些有用的功能&#xff1a; 1. 由于采用了新的数据存储模式&a…

全国植被类型分布数据

引言 全国植被类型分布数据利用 Landsat 卫星数据&#xff08;Landsat TM&#xff0c;ETM和 OLI&#xff09;完成了长时序的地表覆盖变化检测&#xff0c;并结合变化 检测结果实现了逐区域和逐期的地表覆盖动态更新&#xff0c;30米精细植被类型分布数据&#xff0c;共包含 2…