logo头像

学如逆水行舟,不进则退!!!

文章目录

注解反射应用实战

前言

  • 之前两篇文章我们分别介绍了反射和注解的入门,其中也有同学给到建议是从具体应用场景出发更容易理解精髓!今天笔者就结合下自己实际项目中使用到的功能!这里算是抛砖引玉了

功能需求

  • 针对一张表做一个表单查询功能!这个功能十分的简单没有技术难点无非就是我们在写查询时注入条件写成动态条件判断!其他就没啥了

image-20210628142137773

  • 涉及公司内部数据,这里关键数据已经被我换掉了!
  • 除了查询以外,我们还有生成数据的功能。结合对数据的管理我们来看看如何注解反射双管齐下

数据说明

  • 首先数据存储在mongo中,查询是通过MongoTemplate来实现数据的查询
  • 存储的数据时间数据是时间戳,页面展示时需要格式化。这部分由接口处理
  • 关于数据状态已验收未验收实际存储的是1、2
  • 条件查询时目前是上面几种!每隔一段时间需要根据使用频率及用户反馈进行随意调节

实体定义

@Data
public class DangersBook implements Serializable {
    @TimeSequence
    @Ignore
    @Id
    @Query
    private Long id;
    @DangersNo
    @Ignore
    @Query
    @LikeQuery
    private String dangersNo;
    @Ids
    private String instance_id;
    @DangersType
    @Ids
    private String app_id;
    @Ids
    @Query
    @DangersEntId
    @OrQuery
    private String entId;
    private String created_by;
    @Query
    @Ids
    private String zuoyeleixing;
    @Date
    @Query
    private String pizhunkaishi;
    @Date
    @Query
    private String pizhunjieshu;
    @UpdateItem(value = "已验收")
    @Query
    @OrQuery
    private String yanshouStatus;
    @Query
    @Date
    @UpdateItem(value = "")
    private String finishTime;
    private String jianweixiuxukezheng;
    private String zuoyedengji;
    @Query
    @LikeQuery
    private String zuoyeneirong;
    /**
    * @Description 用于判断是否应该生成编号
    */
    @Ignore
    @Query
    private String createDangerNo;
    private String laowubanzu;
    @Query
    private String test;
    @Query
    private String interrupt;
    @Query
    @OrQuery
    private String year;
}

注解说明

  • 首先我们需要先通过反射获取到DangersBook中的所有属性!这是我们对反射的第一处运用!实际上在项目中凡是需要写活属性的获取的都是通过反射进行获取结合注解进行属性过滤!

TimeSequence

  • 在生成数据是数据的唯一id往往都是后端自行生成的这样才能保证真正的唯一!而TimeSequence注解就是对字段进行标注,这样我们在程序中只要识别到属性中存在该注解就会进行唯一id生成
TimeSequence annotation = declaredField.getAnnotation(TimeSequence.class);
if (annotation!=null) {
    //雪花算法
    long generatorId = DistributedIDUtils.getInstance().getGeneratorId(1l, 2l, 3l);
    declaredField.setAccessible(true);
    declaredField.set(object, generatorId);
}
  • 获取到TimeSequence之后我们就可以通过雪花算法生成id然后通过属性将生成的id赋值进去!

Ignore

  • 还有一种情况是有些数据我们不能进行任何操作,那么在反射中我们通过注解进行获取来进行更新数据时就需要将这些数据过滤掉。Ignore就是这个作用
Ignore ignore = declaredField.getAnnotation(Ignore.class);
if (ignore != null) {
    continue;
}

Query & Date & LikeQuery&OrQuery

  • 这三个都是在查询是涉及的。后面两个也是在Query基础上进行扩展的
com.ay.sdksm.custom.components.expose.annation.Query queryField = declaredField.getAnnotation(com.ay.sdksm.custom.components.expose.annation.Query.class);
if (queryField != null) {
    //需要添加查询条件
    try {
        declaredField.setAccessible(true);
        Object o = declaredField.get(dangersBook);
        if (null != o&&!"".equals(o.toString())) {
            String svalue = o.toString();
            Date dateAnnotation = declaredField.getAnnotation(Date.class);
            LikeQuery likeQuery = declaredField.getAnnotation(LikeQuery.class);
            if (dateAnnotation != null) {
                String dateValue = svalue;
                if (StringUtils.isEmpty(dateValue)) {
                    continue;
                }
                String[] dateSplit = dateValue.split(",");
                String start = dateSplit[0];
                String end = start;
                if (dateSplit.length >= 2) {
                    end = dateSplit[1];
                }
                criteria.and(declaredField.getName()).gte(start).lte(end);

            } else if(likeQuery != null){
                Pattern pattern = Pattern.compile(String.format(".*%s.*", svalue), Pattern.CASE_INSENSITIVE);
                criteria.and(declaredField.getName()).regex(pattern);
            }else {
                OrQuery orQuery = declaredField.getAnnotation(OrQuery.class);
                if (null != orQuery) {
                    String[] split = svalue.split(",");
                    String start = split[0];
                    String end = split[0];
                    if (split.length > 1) {
                        end = split[1];
                    }
                    criteria.and(declaredField.getName()).in(start, end);
                } else {
                    if (svalue.contains("$ne")&& JsonUtils.getInstance().isJson(svalue)) {
                        JSONObject jsonObject = JSONObject.parseObject(svalue);
                        String $ne = jsonObject.getString("$ne");
                        criteria.and(declaredField.getName()).ne($ne);
                    }else if(svalue.contains("$like")&& JsonUtils.getInstance().isJson(svalue)){
                        //模糊查询
                        JSONObject jsonObject = JSONObject.parseObject(svalue);
                        String $like = jsonObject.getString("$like");
                        criteria.and(declaredField.getName()).is(String.format("/%s/",$like));
                    } else {
                        Class<?> type = declaredField.getType();
                        if (type == Long.class) {
                            criteria.and(declaredField.getName()).is(Long.valueOf(svalue));
                        } else {
                            criteria.and(declaredField.getName()).is(svalue);
                        }
                    }
                }
            }
        }
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}
  • 首先需要配合Query注解表示需要对字段进行条件组装。当遇到Date我们就需要将条件值进行定制处理我们约定的条件值是已逗号间隔的时间段。这样我们就可以动态的支持时间点、时间段的查询。
  • 然后遇到LikeQuery表示是模糊查询。
  • 还有部分情况是取反查询!总之我们所有的需求都可以在注解Query基础后进行扩展。上面是初期版本其中没有经过设计表现出来就是有些if else荣誉代码!笔者这里也仅是展示注解的使用场景。
  • 后续或者说读者有时间可以自己根据设计模式中策略模式进行代码优化就会很简洁易懂!关于策略模式可以参考这篇文章就够了

DangersNo

DangersNo annotation = declaredField.getAnnotation(DangersNo.class);
if (annotation != null) {
    //根据业务生成指定格式的数据ID;这里比较复杂。代码省略。。。。。。
}

注解总结

  • 总的来说我们可以根据自己不同的业务需求开发出不同的注解表示不同的作用。然后在反射中全部同人对待!当遇到指定的注解时我们就进行对应的业务开发!这点很适合策略模式在代码中我们也可以简化我们的if else 。

反射应用场景

  • 上面注解的使用场景也可以理解成是反射的使用场景。但是那个主要是说注解的。反射大多是在框架中使用的比较多,因为框架需要高可调节性所以内部很多都是进行配置生效的。
  • 对于后端程序员来说Spring应该不会是很陌生的。在Spring中我们常见的也是必备的两个技能知识点是IOC+AOP。

IOC

  • IOC 依赖注入,在Spring中我们需要哪个对象不再需要向传统方式那样New了,而是通过Spring的注入功能就可以轻松获取一个对象,这不仅仅是方式的改变更是对我们操作的简化,我们不在需要为新创建的一个对象进行复杂的属性初始化了这些都被Spring完成了。他完成的主要手段就是通过反射。在项目初始化时spring会根据反射获取属性并进行属性填充!这是一个复杂的过程我们今天仅需牢记在项目初期会通过反射进行属性初始化!在我们使用到类的时候就可以直接通过类对象进行操作

AOP

  • AOP切面编程。你的项目中一定有日志功能,在每个接口处都需要对接口进行记录包括入参出参已经其中耗时等基本信息。如果让你实现是否需要在每个地方进行开发。这种方式我们可以理解成埋点操作。这种方式是不可取的。因为类似日志功能这种通用性的功能我们无法在每一处进行开发这很是耗费时间!
  • 在IOC的时候不仅仅是通过反射初始化属性还会生成代理对象。这个代理对象的生成过程笔者这里是将它理解成一次反射生成的代理对象。不知道这里理解是否正确?如果错误忘指出。

总结

  • 反射有的人说影响性能。这的确是影响性能,但是它提高我们程序的灵活性。我之前说过这事一个时间和空间的问题!鱼和熊掌不可兼得
  • 注解存在任何一个框架中。凡是一个成熟的框架必须有注解这样才能提高生产力

点赞,XDM

上一篇
坚持原创技术分享,您的支持将鼓励我继续创作!

评论系统未开启,无法评论!