这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇是《quarkus依赖注入》系列的第十一篇,之前的[《拦截器》]学习了拦截器的基础知识,现在咱们要更加深入的了解拦截器,掌握两种高级用法:拦截器属性和重复使用拦截器
先来回顾拦截器的基本知识,定义一个拦截器并用来拦截bean中的方法,总共需要完成以下三步
接口SayHello.java
public interface SayHello {
String hello();
}
实现类SayHelloA.java
@ApplicationScoped
@Named("A")
public class SayHelloA implements SayHello {
@Override
public void hello() {
Log.info("hello from A");
}
}
实现类SayHelloB.java
@ApplicationScoped
@Named("B")
public class SayHelloB implements SayHello {
@Override
public void hello() {
Log.info("hello from B");
}
}
实现类SayHelloC.java
@ApplicationScoped
@Named("C")
public class SayHelloC implements SayHello {
@Override
public void hello() {
Log.info("hello from C");
}
}
上述业务需求第二项和第三项,很显然拦截器的实现要同时支持短信通知和邮件通知两种功能,而问题的关键是:拦截器在工作的时候,如何知道当前应该发送短信还是邮件,或者说如何将通知类型准确的告诉拦截器?
这就牵扯到一个知识点:拦截器属性,拦截器自己是个注解,而注解是有属性的,咱们新增一个通知类型的属性(名为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();
}
}
上述代码有以下几处需要注意
接下来是实现具体拦截功能的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");
}
}
上述代码,有以下几处需要注意
首先是SayHelloA,拦截它的时候,业务需求是发送短信,修改后的完整源码如下,用SendMessage注解修饰hello方法,这里的SendMessage没有指定其sendType的值,因此会使用默认值sms
@ApplicationScoped
@Named("A")
public class SayHelloA implements SayHello {
@SendMessage
@Override
public void hello() {
Log.info("hello from A");
}
}
然后是SayHelloB,拦截它的时候,业务需求是发送邮件,注意sendType值等于email
@ApplicationScoped
@Named("B")
public class SayHelloB implements SayHello {
@SendMessage(sendType = "email")
@Override
public void hello() {
Log.info("hello from B");
}
}
最后是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();
}
}
编码完成,可以运行起来验证结果了
名称
链接
备注
项目主页
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-tutorials是个父工程,里面有多个module,本篇实战的module是basic-di,如下图红框
手机扫一扫
移动阅读更方便
你可能感兴趣的文章