quarkus依赖注入之十一:拦截器高级特性上篇(属性设置和重复使用)

这篇具有很好参考价值的文章主要介绍了quarkus依赖注入之十一:拦截器高级特性上篇(属性设置和重复使用)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 本篇是《quarkus依赖注入》系列的第十一篇,之前的[《拦截器》]学习了拦截器的基础知识,现在咱们要更加深入的了解拦截器,掌握两种高级用法:拦截器属性和重复使用拦截器
  • 先来回顾拦截器的基本知识,定义一个拦截器并用来拦截bean中的方法,总共需要完成以下三步
    quarkus依赖注入之十一:拦截器高级特性上篇(属性设置和重复使用)

业务需求设定

  • 为了让本篇所学知识点显得有实用型,这里假定一个业务需求,然后咱们用拦截器来满足这个需求
  • 假设有个名为SayHello的普通接口,此接口有三个实现类:SayHelloA、SayHelloB、SayHelloC,这些实现类都是bean,它们的源码如下
  1. 接口SayHello.java
public interface SayHello {
    String hello();
}
  1. 实现类SayHelloA.java
@ApplicationScoped
@Named("A")
public class SayHelloA implements SayHello {
    @Override
    public void hello() {
        Log.info("hello from A");
    }
}
  1. 实现类SayHelloB.java
@ApplicationScoped
@Named("B")
public class SayHelloB implements SayHello {
    @Override
    public void hello() {
        Log.info("hello from B");
    }
}
  1. 实现类SayHelloC.java
@ApplicationScoped
@Named("C")
public class SayHelloC implements SayHello {
    @Override
    public void hello() {
        Log.info("hello from C");
    }
}
  • 以上是已知条件,现在来看业务需求
  1. 要求设计一个拦截器,名为SendMessage,功能是对外发送通知,通知的方式有短信和邮件两种,具体用哪种是可以设置的
  2. SendMessage拦截器拦截SayHelloA,通知类型是短信
  3. SendMessage拦截器拦截SayHelloB,通知类型是邮件
  4. SendMessage拦截器拦截SayHelloC,通知类型是短信和邮件都发送

功能实现分析

  • 上述业务需求第二项和第三项,很显然拦截器的实现要同时支持短信通知和邮件通知两种功能,而问题的关键是:拦截器在工作的时候,如何知道当前应该发送短信还是邮件,或者说如何将通知类型准确的告诉拦截器?
  • 这就牵扯到一个知识点:拦截器属性,拦截器自己是个注解,而注解是有属性的,咱们新增一个通知类型的属性(名为sendType),只要在使用注解的地方配置sendType,然后在拦截器实现中获取到sendType的值,就解决了通知类型的设置和获取的问题,业务需求2和3也就迎刃而解了,拦截器配置的效果大致如下
@ApplicationScoped
@SendMessage(sendType="sms")
public class SayHelloA implements SayHello {
  • 再来看需求4,这又设计到拦截器的另一个知识点:同一个拦截器重复使用,只要连续两次用SendMessage注解修饰SayHelloC,而每个注解的sendType分别是短信和邮件,这样就能达到目的了,拦截器配置的效果大致如下
@ApplicationScoped
@SendMessage(sendType="sms")
@SendMessage(sendType="email")
public class SayHelloC implements SayHello {
  • 以上就是解决问题的大致思路,接下来编码实现,将涉及的知识点在代码中体现出来

编码:定义拦截器

  • 首先是拦截器定义SendMessage.java,有几处要注意的地方稍后会提到
package com.bolingcavalry.interceptor.define;

import javax.enterprise.util.Nonbinding;
import javax.interceptor.InterceptorBinding;
import java.lang.annotation.*;

@InterceptorBinding
@Repeatable(SendMessage.SendMessageList.class)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SendMessage {

    /**
     * 消息类型 : "sms"表示短信,"email"表示邮件
     * @return
     */
    @Nonbinding
    String sendType() default "sms";

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface SendMessageList {
        SendMessage[] value();
    }
}
  • 上述代码有以下几处需要注意
  1. 允许在同一位置重复使用同一个注解,这是java注解的通用功能,并非quarkus独有
  2. 重复使用注解时,必须定义注解容器,用来放置重复的注解,这里的容器是SendMessageList
  3. 使用Repeatable修饰SendMessage,这样就能在同一位置重复使用SendMessage注解了,注意Repeatable的属性值是容器SendMessageList
  4. sendType是注解属性,用来保存通知类型,任何使用SendMessage注解的地方都能通过设置sendType来指定通知类型,如果不指定则使用默认值sms
  5. 要注意sendType的注解Nonbinding,此注解非常重要,如果不添加此注解,在使用SendMessage的时候,设置sendType为email时拦截器不会生效

quarkus对重复使用同一拦截器注解的限制

  • 虽然可以在同一位置重复使用SendMessage拦截器,但是要注意quarkus的限制
  1. 可以作用在方法上
  2. 不能作用在类上
  3. 不能作用在stereotypes上
  • 关于2和3,官方的说法是将来会解决(This might be added in the future)

编码:实现拦截器

  • 接下来是实现具体拦截功能的SendMessageInterceptor.java,代码如下,有几处要注意的地方稍后会提到
package com.bolingcavalry.interceptor.impl;

import com.bolingcavalry.interceptor.define.SendMessage;
import com.bolingcavalry.interceptor.define.TrackParams;
import io.quarkus.arc.Priority;
import io.quarkus.arc.runtime.InterceptorBindings;
import io.quarkus.logging.Log;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import java.lang.annotation.Annotation;
import java.util.*;
import static io.quarkus.arc.ArcInvocationContext.KEY_INTERCEPTOR_BINDINGS;

@SendMessage
@Interceptor
public class SendMessageInterceptor {

    @AroundInvoke
    Object execute(InvocationContext context) throws Exception {
        // 先执行被拦截的方法
        Object rlt = context.proceed();

        // 获取被拦截方法的类名
        String interceptedClass = context.getTarget().getClass().getSimpleName();

        // 代码能走到这里,表示被拦截的方法已执行成功,未出现异常
        // 从context中获取通知类型,由于允许重复注解,因此通知类型可能有多个
        List<String> allTypes = getAllTypes(context);

        // 将所有消息类型打印出来
        Log.infov("{0} messageTypes : {1}", interceptedClass, allTypes);

        // 遍历所有消息类型,调用对应的方法处理
        for (String type : allTypes) {
            switch (type) {
                // 短信
                case "sms":
                    sendSms();
                    break;
                // 邮件
                case "email":
                    sendEmail();
                    break;
            }
        }

        // 最后再返回方法执行结果
        return rlt;
    }

    /**
     * 从InvocationContext中取出所有注解,过滤出SendMessage类型的,将它们的type属性放入List中返回
     * @param invocationContext
     * @return
     */
    private List<String> getAllTypes(InvocationContext invocationContext) {
        // 取出所有注解
        Set<Annotation> bindings = InterceptorBindings.getInterceptorBindings(invocationContext);

        List<String> allTypes = new ArrayList<>();

        // 遍历所有注解,过滤出SendMessage类型的
        for (Annotation binding : bindings) {
            if (binding instanceof SendMessage) {
               allTypes.add(((SendMessage) binding).sendType());
            }
        }

        return allTypes;
    }

    /**
     * 模拟发送短信
     */
    private void sendSms() {
        Log.info("operating success, from sms");
    }

    /**
     * 模拟发送邮件
     */
    private void sendEmail() {
        Log.info("operating success, from email");
    }
}
  • 上述代码,有以下几处需要注意
  1. 发送短信和邮件不是本篇的重点,因此,对应的sendSms和sendEmail方法中只是日志打印,表示代码已经走到了此处
  2. getAllTypes方法是重点,演示了如何从拦截器上下文对象invocationContext中获取所有注解,并过滤出所有SendMessage类型,再取其type属性
  3. 对取出的sendType属性逐一处理,这样就做到了每个设置的类型都会被处理
  4. 在某个方法上多次用SendMessage注解修饰,最终只会执行一次SendMessageInterceptor#execute方法,这是关键!试想,如果SendMessageInterceptor#execute方法执行了多次,而每次都会取出所有SendMessage类型去处理,那么每种SendMessage类型都会重复处理

编码:使用拦截器

  • 拦截器的定义和实现都已经完成,接下来就是使用拦截器了,注意前面提到的限制,这里要用SendMessage去修饰方法,而不能修饰类
  1. 首先是SayHelloA,拦截它的时候,业务需求是发送短信,修改后的完整源码如下,用SendMessage注解修饰hello方法,这里的SendMessage没有指定其sendType的值,因此会使用默认值sms
@ApplicationScoped
@Named("A")
public class SayHelloA implements SayHello {

    @SendMessage
    @Override
    public void hello() {
        Log.info("hello from A");
    }
}
  1. 然后是SayHelloB,拦截它的时候,业务需求是发送邮件,注意sendType值等于email
@ApplicationScoped
@Named("B")
public class SayHelloB implements SayHello {

    @SendMessage(sendType = "email")
    @Override
    public void hello() {
        Log.info("hello from B");
    }
}
  1. 最后是SayHelloC,拦截它的时候,也无需求是短信和邮件都要发送,注意这里使用了两次SendMessage
@ApplicationScoped
@Named("C")
public class SayHelloC implements SayHello {

    @SendMessage
    @SendMessage(sendType = "email")
    @Override
    public void hello() {
        Log.info("hello from C");
    }
}
  • 拦截器的定义、实现、使用都已经完成,接下来考虑如何验证,还是用单元测试吧,简单方便

编码:单元测试

  • 单元测试类的逻辑很简单,运行几个bean的hello方法即可
@QuarkusTest
public class SendMessageTest {

    @Named("A")
    SayHello sayHelloA;

    @Named("B")
    SayHello sayHelloB;

    @Named("C")
    SayHello sayHelloC;

    @Test
    public void testSendMessage() {
        sayHelloA.hello();
        sayHelloB.hello();
        sayHelloC.hello();
    }
}
  • 编码完成,可以运行起来验证结果了

运行单元测试

  • 单元测试类SendMessageTestd的执行结果如下图,红黄蓝三个框中,分别是SayHelloA、SayHelloB、SayHelloC的拦截结果,可见全部符合预期

quarkus依赖注入之十一:拦截器高级特性上篇(属性设置和重复使用)

  • 至此,拦截器的两个高级特性已经实战完成,希望这些知识点能够帮助您写出更强大和精准的拦截器,实现复杂的业务需求

源码下载

  • 本篇实战的完整源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos)
名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本次实战的源码在quarkus-tutorials文件夹下,如下图红框
    quarkus依赖注入之十一:拦截器高级特性上篇(属性设置和重复使用)
  • quarkus-tutorials是个父工程,里面有多个module,本篇实战的module是basic-di,如下图红框
    quarkus依赖注入之十一:拦截器高级特性上篇(属性设置和重复使用)

欢迎关注博客园:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...文章来源地址https://www.toymoban.com/news/detail-636879.html

到了这里,关于quarkus依赖注入之十一:拦截器高级特性上篇(属性设置和重复使用)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringCloud微服务实战——搭建企业级开发框架:微服务安全加固—自定义Gateway拦截器实现防止SQL注入/XSS攻击

     SQL注入是常见的系统安全问题之一,用户通过特定方式向系统发送SQL脚本,可直接自定义操作系统数据库,如果系统没有对SQL注入进行拦截,那么用户甚至可以直接对数据库进行增删改查等操作。   XSS全称为Cross Site Script跨站点脚本攻击,和SQL注入类似,都是通过特定方

    2024年02月03日
    浏览(65)
  • 数据权限拦截器,多租户拦截器

    WEB类型软件产品,在Java(SpringBoot)+MybatisPlus架构场景下,本文针对下面两个问题,提供解决方案: 多租户的产品,想在表内级别上,实现租户数据隔离(分表、分库方案不在本文讨论范围内)。 ToB、ToG类型的软件产品,需要实现数据权限鉴权。例如用户数据、部门数据、租户

    2024年02月02日
    浏览(45)
  • VUE3 请求拦截器 响应拦截器

    1,导入axios  (使用axios进行接口的请求,页面发送http请求,很多情况我们要对请求和其响应进行特定的处理,如:判断token,设置请求头。如果请求数非常多,单独对每一个请求进行处理会变得非常麻烦,程序的优雅性也会大打折扣。所以axios为开发者提供了这样一个API:拦

    2024年02月16日
    浏览(49)
  • SpringBoot加入拦截器——登录拦截器的实现

            拦截器 Interceptor 在 Spring MVC 中的地位等同于 Servlet 规范中的过滤器 Filter,拦截的是处理器的执行,由于是全局行为,因此常用于做一些通用的功能,如请求日志打印、权限控制等。         核心原理:AOP思想 preHandle:  预先处理,在目标的controller方法执行之前,进行

    2024年02月15日
    浏览(43)
  • 自定义注解与拦截器实现不规范sql拦截(拦截器实现篇)

    最近考虑myBatis中sql语句使用规范的问题,如果漏下条件或者写一些不规范语句会对程序性能造成很大影响。最好的方法就是利用代码进行限制,通过拦截器进行sql格式的判断在自测环节就能找到问题。写了个简单情景下的demo,并通过idea插件来将myBatis的mapper方法都打上拦截器

    2024年01月22日
    浏览(45)
  • 【SpringBoot篇】Interceptor拦截器 | 拦截器和过滤器的区别

    拦截器(Interceptor)是一种软件设计模式,用于在应用程序处理请求或响应时对其进行拦截和修改。拦截器可以在整个应用程序中使用,用于执行跨越多个层的通用任务,如身份验证、授权、缓存、日志记录、性能计量等。 在Web开发中,拦截器通常用于在请求到达控制器之前

    2024年02月04日
    浏览(63)
  • WebService 客户端增加Header头、并且指定命名空间、添加拦截器(日志拦截器,自定义拦截器)、soap:Envelope 添加命名空间

    1.增加Header头 生成XML结果如下 2.添加拦截器 3.soap:Envelope 添加命名空间 生成XML结果如下

    2024年02月10日
    浏览(50)
  • 微信小程序封装request请求,包含请求拦截器,响应拦截器和请求重试功能

    在发送请求之前,先判断用户是否有token,没有就执行登陆请求,将token保存,然后再执行原来请求; 拥有token,就直接执行请求;但是用户的这个token可能是过期的,如果执行请求发现用户登陆过期,就统一返回40001,然后对40001的响应统一处理,执行登陆请求,再执行原来请

    2024年02月13日
    浏览(50)
  • 分布式项目 16 购物车系统,dubbo框架(重点是拦截器),优化userId,配合拦截器

    01.创建jt-cart项目 第一步: 第二步: 第三步: 第四步: 在pom.xml文件中添加jt-common的依赖,如图所示: 第五步: 添加插件 第六步:创建pojo实体类对象 说明:在jt-common项目下的com.jt.pojo创建Cart实体类 第七步:创建Dubbo接口 说明:在jt-common项目com.jt.service包下创建DubboCartSer

    2024年02月09日
    浏览(49)
  • SpringMVC 拦截器

    Spring MVC 拦截器是Spring框架中的一种机制,用于在请求到达处理器之前和渲染视图之前拦截请求,并允许开发者在这两个时间点进行自定义的处理逻辑。拦截器与过滤器(Filter)类似,但更加专注于对请求的处理器的拦截,比如对访问权限进行控制、日志记录、性能监控等。

    2024年01月19日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包