spring中监听事件中使用泛型不生效的问题

674 阅读2分钟

使用spring的事件机制做回调

原代码

事件类

package com.cvte.maxhub.mhqa.event;

import lombok.Data;

@Data
public class QueueBuildCompletedEvent<T> {
    private T data;
}

监听器类

package com.cvte.maxhub.mhqa.listener;

import com.cvte.maxhub.mhqa.event.QueueBuildCompletedEvent;

public interface QueueBuildCompletedEventListener<T> {

    void onQueueBuildCompletedEvent(QueueBuildCompletedEvent<T> event);
}

发送事件端

package com.cvte.maxhub.mhqa.link.controller;

import com.cvte.maxhub.mhqa.event.QueueBuildCompletedEvent;
import com.cvte.maxhub.mhqa.link.domain.QueueBuildDO;
import com.cvte.maxhub.mhqa.link.domain.request.VesselQueueBuildCreatingRequest;
import com.cvte.maxhub.mhqa.link.service.QueueBuildService;
import com.cvte.maxhub.mhqa.support.Result;
import com.cvte.maxhub.mhqa.support.ResultBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/link/jobs/queue")
@RequiredArgsConstructor
@Slf4j
public class QueueBuildController {

    private final QueueBuildService<QueueBuildDO> queueBuildService;
    private final ApplicationEventPublisher publisher;

    @PostMapping
    public Result addForVessel(@RequestBody @Validated VesselQueueBuildCreatingRequest request) {
        queueBuildService.addForVessel(request);
        return ResultBuilder.success(null);
    }

    @GetMapping("test")
    public Result test() {
        QueueBuildCompletedEvent<String> testEvent = new QueueBuildCompletedEvent<>();
        testEvent.setData("");
        publisher.publishEvent(testEvent);
        log.error("请求发出去了");
        return ResultBuilder.success();
    }
}

处理事件端

package com.cvte.maxhub.mhqa.link.service.impl;

/**
 * @author zhongxie
 */
@Service
@RequiredArgsConstructor
@Slf4j
@EnableScheduling
public class QueueBuildServiceImpl implements QueueBuildCompletedEventListener<String> {

    private static final String COLUMN_NAME_STATUS = "c_status";
    private static final String COLUMN_NAME_UUID = "c_uuid";
    private static final String COLUMN_NAME_CREATE_TIME = "c_create_time";
    private static final String COLUMN_NAME_DEVICE_CHANNEL = "c_device_channel";
    private static final String COLUMN_NAME_BUILD_UUID = "c_build_uuid";


    @Async
    @EventListener()
    @Override
    public void onQueueBuildCompletedEvent(QueueBuildCompletedEvent<String> event) {
        log.error("我接收到事件了:{}", event);
        String buildUuid = event.getData();
        if (!StringUtils.isEmpty(buildUuid)) {
            UpdateWrapper<QueueBuildDO> wrapper = new UpdateWrapper<QueueBuildDO>()
                    .eq(COLUMN_NAME_BUILD_UUID, buildUuid)
                    .eq(COLUMN_NAME_STATUS, QueueBuildStatusEnum.RUNNING)
                    .set(COLUMN_NAME_STATUS, QueueBuildStatusEnum.FINISH);
            update(wrapper);
        }
    }
}

异常现象

发送事件后,事件接收端无法正常接收到事件并处理。
觉得很神奇,猜测事件机制不支持泛型导致? --> 不太可能,作为spring的一个机制,不应该这么不成熟
应该是没有开启支持泛型消息导致的

查野生文档

说了很多消息机制的实现原理,但是没解决我的问题,隐隐感觉与类型转换又关系 blog.csdn.net/ITlikeyou/a…
blog.csdn.net/yyb_gz/arti…

查找官方文档@EventListner的说明

docs.spring.io/spring-fram…
寻找关键词@EventListener

有以下两段描述:

第一个方案描述

img_9.png 大概意思是@EventListener中指定需要监听的event类型,并在形参中明确的泛型的实际类型可以进一步缩小事件的范围
可以在@EventListener()的value中签名需要监听的事件类型,形参中便可使用泛型了

第二个方案描述

img_10.png 大概意思是由于类型擦出的存在,泛型事件类型可以通过继承ApplicationEvent并实现ResolvableTypeProvider来获得更好的特性支持

以泛型的实际类型为String类型为例,试验以上两种方法

根据方案一修改后

能正常获取事件。

@Async
@EventListener({QueueBuildCompletedEvent.class})
@Override
public void onQueueBuildCompletedEvent(QueueBuildCompletedEvent<String> event) {
    log.error("我接收到事件了:{}", event);
    String buildUuid = event.getData();
    if (!StringUtils.isEmpty(buildUuid)) {
        UpdateWrapper<QueueBuildDO> wrapper = new UpdateWrapper<QueueBuildDO>()
                .eq(COLUMN_NAME_BUILD_UUID, buildUuid)
                .eq(COLUMN_NAME_STATUS, QueueBuildStatusEnum.RUNNING)
                .set(COLUMN_NAME_STATUS, QueueBuildStatusEnum.FINISH);
        update(wrapper);
    }
}

根据方案二修改后

能正常获取事件。

@Getter
public class QueueBuildCompletedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
    private T data;

    public QueueBuildCompletedEvent(T data) {
        super(data);
        this.data = data;
    }

    @Override
    public ResolvableType getResolvableType() {
        return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
    }
}

@Async
@EventListener
@Override
public void onQueueBuildCompletedEvent(QueueBuildCompletedEvent<String> event) {
    log.error("我接收到事件了:{}", event);
    String buildUuid = event.getData();
    if (!StringUtils.isEmpty(buildUuid)) {
        UpdateWrapper<QueueBuildDO> wrapper = new UpdateWrapper<QueueBuildDO>()
                .eq(COLUMN_NAME_BUILD_UUID, buildUuid)
                .eq(COLUMN_NAME_STATUS, QueueBuildStatusEnum.RUNNING)
                .set(COLUMN_NAME_STATUS, QueueBuildStatusEnum.FINISH);
        update(wrapper);
    }
}

原理

待续吧,暂时么得空了