安琪拉教鲁班玩Java反射-业务场景篇

安琪拉教鲁班玩Java反射-业务场景篇

Java 语言中有很多特性,其中有一项很重要的特性就是反射,我们在很多框架类的代码中可能会看到反射的身影,那实际在业务逻辑层是否可以用反射特性做些事情呢? 安琪拉在完成最近的一个业务需求时就用了反射,这里记录一下使用历程。

前言

声明: 本系列文章分为三部分,此篇是开头第一篇《反射的业务使用场景》

  • 反射的业务使用场景
  • 反射的原理
  • 通用服务能力模型设计

为了给日常的curd 增加一些难度,或者说为了让工作更好玩,鲁班开始折腾起自己手上的工具,某天小鲁班发现自己的抢还有反射的技能,于是向安琪拉请教如何达到最佳释放效果。

鲁班:安琪拉,我最近接到了一个需求,要在下路把对方每一波过来的小兵做标注,只有遇到特定的小兵,我才开火。

安琪拉: 那这些小兵有什么特点呢? 你打算怎么精准定位要开火的小兵?

鲁班:如果小兵身上的符文是红色符文(除此以外,还有蓝色符文和紫色符文),法术防御是魔法防御(除此以外,还有物理防御),我只对这些小兵开火,当然咯,可能以后还需要对带各种属性的小兵进行开火打击。

安琪拉: 需求我大概清楚了,你有思路了吗?我们先把模型建立起来,如下图所示,是小兵的模型

在这里插入图片描述

一个小兵有很多属性, 属性可能还是符合类型, 如上图,小兵可能有符文、盔甲、铲子,法术,等等。

你有实现对含有指定属性值的小兵开火的实现思路了吗?例如,对红色符文 或 魔法防御的进行开火。

鲁班:我的想法就是这样子的,先创建Batman(小兵)类,然后对rune(符文) 和 magicArts 法术属性做判断,如何满足前面的条件,我就open fire. yoyoyo 就是这么简单,突然觉得自己英语非常丝滑。

安琪拉: ok,先按照你的思路写一般实现。

鲁班:为了快速聚焦业务,简化版本如下

//1. 小兵类
public class Batman {
   private long id;
    /**
     * 符文
     */
    private String rune;

    private String armor;

    private String shovel;

    private Map<String,String> magicArts;

    //getter and setter
}

然后开始写业务逻辑

//2. 定义开火接口
public interface OpenFire {

    /**
     * 开火
     */
    void openFire(Batman batman);
}

//3. 实现开火接口
public class OpenFireImpl implements OpenFire{

    @Override
    public void openFire(Batman batman) {
        //如果是红色符文, 或者防御是魔法防御 ===> 开火
        Map<String, String> magicArts;
        if(StringUtil.equalsIgnoreCase(batman.getRune(), "red")
            || (MapUtils.isNotEmpty(magicArts = batman.getMagicArts()) && StringUtil.equalsIgnoreCase(magicArts.get("defense"), "magic"))) {
            System.out.println("open fire:" + batman.getId());
        }
    }
}

bingo,写完了,神速

安琪拉: 看上去没什么问题,这时候如果产品经理跟你讲,我现在需要盔甲是魔女斗篷时,你给我开火;另外原来红色符文和魔法防御二者满足其一就可以改成必须二者都符合才开火,另外小兵现在新增加了几个属性: 召唤师技能、被动等,需要判断召唤师技能为某种类型时你开火,你怎么实现?

鲁班 :产品需求如果这么改,我只好亮出我40米的大砍刀,

在这里插入图片描述

安琪拉: 那如果是你老板让你改呢,想清楚, 3.25 还是 3.75,再回答

鲁班 :o(╥﹏╥)o,那好吧,我再多加个判断语句,这种苦逼日子何时是个头啊?

安琪拉: 你可以想想,把这个业务场景抽象一层,做成业务平台的通用能力,比如,如果下次再有需求,是对具有指定属性值的野怪、对面英雄做标记,或者进行其他一些业务逻辑处理,你是不是可以复用这套平台通用能力。

鲁班 :这么一说有点道理,似乎平常有很多这种场景,对一组集合对象,按照集合对象的属性值做过滤筛选,然后做后置业务逻辑处理。

安琪拉: 是的,那我们来对这一类业务场景做归纳,这种通用能力我们取一个名字,叫做精准定位,按照对象的某个或某几个属性进行对象定位。

我们先来实现这个前面的这个需求场景。

第一步,首先我们需要有个能力,判断某个对象的某个属性值是否为预期属性值,这个是最小原子能力,以前面例子为例,就是判断小兵对象的符文属性值是否为红色。

代码如下: 如果你对👇 代码有疑惑,后面会讲反射的时候解释这段代码,先浏览一下。

//精准定位服务
public class PreciseLocateService {

    //定位目标是否为预期目标
    public  <T> boolean locateTarget(Class<T> tClass, T obj, String propertyName, Object propertyVal) {
        Object extractPropertyVal = extractPropertyVal(tClass, obj, propertyName);
        return Objects.equals(extractPropertyVal, propertyVal);
    }

    //提取指定对象的指定属性值
    public <T> Object extractPropertyVal(Class<T> tClass, T obj, String propertyName) {
        if(Objects.isNull(tClass) || Objects.isNull(obj) || StringUtil.isBlank(propertyName)) {
            return null;
        }
        // class如果是原始类型 直接返回false
        if (tClass.isPrimitive()) {
            return false;
        }
        // 如果属性是map类型, 采用get方法
        if (Map.class.isAssignableFrom(tClass)) {
            return ((Map) obj).get(propertyName);
        }
        PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(tClass, propertyName);
        Method method = propertyDescriptor.getReadMethod();

        if (Objects.isNull(method)) {
            return null;
        }

        try {
            return method.invoke(obj);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

我们对前面的业务需求再实现一遍。

public class OpenFireImpl implements OpenFire{

  	@ref
    PreciseLocateService preciseLocateService;

    @Override
    public void openFire(Batman batman) {

        //如果是红色符文, 或者防御是魔法防御 ===> 开火
        //方案1
        Map<String, String> magicArts;
        if(StringUtil.equalsIgnoreCase(batman.getRune(), "red")) {
            System.out.println("open fire:" + batman.getId());
        }

        //方案2
        if(preciseLocateService.locateTarget(Batman.class, batman, "rune", "red")){
            System.out.println("open fire:" + batman.getId());
        }
    }
}

鲁班: 还是前面的需求,如果新增很多属性判断的业务逻辑,或者新增属性,难道不需要加很多 locateTarget的代码吗?

安琪拉: 这个是个很好的问题,在我的业务场景中,是把条件、精准定位规则、业务逻辑做成了配置放在配置中心,可以自由灵活配置,这个在下一篇文章会讲到,今天重点是定位服务中引出来的反射。

鲁班: 那什么是反射啊? 上面这个代码又是如何用反射的呢?

安琪拉: 简单来说,反射就是Java 允许运行时发现和使用类型的信息。

我们以Batman(小兵) 类为例,Batman.java 代码编写完成之后,首先会编译成Batman.class 字节码文件,我们知道Java 是平台无关的,在于不同平台JDK 都认识同样的.class 字节码。

在这里插入图片描述

简化一下,Batman.class 类加载信息存放,如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dCdJDw52-1608741034782)(/Users/jiulong/Library/Application Support/typora-user-images/image-20201224001606767.png)]

  • 方法区

    类描述信息(版本、字段、方法、接口等信息)

  • 对象实例数据,例如 preciseLocateService 引用就存在堆中,以为属于对象实例数据

  • 虚拟机栈

    方法内的局部变量,入参等,例如batman。

未完,后面还有二篇,分别是关于反射原理和实际业务场景设计。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值