zoukankan      html  css  js  c++  java
  • JAVA自定义注解的使用的一个简单例子

      作为一个 JAVA 开发者,对注解这一概念一定是不陌生的。像我们平时常用的就有 @Controller, @Service,@Test,@Override 等等好多个,正确的使用注解确实可以方便我们的开发,以@Controller 为例,加上该注解后,框架层面为我们节省了一大堆需要在 Servlet 层面写的通用代码,大大减少了实际开发时的重复代码量。

      除了使用这些框架提供的注解外,我们也可以为我们的应用自定义注解方便自己的开发,下面我们来看一下如何自定义和使用注解,并在提供一个实际使用自定义注解的例子。

      注解的概念

      从JDK 1.5开始, Java增加了对元数据(MetaData)的支持,也就是 Annotation(注解)。 注解其实就是代码里的特殊标记,它用于替代配置文件:传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。 

      对于注解,官方的说法是:注解是一种能被添加到java代码中的元数据,类、方法、变量、参数和包都可以用注解来修饰。注解对于它所修饰的代码并没有直接的影响。

      注解实际上就是一种数据结构,为我们的类、属性、方法等添加附加信息,我们可以利用这些附加信息对宿主进行一些逻辑判断。

      注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。

      我们可以通过元注解来配置我们自定义注解的生命周期,在实际生产中,用的最多的是在运行期阶段使用注解,所以一般情况下我们会将自定义注解的生命周期配置为运行期生效。

      元注解

      上面说过,我们可以通过元注解来配置注解的生命周期,同样的我们可以使用元注解配置注解的作用对象等等基本属性。元注解是一种用于修饰注解的注解。

      在JDK 1.5中提供了4个标准的用来对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解),他们分别是:

       @Target、@Retention、@Documented、@Inherited

      我们可以使用这4个元注解来对我们自定义的注解类型进行注解,接下来,我们挨个对这4个元注解的作用进行介绍。

      1、Target注解:该注解的作用是:描述注解的使用范围(即:被修饰的注解可以用在什么地方) 。Target注解用来说明那些被它所注解的注解类可修饰的对象范围:注解可以用于修饰 packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数),在定义注解类时使用了@Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在ElementType 枚举中。

      2、Reteniton注解:该注解的作用是:描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时) 。Reteniton注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略,定义在RetentionPolicy枚举中。

      3、Documented注解:该注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。

      4、Inherited注解:该注解的作用是:使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)。
      其中 Target 和 Reteniton 是我们最常用的。

      例子

      注解的基础知识比较少,我们直接上手一个实际生产的例子。

      在生产中,遇到了一个用于描述评分信息的类,类的属性是评分时的各个评分项。评分项非常多(getter/setter 全部略过节约篇幅):

    public class SysMark extends BaseEntity {
        private String markReasons;
        private String fj1ReasonSelf;
    private Double fj1MarkSelf;
        private String fj2ReasonSelf;
    private Double fj2MarkSelf;
        private String fj3ReasonSelf;
    private Double fj3MarkSelf;
        private String fj4ReasonSelf;
    private Double fj4MarkSelf;
        private String fj5ReasonSelf;
    private Double fj5MarkSelf;
        private String jf1ReasonSelf;
    private Double jf1MarkSelf;
        private String jf2ReasonSelf;
    private Double jf2MarkSelf;
        private String jf3ReasonSelf;
    private Double jf3MarkSelf;
        private String jf4ReasonSelf;
    private Double jf4MarkSelf;
        private String jf5ReasonSelf;
    private Double jf5MarkSelf;
        private String jf6ReasonSelf;
    private Double jf6MarkSelf;
        private String llxz1ReasonSelf;
    private Double llxz1MarkSelf;
        private String llxz2ReasonSelf;
    private Double llxz2MarkSelf;
        private String llxz3ReasonSelf;
    private Double llxz3MarkSelf;
        private String llxz4ReasonSelf;
    private Double llxz4MarkSelf;
        private String zygw1ReasonSelf;
    private Double zygw1MarkSelf;
        private String zygw2ReasonSelf;
    private Double zygw2MarkSelf;
        private String zygw3ReasonSelf;
    private Double zygw3MarkSelf;
        private String jsyw1ReasonSelf;
    private Double jsyw1MarkSelf;
        private String jsyw2ReasonSelf;
    private Double jsyw2MarkSelf;
        private String jsyw3ReasonSelf;
    private Double jsyw3MarkSelf;
        private String jsyw4ReasonSelf;
    private Double jsyw4MarkSelf;
        private String zgsx1ReasonSelf;
    private Double zgsx1MarkSelf;
        private String zgsx2ReasonSelf;
    private Double zgsx2MarkSelf;
        private String zgsx3ReasonSelf;
    private Double zgsx3MarkSelf;
        private String zgsx4ReasonSelf;private Double zgsx4MarkSelf;
        private java.sql.Timestamp markTime;
        private java.sql.Timestamp updateTime;
        private String startTime;
        private String endTime;
        private String markId;
        private Double markMark;
        private String markMonth;
        private String markYear;
        private String markJiDu;
        private long uid;
        private String deptId;
    }

      目前接到的需求是,将评分项汇总,组成一个  “评分项1:得分1;评分项2:得分2...”  的字符串。其中每个评分项得分在实际语义中包含三种情况:加分项,减分项,否决项(总分直接判0)。

      在通常情况下,我们一般便会通过 if - else 逐个判断每个属性是否有打分,判断该评分项的类型来决定得分是加分、减分还是直接判 0 。 

      但这样做会带来很多不方便,几十个 if 判断不说,万一我们要新增或减少一个评分项或者配错/漏配了一个评分项,我们需要那 if 的代码逐行的与类中的属性比对,非常的繁琐。这时候我们可以考虑自定义一个注解用于修饰这些属性,在定义属性时便为其附加 评分项名称 以及加减分/否决的属性,然后通过反射对所有属性统一进行处理。

      我们来定义出这个注解:

    /**
     * @Author Nxy
     * @Date 2020/2/15 13:17
     * @Description 自定义评分原因注解
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = ElementType.FIELD)
    public @interface MarkReason {
        //评分项目名称
        public String reasonName();
        //是否减分项
        public boolean isSubtraction() default true;
        //是否否决项
        public boolean isFouJue() default false;
    }

      其中两个元注解的含义便是:@Retention(RetentionPolicy.RUNTIME):该注解在运行期生效;@Target(value = ElementType.FIELD):该注解作用于属性。

      我们为注解定义了三个属性 resonName、isSubtraction、isFouJue,被 MarkReason 注解修饰的属性可以拥有这三个属性。

      我们用该注解对上述评分类进行修饰后评分类变成这样:

    public class SysMark extends BaseEntity {
        private String markReasons;
        private String fj1ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众发生违法行为", isFouJue = true)
        private Double fj1MarkSelf;
        private String fj2ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众发生撞“红线”及以上问题", isFouJue = true)
        private Double fj2MarkSelf;
        private String fj3ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众参与群体上访、越级上访", isFouJue = true)
        private Double fj3MarkSelf;
        private String fj4ReasonSelf;
        @MarkReason(reasonName = "责任区发生打架斗殴等不良行为", isFouJue = true)
        private Double fj4MarkSelf;
        private String fj5ReasonSelf;
        @MarkReason(reasonName = "经研究其他否决项目的问题", isFouJue = true)
        private Double fj5MarkSelf;
        private String jf1ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众发现安全重大隐患、防止安全事故、受到段级及以上表扬表彰或通报嘉奖", isSubtraction = false)
        private Double jf1MarkSelf;
        private String jf2ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众参加技术比武获得名次", isSubtraction = false)
        private Double jf2MarkSelf;
        private String jf3ReasonSelf;
        @MarkReason(reasonName = "积极组织责任区党员群众 围绕安全、运输和技术难题立项攻关取得实效,受到总公司、 集团公司、段表彰", isSubtraction = false)
        private Double jf3MarkSelf;
        private String jf4ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众完成急难险重任务成绩突出", isSubtraction = false)
        private Double jf4MarkSelf;
        private String jf5ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众做好人好事、见义勇为事迹受到表彰奖励或媒体表扬", isSubtraction = false)
        private Double jf5MarkSelf;
        private String jf6ReasonSelf;
        @MarkReason(reasonName = "其他受到集团公司及以上表彰奖励", isSubtraction = false)
        private Double jf6MarkSelf;
        private String llxz1ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众无故不参加上级组织的集体活动")
        private Double llxz1MarkSelf;
        private String llxz2ReasonSelf;
        @MarkReason(reasonName = "责任区内环境卫生差、备品摆放不整齐")
        private Double llxz2MarkSelf;
        private String llxz3ReasonSelf;
        @MarkReason(reasonName = "班组标准化验收不达标")
        private Double llxz3MarkSelf;
        private String llxz4ReasonSelf;
        @MarkReason(reasonName = "班组未完成生产任务,运输组织工作,旅客、货主等服务工作受到上级批评")
        private Double llxz4MarkSelf;
        private String zygw1ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众发生迟到、早退")
        private Double zygw1MarkSelf;
        private String zygw2ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众发生严重“两违”问题")
        private Double zygw2MarkSelf;
        private String zygw3ReasonSelf;
        @MarkReason(reasonName = "作业提醒不到位")
        private Double zygw3MarkSelf;
        private String jsyw1ReasonSelf;
        @MarkReason(reasonName = "不参加月度业务考试、模拟演练")
        private Double jsyw1MarkSelf;
        private String jsyw2ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众月度考试或抽考成绩不达标")
        private Double jsyw2MarkSelf;
        private String jsyw3ReasonSelf;
        @MarkReason(reasonName = "应知应会考试、专业技能考核不达标")
        private Double jsyw3MarkSelf;
        private String jsyw4ReasonSelf;
        @MarkReason(reasonName = "责任区党员群众技术业务帮带效果不明显")
        private Double jsyw4MarkSelf;
        private String zgsx1ReasonSelf;
        @MarkReason(reasonName = "对责任区内职工思想动态不掌握、不熟悉、不了解,不能及时做思想工作")
        private Double zgsx1MarkSelf;
        private String zgsx2ReasonSelf;
        @MarkReason(reasonName = "未及时与发生“两违”问题的党员群众谈心谈")
        private Double zgsx2MarkSelf;
        private String zgsx3ReasonSelf;
        @MarkReason(reasonName = "未及时与困难党员群众谈心谈话")
        private Double zgsx3MarkSelf;
        private String zgsx4ReasonSelf;
        @MarkReason(reasonName = "未及时化解矛盾造成不良影响")
        private Double zgsx4MarkSelf;
        private java.sql.Timestamp markTime;
        private java.sql.Timestamp updateTime;
        private String startTime;
        private String endTime;
        private String markId;
        private Double markMark;
        private String markMonth;
        private String markYear;
        private String markJiDu;
        private long uid;
        private String deptId;
    }

      这样一来,在每个属性被定义时,它的语义便被一同写进了其注解中。我们通过反射获取每个属性的注解,对所有属性进行统一的处理:

        /**
         * @Author Nxy
         * @Date 2020/2/15 14:14
         * @Description 汇总加减分原因
         */
        public static void setMarkReasons(BaseEntity markBean) throws IllegalAccessException, NoSuchFieldException {
            Class beanClass = markBean.getClass();
            Field[] fields = beanClass.getDeclaredFields();
            if (fields == null || fields.length == 0) {
                throw new RuntimeException(markBean + " has no field");
            }
            Field targetField = beanClass.getDeclaredField("markReasons");
            StringBuilder reasonsSb = new StringBuilder();
            //遍历属性
            for (Field field : fields) {
            //判断该属性是否被 MarkReason 注解修饰
    if (field.isAnnotationPresent(MarkReason.class)) { //允许私有属性访问 field.setAccessible(true); String isSubtraction = "-"; MarkReason reasonAnno = field.getAnnotation(MarkReason.class);
              Object markMark = field.get(markBean);
              //判断该项是否已评分
              if(markMark==null){
                continue;
              }
    //判断是否是减分项 if (!reasonAnno.isSubtraction()) { isSubtraction = "+"; } String project=""; //判断是否否决项 if(reasonAnno.isFouJue()){ project = "-100"; }else { project = isSubtraction + (double) markMark; } //拼装加减分原因 reasonsSb.append(reasonAnno.reasonName() + ":" + project + ";"); } }
         //汇总后将结果写入对象 targetField.setAccessible(
    true); targetField.set(markBean, reasonsSb.toString()); }

      通过上面的方法,我们只需要在定义一个新的评分项时将其用 @MarkReason 注解修饰即可,汇总评分原因的代码不需要做任何改变。而使用传统的 if 判断的方法时,新增/删除/改变一个评分项,相应的 if 节点的代码都需要做出对应的改变。并且相较 if 判断,该方法的代码量也减少了非常多。

     

  • 相关阅读:
    Object的公用方法
    Java的特点
    Set集合
    Java语言的三大特性
    List集合
    Collection类
    HashSet
    Codeforces1141F2 Same Sum Blocks (Hard)
    Codeforce1176F Destroy it!
    jzoj 5348. 【NOIP2017提高A组模拟9.5】心灵治愈
  • 原文地址:https://www.cnblogs.com/niuyourou/p/12312879.html
Copyright © 2011-2022 走看看