Java动态获取实现类的方式详解

 更新时间:2024年01月04日 11:08:47   作者:Java个体户  
这篇文章主要介绍了Java动态获取实现类的方式详解,文中通过示例代码介绍的非常详细,对大家的学习或工作有一定的参考价值,需要的朋友们下面随着小编来一起学习吧

应用场景

支付的时候,有不同的渠道,比如微信还是支付宝?

这个时候,就需要根据渠道类型,选择走哪个渠道。

先来看下读

/**
 * 根据接口和注解@SupportCodes的值,获取接口实现类
 *
 * @param interfaceClass 接口
 * @param sopportCode 接口实现类的注解@SupportCodes的值
 * @return 接口实现类
 */
public static <T> T getServiceImpl(Class<T> interfaceClass, String sopportCode)
{
  // 如果没有初始化,则初始化
  if (null == serviceImplFactoryMap.get(interfaceClass)) {
    // 初始化
    init(interfaceClass);
    if (null == serviceImplFactoryMap) {
      return null;
    }
  }
  // 如果初始化后,还是没有,则返回null
  if (null == serviceImplFactoryMap.get(interfaceClass)) {
    return null;
  }
  // 返回接口实现类
  return (T)((Map)serviceImplFactoryMap.get(interfaceClass)).get(sopportCode);
}

为什么要先看读?读是需求,因为有读的需求,才需要实现写的功能。

重点来看入参,任何功能,无非是入参和出参。

入参:接口和渠道代码。
出参:渠道实现类。

那具体怎么实现呢?

类似这种需求,基本上都是map。key是渠道代码,value实现类。

大概的实现思路,基本上就是这样。存储用到的数据结构是map。

刚才看了读方法,再来看下写。

读,无非是从map读。写,也一样,无非是把数据写到map。

那具体怎么写呢?直接看代码

/**
 * 初始化
 *
 * @param interfaceClass 接口
 * @author javaself
 */
protected static <T> void init(Class<T> interfaceClass) {
  log.info("begin to init " + interfaceClass);
  synchronized (FactoryUtil.class) {
    try {
      // 如果已经初始化,则返回
      if (null != serviceImplFactoryMap.get(interfaceClass)) {
        return;
      }
      
      Map<String, Object> serviceMap = new HashMap();
      // 获取所有实现类
      Map<String, T> beans = appContext.getBeansOfType(interfaceClass); //实现类名字作为key,实现类实例作为value
      Set<Entry<String, T>> entrySet = beans.entrySet();
      Iterator<Entry<String, T>> iterator = entrySet.iterator();
      // 遍历所有实现类,获取注解@SupportCodes的值,作为key,实现类实例作为value
      while (iterator.hasNext()) {
        Object interfaceServiceImpl = ((Entry)iterator.next()).getValue();
        SupportCodes annotationValue = (SupportCodes)interfaceServiceImpl.getClass().getAnnotation(SupportCodes.class);
        if (null != annotationValue) {
          for (String flag : annotationValue.value()) { //可能有多个注解值,所以遍历
            //写到map: key=注解值,value=实现类实例
            serviceMap.put(flag, interfaceServiceImpl); 
            log.info(flag + "=>" + interfaceServiceImpl.getClass());
          }
        }
      }
      
      //写到map: key=接口,value=上面的map
      serviceImplFactoryMap.put(interfaceClass, serviceMap);
    } catch (Exception e) {
      log.error("init failed", e);
      throw new RuntimeException(e);
    }
  }
  log.info(interfaceClass + "inited");
}

写方法的入参是:接口。

也就是说,根据接口,可以获取不同实现类。具体是基于spring的获取bean的功能。

拿到不同实现类之后,再根据注解代码,组装成key/value写到map。key是注解代码,value是实现类。

注解代码是干嘛用的?就是前面的渠道代码。属于业务代码。具体是基于自定义注解实现。自定义注解用的时候,有几步,首先,是自定义注解,其次,注解到类上面去,最后,读注解的值的时候直接使用spring提供的工具类即可。

到这里其实基本上已经写完了,核心思路就是怎么玩弄渠道代码和渠道实现类。

何时写?

读的时候,写。也就是说,第一次用到的时候,写。而不是启动项目的时候,写。

只需要写一次即可,也就是说,只在第一次用到的时候,才写。后面直接用即可。

写的时候,注意并发问题。类似这种只写一次,但是又需要注意并发问题的情况,一般直接使用同步关键字即可。

何时读?

支付系统里面根据接口和渠道代码,决定走哪个渠道实现类

IGetPayInstructionService payService = FactoryUtil.getServiceImpl(IGetPayInstructionService.class, channelCode);

渠道代码的值,从哪里来?网关入口会判断。具体的话,用微信还是支付宝扫码的时候,可以根据请求头判断是哪个app。

完整代码

工具类

package com.xxx.commons.factory;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.stereotype.Component;

/**
 * 工厂工具类:用于获取接口实现类
 *
 * ---
 * 应用场景:当一个接口有多个实现类时,通过该工具类获取对应的实现类。
 * 不同实现类,通过注解@SupportCodes来区分。获取的时候,也是通过@SupportCodes的值来获取。
 *
 * ---
 * 举例说明
 *
 * 不同渠道的支付接口,都实现了IPayService接口,如下:微信支付、支付宝支付
 *
 * @author javaself
 */
@Component
public class FactoryUtil
  implements ApplicationContextAware
{
  private static Log log = LogFactory.getLog(FactoryUtil.class);
  private static Map<Class<?>, Map<String, Object>> serviceImplFactoryMap = new HashMap();
  private static AbstractApplicationContext appContext;

  /**
   * 根据接口和注解@SupportCodes的值,获取接口实现类
   *
   * @param interfaceClass 接口
   * @param sopportCode 接口实现类的注解@SupportCodes的值
   * @return 接口实现类
   */
  public static <T> T getServiceImpl(Class<T> interfaceClass, String sopportCode)
  {
    // 如果没有初始化,则初始化
    if (null == serviceImplFactoryMap.get(interfaceClass)) {
      // 初始化
      init(interfaceClass);
      if (null == serviceImplFactoryMap) {
        return null;
      }
    }
    // 如果初始化后,还是没有,则返回null
    if (null == serviceImplFactoryMap.get(interfaceClass)) {
      return null;
    }
    // 返回接口实现类
    return (T)((Map)serviceImplFactoryMap.get(interfaceClass)).get(sopportCode);
  }

  /**
   * 初始化
   *
   * @param interfaceClass 接口
   * @author javaself
   */
  protected static <T> void init(Class<T> interfaceClass) {
    log.info("begin to init " + interfaceClass);
    synchronized (FactoryUtil.class) {
      try {
        // 如果已经初始化,则返回
        if (null != serviceImplFactoryMap.get(interfaceClass)) {
          return;
        }

        Map<String, Object> serviceMap = new HashMap();
        // 获取所有实现类
        Map<String, T> beans = appContext.getBeansOfType(interfaceClass); //实现类名字作为key,实现类实例作为value
        Set<Entry<String, T>> entrySet = beans.entrySet();
        Iterator<Entry<String, T>> iterator = entrySet.iterator();
        // 遍历所有实现类,获取注解@SupportCodes的值,作为key,实现类实例作为value
        while (iterator.hasNext()) {
          Object interfaceServiceImpl = ((Entry)iterator.next()).getValue();
          SupportCodes annotationValue = (SupportCodes)interfaceServiceImpl.getClass().getAnnotation(SupportCodes.class);
          if (null != annotationValue) {
            for (String flag : annotationValue.value()) { //可能有多个注解值,所以遍历
              //写到map: key=注解值,value=实现类实例
              serviceMap.put(flag, interfaceServiceImpl);
              log.info(flag + "=>" + interfaceServiceImpl.getClass());
            }
          }
        }

        //写到map: key=接口,value=上面的map
        serviceImplFactoryMap.put(interfaceClass, serviceMap);
      } catch (Exception e) {
        log.error("init failed", e);
        throw new RuntimeException(e);
      }
    }
    log.info(interfaceClass + "inited");
  }
  
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
  {
    appContext = (AbstractApplicationContext)applicationContext;
  }
}

注解类

package com.xxx.commons.factory;

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({java.lang.annotation.ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportCodes
{
  String[] value();
}

接口实现类

/**
 * 微信公众号支付
 * 
 */
@SupportCodes("WeChatPay")
@Service
public class WeChatPayService implements IGetPayInstructionService {

以上就是Java动态获取实现类的方式详解的详细内容,更多关于Java动态获取实现类的资料请关注脚本之家其它相关文章!

相关文章

  • SpringCloud 搭建企业级开发框架之实现多租户多平台短信通知服务(微服务实战)

    SpringCloud 搭建企业级开发框架之实现多租户多平台短信通知服务(微服务实战)

    这篇文章主要介绍了SpringCloud 搭建企业级开发框架之实现多租户多平台短信通知服务,系统可以支持多家云平台提供的短信服务。这里以阿里云和腾讯云为例,集成短信通知服务,需要的朋友可以参考下
    2021-11-11
  • java使用Logback配置输出日志内容到文件示例代码

    java使用Logback配置输出日志内容到文件示例代码

    这篇文章主要介绍了java Logback输出日志内容到文件,要将logger.info的信息输出到文件,您可以使用Logback配置,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-09-09
  • Java 适配器模式从入门到实战指南

    Java 适配器模式从入门到实战指南

    这篇文章给大家介绍Java适配器模式从入门到实战指南,本文从入门到实战,结合真实业务场景+可运行代码+面试高频考点,带你吃透适配器模式,看完直接能用、能说、能面试,新手也能快速上手
    2026-04-04
  • java使用回溯法求解数独示例

    java使用回溯法求解数独示例

    这篇文章主要介绍了java使用回溯法求解数独示例,大家参考使用吧
    2014-01-01
  • spring boot tomcat版本升级的实现示例

    spring boot tomcat版本升级的实现示例

    本文主要介绍了spring boot tomcat版本升级的实现示例,将tomcat升级一个小版本升级到9.0.44版本,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11
  • 一篇文章带你了解一些Java反射的学习记录

    一篇文章带你了解一些Java反射的学习记录

    java反射机制是一个很好用的东西,用它可以解决很多死的东西,因为反射机制的灵活行很大,有了他,我们就不要花太多的时间来写操做数据库的代码了,这个可以很大的减少开发时间,而且代码的可读性好
    2021-09-09
  • Java开源诊断工具Arthas使用方法详解

    Java开源诊断工具Arthas使用方法详解

    这篇文章主要介绍了Java开源诊断工具Arthas使用方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • spring kafka框架中@KafkaListener 注解解读和使用案例

    spring kafka框架中@KafkaListener 注解解读和使用案例

    Kafka 目前主要作为一个分布式的发布订阅式的消息系统使用,也是目前最流行的消息队列系统之一,这篇文章主要介绍了kafka @KafkaListener 注解解读,需要的朋友可以参考下
    2023-02-02
  • Spring Security实现两周内自动登录

    Spring Security实现两周内自动登录"记住我"功能

    登录过程中经常使用的“记住我”功能,也就是我们经常会在各种网站登陆时见到的"两周内免登录",“三天内免登录”的功能。今天小编给大家分享基于Spring Security实现两周内自动登录"记住我"功能,感兴趣的朋友一起看看吧
    2019-11-11
  • ElasticSearch如何设置某个字段不分词浅析

    ElasticSearch如何设置某个字段不分词浅析

    最近在学习ElasticSearch官方文档过程中发现的某个问题,记录一下 希望能帮助到后面的朋友,下面这篇文章主要给大家介绍了关于ElasticSearch如何设置某个字段不分词的相关资料,需要的朋友可以参考下
    2022-04-04

最新评论