使用ApplicationContext的getBeansOfType实现接口实现类的动态调用

需求

设计一个分发系统,对视频或图文进行多种渠道的分发,发布渠道类型被设计成视频、图文、音频、短文本等,针对不同的发布内容,需要分发至不同的发布渠道,我们需要一个发布接口,并且这些不同的发布渠道需要实现这个发布接口,这就涉及到了接口实现类的动态调用。

流程示意图:
在这里插入图片描述
getBeansOfType方法可以根据接口类型返回相应的所有bean。

<T> Map<String, T> getBeansOfType(@Nullable Class<T> var1) throws BeansException;
1、 定义视频发布接口
public interface VideoPublishService<T extends BasePayload> {

    /**
     * 上传信息
     *
     * @param payload  上传信息荷载
     * @return 返回值外部作品id
     */
    Result<String> publish(T payload);
}
2、渠道类型枚举定义
public enum ChannelEnum {
    KUAISHOU("kuaishou", "快手"),
    DOUYIN("douyin", "抖音"),
    BILIBILI("bilibili", "哔哩哔哩");
}
3、定义视频发布接口实现类
@ChannelService(value = "bilibiliVideoPublishService", channel = ChannelEnum.BILIBILI)
public class BilibiliVideoPublishServiceImpl implements VideoPublishService<BilibiliPayload> {
    @Override
    public Result<String> publish(BilibiliPayload payload) {
        return "哔哩哔哩发布成功!";
    }
}

@ChannelService(value = "kuaishouVideoPublishService", channel = ChannelEnum.KUAISHOU)
public class KuaishouVideoPublishServiceImpl implements VideoPublishService<KuaishouPayload> {
    @Override
    public Result<String> publish(KuaishouPayload payload) {
        return "快手发布成功!";
    }
}
4、自定义Service注解@ChannelService
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ChannelService {

    @AliasFor(annotation = Component.class)
    String value() default "";

    /**
     * 渠道名称
     * @return
     */
    ChannelEnum channel();
}

@AliasFor 表示别名,它可以注解到自定义注解的两个属性上,表示这两个互为别名,也就是说这两个属性其实同一个含义。
此处的别名是使用Component而不是Service,看一下Service注解,它的别名已经是Component了在这里插入图片描述

5、工厂类
@Slf4j
@Component
public class VideoPublishDispatchService {

    private HashMap<String, VideoPublishService> mediaMap = Maps.newHashMap();

    @Autowired
    private ApplicationContextUtils applicationContextUtils;

    /**
     * 初始化授权服务
     */
    @PostConstruct
    public void init() throws Exception {
        ApplicationContext context = applicationContextUtils.getApplicationContext();
        // 得到所有的实现类
        Map<String, VideoPublishService> beanMap = context.getBeansOfType(VideoPublishService.class);
        for (Map.Entry<String, VideoPublishService> mediaPublishServiceEntry : beanMap.entrySet()) {
            Class<?> targetClass = AopUtils.getTargetClass(mediaPublishServiceEntry.getValue());
            ChannelService channelServiceInfo = targetClass.getAnnotation(ChannelService.class);
            if(null == channelServiceInfo){
                log.error("渠道名称未设置,class:{},channel:{}",targetClass.getName(), ChannelService.class.getName());
                throw new Exception("服务:"+targetClass.getName()+" 需要配置对应的渠道名称");
            }
            mediaMap.put(channelServiceInfo.channel().getChannelId(),mediaPublishServiceEntry.getValue());
        }
    }

    /**
     * 获取服务
     *
     * @param channelId 服务类型
     * @return
     */
    public MediaPublishService getService(String channelId) {
        return mediaMap.get(channelId);
    }
}
/**
 * 上下文获取工具类
 */
@Slf4j
@Component
public class ApplicationContextUtils implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public ApplicationContext getApplicationContext() {
        return this.applicationContext;
    }
}
@PostConstruct说明

servlet的生命周期:服务器加载servlet -> PostConstruct -> init -> doGet/doPost -> destroy -> PreDestroy -> 完毕
当使用依赖注入时,使用@Autowired将A注入到B中,首先需要生成这两个对象,那么Autowired是在构造方法Constructor后执行的。如果想在生成对象时完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。
执行顺序是:Constructor -> Autowired -> PostConstruct

6、调用
VideoPublishService videoWorker = VideoPublishDispatchService.getService(channelId);
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值