当前位置: 首页 > 科技观察

连注解都不会用,怎么能让Java爱上你

时间:2023-03-22 00:03:31 科技观察

转载本文请联系小菜良记公众号。了解“注释”也称为元数据。为我们在代码中添加信息提供了一种形式化的方式,使我们在以后使用这些数据非常方便。Java1.5之后引入的注释可以提供完整描述程序所需的信息,其格式可以由编译器测试和验证,并存储有关程序的附加信息。注解的使用很简单,只需要配合@符号即可。一些Java初学者经常把“注解”和“注解”混淆,但两者的功能是相似的。它们都用于描述信息。不同的是,“注解”所描述的信息是供应用程序阅读的,而“注解”所描述的信息是供开发人员查看的。初学者对“笔记”的印象可能不深,“笔记”可能不起眼但随处可见。最常见的是@Override,表示当前方法定义会覆盖父类的方法。如果拼写错误,或者方法签名与覆盖的方法不匹配,编译器将发出错误消息。既然提到了@Override注解,如果你仔细回忆一下,你的脑海里可能就会出现@SuppressWarnnings注解。还记得第一次看到这个注解的时候,Myeclipse提示我要用,我也没在意,就标记了。后来才知道这个注解是用来关闭编译器对类、方法、成员变量、变量初始化的警告。说完上面两个,再来一个,可能是最不常见的。那就是@Deprecated不要惊讶,因为这个类有一条横线,那是因为@Deprecated的作用。它的具体作用是标识一个方法或类,已经过时,不推荐使用。如果开发人员使用带有它注释的元素,编译器将发出警告消息。开始没多久,我们就已经学会了三个注解的使用。虽然只是基础,但还是用这三个注解来清线打怪吧。第一层程瓦注解一旦构造完成,就享有编译器的类型检查保护。再来看下一组代码预热:这是怎么回事?,这是我们自己制作的。然后key卖完了,接下来就是揭秘注解的创建了:@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceMyAnnotation{}这样一个简单的注解就新鲜出炉了。应该注意的是,这不是一个接口。需要留言,界面前有@。如果省略此标记,将产生巨大的差异。细心的朋友可能注意到了,在定义的注解的头部都有注解。这就是我接下来要讲的,敲黑板,注意!元注释来帮助定义注释时,将需要一些元注释。上面有两个,分别是@Target和@Retention。其中,@Target用于定义你的注解将应用到哪里(比如方法或域),@Retention用于定义注解在哪个级别可用。在源代码中(“SOURCE”),在类文件中(“CLASS”)或在运行时(“RUNTIME”)。Java提供了四种元注解,如下所示:NameUsage"@Target"标识可以使用注解的位置。ElementType参数包括:1.CONSTARUCTOR:构造函数声明2.FIELD:域声明(包括枚举实例)3.LOCAL_VARIABLE:局部变量声明4.METHOD:方法声明5.PACKAGE:包声明6.TYPE:类、接口(包括注解类型)或枚举声明“@Retention”表示注解信息需要保存在什么级别,RetentionPolicy参数包括:1.SOURCE:注解将被编译器丢弃2.CLASS:注解可用在class文件中,但是会被VMDiscarded3.RUNTIME:VM在运行时也会保留注解,所以可以通过反射机制读取注解信息"@Doc??umented"在JavaDoc中包含这个注解"@Inherited"允许子类从父类继承注解也分类我们在上面的例子中创建了一个@MyAnnotation注解。看起来很简单,没有任何内容,所以这种标注也被称为“标记标注”。有几种类型的注释:标记注释:注释内部没有属性。Usage:"@annotationname"//Define@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceMyAnnotation{}单值注解:注解内只有一个属性。使用方法:"@Annotationname(key=value)"//定义@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceSingleValueAnnotation{Stringname();}//使用@SingleValueAnnotation(name="test")publicvoidsingleTest(){}多值注解:注解里面有两个属性。使用方法:"@注解名称(key=value,key=value,...)"//定义@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceMultiValueAnnotation{Stringname();intnum();}//使用@MultiValueAnnotation(name="test",num=1)publicvoidmultiTest(){}也有默认值。当我们不使用标签注解的时候,如果我们在使用注解的时候没有给注解中的属性赋值,那么编译器就会报错,提示我们赋值。这很不方便。有时我们不使用它或者值是固定的,我们不想重复它。那么我们就需要使用“default”关键字来帮助我们解决这个问题。@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceMultiValueAnnotation{Stringname();intnum()default0;}我们在属性上使用default关键字来声明num属性的默认值为0,所以我们在使用上面的注解时,不需要手动给num赋值。第二遍builder的注解注解有让编译器进行编译检查的作用,但是如果没有读取注解的工具,那么注解也不会比注解有用。至少注解可以让开发者更直观的看到这段代码的用处。回到反射要创建和使用“注释处理器”,我们还需要使用反射机制来构造此类工具。这是一个简单的例子:.println("name:"+annotation.name()+"\nnum:"+annotation.num());}}}publicstaticvoidmain(String[]args){track(TestService.class);}}/*OUTPUT:name:testnum:0*/在上面的例子中,我们使用了两个反射方法:getDeclaredMethods()和getAnnotation()。其中getDeclaredMethods()用于返回该类的所有方法,getAnnotation()用于获取指定类型的注解对象。如果方法上没有这样的注解,则返回“null”值。注解元素的可用类型在上面的@MultiValueAnnotation注解中,我们定义了String类型的“name”和int类型的“num”。此外,我们还可以使用其他类型如下:“基本类型”(“int、float、boolean等”)“String”“Class”“enum”“Annotation”“上述类型的数组”如果其他类型比上面的都使用,编译会报错。并且需要注意的是我们在上面的例子中也看到了默认值“不能使用封装类型的基本类型”的限制,我们可以在使用注解的时候给注解属性赋值,或者给annotation在定义注解时为一个值默认值,但两者都说明了一个事情:“即注解元素不能有不确定的值,要么有默认值,要么在使用注解时提供元素的值。“基本元素没有空值,所以对于非基本类型的元素,无论是在使用中声明还是在定义时声明,“都不能取空值作为它的值”。因此,在实际开发中,我们常常将一些特殊的值定义为不存在的标识符,例如“负数”或“空字符串”。.接下来,我们将对注解有更深入的了解!注释也可以嵌套在修改后的注释元素中。当我们看到我们可以使用Annotation来修改它的时候,我想我们看到它的时候会觉得有点奇怪。在这里为你揭秘。先看一组注解:@Constraints@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public@interfaceConstraints{booleanprimaryKey()defaultfalse;booleanunique()defaultfalse;}@SQLString@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public@interfaceSQLString{Stringname()default"";Constraintsconstraints()default@Constraints;}我们在@SQLString注解中使用Constraints注解元素,并将默认值设置为@Constraints。此时Constraints中的值就是@Constraints注解中定义的默认值。如果我们要使用自定义,方法如下:@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public@interfaceSQLString{Stringname()default"";Constraintsconstraints()default@Constraints(primaryKey=true);这样,我们就可以使用自己定义的“value”注解了。不支持继承。我们不能使用extends来继承某个@interface,但是我们可以嵌套它来解决这个问题。AOP和注解“AOP”的搭配在今天的开发中对我们来说并不陌生,那么“AOP”和“注解”会产生什么样的化学反应呢?请看下面代码:@ApiLog:@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD,ElementType.TYPE})public@interfaceApiLog{/***接口名*/Stringname();}使用:@GetMapping(value="/getConfig")@ApiLog(name="获取系统相关配置")publicResultgetConfig()throwsException{returnsendOK(SystemService.getConfig(type));}Aop使用:@Aspect@ComponentpublicclassSysLogAspect{@AutowiredprivateLogServicelogService;@Pointcut("@annotation(cbuc.life.annotation.ApiLog)")publicvoidlogPointCut(){}@Around("logPointCut()")publicObjectaround(ProceedingJoinPointpoint)throwsThrowable{longbeginTime=System.currentTimeMillis();//执行方法Objectresult=point.proceed();//执行时长(毫秒)longtime=System.currentTimeMillis()-beginTime;//保存日志saveSysLog(point,time);returnresult;}privatevoidsaveSysLog(ProceedingJoinPointjoinPoint,longtime){MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();Methodmethod=signature.getMethod();LogEntitylog=newLogEntity();ApiLogapiLog=method.getAnnotation(ApiLog.class);if(apiLog!=null){//注解上的说明log.setMethodDescribe(syslog.value());}//请求的方法名StringclassName=joinPoint.getTarget().getClass().getName();StringmethodName=signature.getName();log.setMethod(className+"."+methodName+"()");//请求参数Object[]args=joinPoint.getArgs();Stringparams=JSON.toJSONString(args[0]);log.setParams(params);//获取requestHttpServletRequestrequest=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();//设置IP地址log.setIp(ServletUtil.getIpAddress(request));//用户名Stringusername=LoginInfo.getUsername();log.setUsername(username);//保存系统日志logService.save(log);}}传上面的代码,我们可以在希望记录日志的方法上加上@ApiLog注解,方法的动作就会记录在日志表中。不管方法名是什么,类在哪里,都可以轻松解析,没有代码入侵!