Java注解基本原理解析和生产使用场景(精华)

一,引言

1.1 引入注解

我们经常使用便利贴用来提醒我们在某个时间点检查下系统的运行状态。我们可以把便利贴贴在不同的地方用于提醒我们做一件事情。其实注解的作用是相同的,需要一个固定化的结构让java能够读取。这个也属于设计模式的注释模式,可以极大的提高开发的效率。

在这里插入图片描述

1.2 文字概念

注解(Annotation)是撰写在原始码中的资讯,可以提供代码以外的额外资讯,本身有其结构,你可以使用工具提取这些结构化讯息。

简单来说注解就是写在程序上方的结构化注释,java会按照其结构读取讯息,并且做对应操作。比如@Override注解标注在方法上,在编译期间,java就会检查当前方法是否重载了父类的方法,如果没有就报错。

1.3 java的注解

  1. 注解是一种数据类型,我们可以通过class,interface定义类和接口一样。通过@interface可以定义一个注解。

  2. 注解可以有成员变量,但是没有方法。成员变量可以有默认的值。成员变量的类型可以使java中所有的数据类型,包含注解类型本身。

[@Target]
[@Retention]
[@Documented]
[@Inherited]
public @interface [名称] {
    // 元素
     String value() default "hello";
}

1.4 注解的三种分类

1.4.1 java自带标准注解
  1. @Override:表示当前的方法定义将覆盖父类中的方法
  2. @Deprecated:表示代码被弃用,如果使用了被@Deprecated注解的代码则编译器将发出警告
  3. @SuppressWarnings:表示关闭编译器警告信息
1.4.2 元注解-定义注解的注解
注解名称作用可能值
@Retention说明了这个注解的存活时间SOURCE,CLASS ,RUNTIME
@Documented将注解中的元素包含到 Javadoc 中去
@Target限定注解可以标注的位置ANNOTATION_TYPE、CONSTRUCTOR 、FIELD 、LOCAL_VARIABLE 、METHOD 、PACKAGE 、PARAMETER 、TYPE
@Inherited子类自动拥有父类的注解
@Repeatable

注1:

  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视
  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
1.4.3 自定义注解

利用上述注解生成自己定义的注解,就像自己定义一个类一样,可以通过@interface创建一个自定义注解。稍后会讲解注解的价值和使用。

注解的价值

  1. 生成文档,通过代码里标识的元数据生成javadoc文档。
  2. 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
  3. 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
  4. 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例

注:前三种作用都是在编译期间当编译器扫描到注解时,进行操作。
最后一种运行期间动态处理是通过java的反射,当扫描到注解时通过反射获取到类的方法和属性上的注解及注解的属性值。按照

自定义注解生产使用案例

很多博客都介绍了如何通过java反射获取方法或者属性的注解,但是很少有人讲清楚到底如何使用这些注解,这里我寻找了几个生产中使用的场景,希望大家可以理解注解的如何使用。

场景1:在spring启动时获取标注某个注解的对相集合

@Component
public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
    public static Map<String, Object> beanMap = new HashMap<>();
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {

        //获取标注有TargetBean注解的bean
      beanMap = event.getApplicationContext().getBeansWithAnnotation(TargetBean.class);

        System.out.println(beanMap);
    }
}

https://blog.csdn.net/qq_37334435/article/details/91383537
https://my.oschina.net/mfkwfc/blog/60885

场景2: 利用注解实现对象信息统计和汇总

这个来自于网友生产案例,很有代表性的讲述了注解的使用。

评分类

package annotation.demo2;

/**
 * 业务逻辑:总分100分,根据各个字段规则为每人评分,评分最高的3名学生可以得到奖学金。
 *  注:这里只给出了6个字段,实际上评分项可能有几十个,如果通过if else判断代码会非常臃肿
 *
 *  1,如果选项是否决项,则直接分数为0
 *  2,如果选项是减分项则从总分中减去对应分值
 *  3,如果选项是普通项则计入总分
 *
 */
public class StudentMark {
    private  String name;
    @MarkReason(reasonName = "积极参加学校活动")
    private Double point1; //评分项1  加分项
    @MarkReason(reasonName = "挂科超过2门以上",isSubtraction = true)
    private Double point2; //评分项2  减分项
    @MarkReason(reasonName = "考试作弊行为",isFouJue = true)
    private Double point3; //评分项3  否决项(分数直接为0)
    @MarkReason(reasonName = "成绩达到优秀的课程数量超过6门")
    private Double point4; //评分项4
    @MarkReason(reasonName = "获得创业大赛前三名")
    private Double point5; //评分项5
    @MarkReason(reasonName = "1年内已经领取过奖学金",isFouJue = true)
    private Double point6; //评分项6
    @MarkReason(reasonName = "旷课行为超过2次",isSubtraction = true)
    private Double point7; //评分项6
    private String summary; //汇总

    public StudentMark(String name,Double point1, Double point2, Double point3, Double point4, Double point5, Double point6, Double point7) {
        this.name = name;
        this.point1 = point1;
        this.point2 = point2;
        this.point3 = point3;
        this.point4 = point4;
        this.point5 = point5;
        this.point6 = point6;
        this.point7 = point7;
    }

//省略getset

注解


/**
 * 注解定义了三个属性 resonName、isSubtraction、isFouJue,被 MarkReason 注解修饰的属性可以拥有这三个属性。
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface MarkReason {
    //评分项目名称
    public String reasonName();
    //是否减分项
    public boolean isSubtraction() default false;
    //是否否决项
    public boolean isFouJue() default false;
}

解析注解工具类

package annotation.demo2;![在这里插入图片描述](https://img-blog.csdnimg.cn/20200401124458373.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2tvdXJ5b3VzaGluZQ==,size_16,color_FFFFFF,t_70)

import java.lang.reflect.Field;

public class AnnotationUtil {

    public static void setMarkReasons(StudentMark bean) throws NoSuchFieldException, IllegalAccessException {

        //获取到目标对象所有的字段
        Field []fields =  bean.getClass().getDeclaredFields();

        double score=100;
        String isSubtraction="";
        boolean isFouJue =false;
        StringBuilder sbReason = new StringBuilder();
        for(Field field:fields){
            //判断字段上是否标注了MarkReason自定义注解
            if(field.isAnnotationPresent(MarkReason.class)  ){
                    field.setAccessible(true); //允许访问私有属性

                //获取字段上注解类型,并且计算分值
                MarkReason markReason = field.getAnnotation(MarkReason.class);
                double fieldvalue = (double)field.get(bean);
                if(markReason.isFouJue()){
                    score=0;
                    isFouJue=true;
                }else if(markReason.isSubtraction()){
                    score-=fieldvalue;
                    isSubtraction="-";
                }else {
                    score+=fieldvalue;
                    isSubtraction="+";
                }
                //拼接统计项
                sbReason.append(markReason.reasonName()+":"+isSubtraction+fieldvalue);
            }

        }
        score=isFouJue?0:score;
        sbReason.append("总分:"+score);

        Field targetField = bean.getClass().getDeclaredField("summary");
        targetField.setAccessible(true);
        targetField.set(bean,sbReason.toString());//设置summary字段的值
    }
}

测试类


public class ClientTest {
    public static void main(String[] args) {
        StudentMark stu1 = new StudentMark("张三",33d,33d,33d,223d,31d,21d,32d);
        StudentMark stu2 = new StudentMark("张三",33d,33d,32d,223d,31d,21d,32d);
        try {
            AnnotationUtil.setMarkReasons(stu2);
            AnnotationUtil.setMarkReasons(stu1);
            System.out.println(stu1.toString());
            System.out.println(stu2.toString());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

测试结果

{"name":"张三","point1":33.0,"point2":33.0,"point3":33.0,"point4":223.0,"point5":31.0,"point6":21.0,"point7":32.0,"summary":"积极参加学校活动:+33.0挂科超过2门以上:-33.0考试作弊行为:-33.0成绩达到优秀的课程数量超过6门:+223.0获得创业大赛前三名:+31.01年内已经领取过奖学金:+21.0旷课行为超过2次:-32.0总分:0.0"}

结果说明:

上述案例有很多不严谨的地方,但是不妨碍我们理解注解在这个案例的价值。如果字段更多注解的价值就更大,把一些字段的额外属性保存在注解中。通过反射获取注解和字段的属性进行业务逻辑实现。可以节省代码是业务逻辑更加专注。

结论

希望大家能够通过上面两个例子理解注解的概念,原理,和使用的场景。需要发挥我们的想象力利用注解灵活实现我们的业务。

参考

https://www.cnblogs.com/niuyourou/p/12312879.html
https://baijiahao.baidu.com/s?id=1637216017950921752&wfr=spider&for=pc

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值