自定义注解+反射示例

自定义注解+反射 案例

1.案例背景

在实际项目开发中,在系统与系统对接,或者微服务与微服务对接中,往往采用暴露接口的方式,接口即对应一个服务,有时我们为了减少服务的注册量,实现服务的复用,经常在一个服务中接收多种数据。下面做一个简单的演示,通过自定义注解+反射实现一个接口接收多种数据。
案例介绍: 水果商城系统中,水果有很多种,水果的一些属性信息需要由其它微服务来推送,现在假设有苹果(Apple)、香蕉(Banana)、橙子(Orange)三种水果类型,水果商城系统需要接收每种水果的详细介绍信息,通常的逻辑是为每种水果构建一个Controller接口来接收,编码逻辑如下:

/**
 * 处理苹果数据接口
 */
@RestController
public class AppleController {
    @PostMapping("/fruit/apple")
    public Object getAppleDetail(@RequestBody JSONObject requestData) {
        // TODO 数据处理
        return "苹果数据接收成功";
    }
}
/**
 * 处理香蕉数据接口
 */
@RestController
public class BananaController {
    @PostMapping("/fruit/banana")
    public Object getAppleDetail(@RequestBody JSONObject requestData) {
        // TODO 数据处理
        return "香蕉数据接收成功";
    }
}
/**
 * 处理橙子数据接口
 */
@RestController
public class OrangeController {
    @PostMapping("/fruit/orange")
    public Object getAppleDetail(@RequestBody JSONObject requestData) {
        // TODO 数据处理
        return "橙子数据接收成功";
    }
}

以上代码可以实现我们的要求,但如果做服务注册的话,几十上百种水果类型,可能要注册很多服务,这显然是不合理的,所以我们要在编码时实现服务的复用,通过一个服务接口做到接收所有类型的水果数据。下面进行编码实现。

2.编码实现

一些注释都在代码中标明。

2.1 创建水果的基础控制器

基础控制器主要用来写一些通用的方法,方便代码的复用,这里没有过多的业务处理,就不着重去写。

/**
 * 水果类查询基础父类
 */
@RequestMapping("/fruit")
@RestController
public class FruitBaseController {

    public Object getFruitDetail(String fruitName,  Map<String , Object> dataMap) {
        return "水果:" + fruitName + "介绍信息为空";
    }

    // TODO 其它的基础方法
}
2.2 创建自定义注解
/**
 * 自定义注解:水果详情注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitDetail {
    String value() default "";
}
2.3 创建水果详细数据业务接口
/**
 * 水果详情service接口
 */
public interface IFruitDetailService {

    public String getFruitDetail(String fruitName, Map<String , Object> requestData);
}
2.4 service接口进行业务实现

这里不讨论业务数据处理的具体逻辑,仅仅做一个实现。

/**
 * 苹果 详情数据处理服务
 */
@Service
@FruitDetail("Apple")
public class AppleFruitDetailService implements IFruitDetailService {

    @Override
    public String getFruitDetail(String fruitName, Map<String, Object> requestData) {
        System.out.println("接收苹果详情数据");
        // TODO 一些具体业务操作
        return "苹果详情数据处理完毕";
    }
}
/**
 * 香蕉 详情数据处理服务
 */
@Service
@FruitDetail("Banana")
public class BananaFruitDetailService implements IFruitDetailService {

    @Override
    public String getFruitDetail(String fruitName, Map<String, Object> requestData) {
        System.out.println("接收香蕉详情数据");
        // TODO 一些具体业务操作
        return "香蕉详情数据处理完毕";
    }
}
/**
 * 橙子 详情数据处理服务
 */
@Service
@FruitDetail("Orange")
public class OrangeFruitDetailService implements IFruitDetailService {
    @Override
    public String getFruitDetail(String fruitName, Map<String, Object> requestData) {
        System.out.println("接收橙子详情数据");
        // TODO 一些具体业务操作
        return "橙子详情数据处理完毕";
    }
}
2.5 创建水果详情控制器
@RestController
public class FruitDetailController extends FruitBaseController{

    @Autowired
    private AnnotationUtil annotationUtil;

 /**
     * 接收数据接口
     * @param requestData 数据
     * @return
     */
    @PostMapping("/fruitDetail")
    public Object fruitDetail(@RequestBody JSONObject requestData) {
        // 接口数据
        List<Map> requestDataList = getRequestDataList(requestData);
        if (CollectionUtils.isEmpty(requestDataList)) {
            return "请求数据不能为空";
        }
        // 进行数据校验
        List<Map> fruitNameEmptyList = requestDataList.stream().filter(fruitData -> ObjectUtils.isEmpty(fruitData.get("FRUIT_NAME"))).collect(Collectors.toList());
        if (fruitNameEmptyList.size() > 0) {
            return "水果的名称必填";
        }
        StringBuffer resultBuf = new StringBuffer();
        // 循环处理数据
        requestDataList.forEach(fruitData -> {
            // 水果名称
            String fruitName = fruitData.get("FRUIT_NAME").toString();
            // 返回结果
            String result = null;
            result = annotationUtil.getSpecificMethodForFruitDetail( fruitName, fruitData);
            resultBuf.append(result);
        });
        return resultBuf.toString();
    }


    /**
     * 请求的数据 转换
     * @param requestData
     * @return
     */
    public List<Map> getRequestDataList(JSONObject requestData) {
        try {
            Object requestData1 = requestData.get("RequestData");
            return JSONArray.parseArray(JSON.toJSONString(requestData1), Map.class);
        }catch (Exception e) {
            return null;
        }
    }

}
2.6 工具组件
/**
 * 工具组件
 */
@Component
public class AnnotationUtil {

    @Autowired
    private ApplicationContext applicationContext;

     /**
     * 方式一:获取指定业务方法
     * @param packagePath
     * @param fruitName
     * @param dataMap
     * @return
     */
    public String getSpecificMethodForFruitDetail( String fruitName, Map<String , Object> dataMap) {
        Map<String, Object> beansWithAnnotationMap = applicationContext.getBeansWithAnnotation(FruitDetail.class);
        IFruitDetailService fruitDetailService = null;
        // 匹配对应的bean
        for (Map.Entry<String, Object> beanEntry : beansWithAnnotationMap.entrySet()) {
            String annotationValue = beanEntry.getValue().getClass().getAnnotation(FruitDetail.class).value();
            if (annotationValue.equals(fruitName)) {
                fruitDetailService = (IFruitDetailService) beanEntry.getValue();
                break;
            }
        }
        String fruitDetail = fruitDetailService.getFruitDetail(fruitName, dataMap);
        return fruitDetail;
    }

    /**
     * 方式二:获取指定业务方法
     * 需引入 reflections 包
     * @param packagePath
     * @param fruitName
     * @param dataMap
     * @return
     */
    public String getSpecificMethodForFruitDetail(String packagePath, String fruitName, Map<String , Object> dataMap) {
        Reflections reflections = new Reflections(packagePath);
        Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(FruitDetail.class);
        List<Class<?>> fruitNameList = typesAnnotatedWith.stream().filter(bean -> bean.getAnnotation(FruitDetail.class).value().equals(fruitName)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(fruitNameList)) {
            return null;
        }
        Class<?> fruitClass = fruitNameList.get(0);
        // 创建bean实例
        IFruitDetailService fruitDetailService = (IFruitDetailService) applicationContext.getAutowireCapableBeanFactory().createBean(fruitClass, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
        return fruitDetailService.getFruitDetail(fruitName, dataMap);
    }
3.启动运行

使用postman工具对接口 “http://localhost:端口号/fruit/fruitDetail” 进行访问,报文格式如下:

{
    "RequestData":[
        {
            "FRUIT_NAME":"Apple",
            "DESCRIBE":"香甜可口,好吃不贵",
            "PRICE":"12.00"
        },
        {
            "FRUIT_NAME":"Apple",
            "DESCRIBE":"香甜可口,好吃不贵",
            "PRICE":"22.00"
        }
    ]
}
4. 总结

这种方式通过一个接口,即只用注册一个服务即可接收所有类型水果的数据,把主要精力放在业务处理逻辑上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值