这篇文章主要介绍“怎么使用注解”,在日常操作中,相信很多人在怎么使用注解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么使用注解”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
首关之程娲造注
注解一旦构造出来,就享有编译器的类型检查保护。让我们先看下一组代码热热身:
public class TestService { @MyAnnotation public void runTset() { System.out.println("annotation test"); } }
不要纳闷,Java 中确实没有一个注解名为MyAnnotation,但这个怎么来的呢,就是我们自己造的。
那么关子卖完了,接下来就来揭秘注解的制造:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { }
这样子,一个简单的注解就新鲜出炉了。需要注意的是这可不是一个接口,需要留言interface前面还有一个@,这个标识要是漏掉了,那可以天差地别。
细心的小伙伴可能注意到了,定义的注解头上怎么还有注解。
这就是接下来要讲到,敲黑板,注意看!
元注解来帮忙
在定义注解时,会需要一些元注解。上面出现了两个,分别是@Target和@Retention.
其中@Target用来定义你的注解将应用于什么地方(例如一个方法或一个域),@Retention用来定义该注解在哪一个级别可用,在源代码中(「SOURCE」),类文件中(「CLASS」)或者运行时(「RUNTIME」)。Java 提供了四种元注解,如下:
名称 | 用处 |
---|---|
「@Target」 | 标识该注解可以用于什么地方。其中 ElementType 参数包括:1. CONSTARUCTOR :构造器的声明2. FIELD :域声明(包括enum实例)3. LOCAL_VARIABLE :局部变量声明4. METHOD :方法声明5. PACKAGE :包声明6. TYPE :类、接口(包括注解类型)或enum 声明 |
「@Retention」 | 表示需要在什么级别保存该注解信息,其中RetentionPolicy 参数包括:1. SOURCE :注解将被编译器丢弃2. CLASS :注解在 class 文件中可用,但会被 VM 丢弃3. RUNTIME :VM 将在运行期也保留注解,因此可以通过反射机制读取注解的信息 |
「@Documented」 | 将此注解包含在 JavaDoc 中 |
「@Inherited」 | 允许子类继承父类的注解 |
注解也分类
我们在上面示例中创建了一个 @MyAnnotation 注解。看起来很简单,没什么内容,因此这种注解我们也称为 「标记注解」,注解也分几类:
标记注解:注解内部没有属性。使用方式:「@注解名」
//定义 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { }
单值注解:注解内部只有一个属性。使用方式:「@注解名(key = value)」
//定义 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface SingleValueAnnotation { String name(); } //使用 @SingleValueAnnotation(name = "test") public void singleTest() {}
多值注解:注解内部有过个属性。使用方式:「@注解名(key = value, key = value, ...)」
//定义 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MultiValueAnnotation { String name(); int num(); } //使用 @MultiValueAnnotation(name = "test", num = 1) public void multiTest() {}
值也有默认
当我们使用的不是标记注解时,如果在使用注解的时候不给注解中的属性赋上值,那么编译器就会报错,提示我们需要赋值。
这样子是很不方便,有时候我们并没有使用到或值是固定的不想重复写,那么这个时候就需要借助「default」关键字来帮忙我们解决这种问题。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MultiValueAnnotation { String name(); int num() default 0; }
我们在属性上使用了 default 关键字来声明 num 属性的默认值为 0 ,这样子我们在使用上述那个注解的时候就可以不用手动给num赋值了。
次关之造器解注
注解具有让编译器进行编译检查的作用,但是如果没有用来读取注解的工具,那注解也不会比注释更有用,起码注释可以让开发人员更直观的看到此段代码的用处。
重回反射想要创建与使用 「注解处理器」,我们还需要借助反射机制来构造这类工具。以下是简单的例子:
public class AnnotationHandle { public static void track(Class<?> c) { for (Method m : c.getDeclaredMethods()) { MultiValueAnnotation annotation = m.getAnnotation(MultiValueAnnotation.class); if (annotation != null) { System.out.println("name:" + annotation.name() + "\n num:" + annotation.num()); } } } public static void main(String[] args) { track(TestService.class); } } /* OUTPUT: name:test num:0 */
在上述例子中我们用到了两个反射的方法:getDeclaredMethods()和getAnnotation()。
其中getDeclaredMethods() 用来返回该类的所有方法,getAnnotation()用来获取指定类型的注解对象。如果方法上没有该注解则会返回 「null」 值。
注解元素可用类型
上述@MultiValueAnnotation注解中我们定义了 String类型的 「name」 和 int类型的 「num」,除此之外我们还可以使用其他类型如下:
「基本类型」(「int、float、boolean等」)
「String」
「Class」
「enum」
「Annotation」
「以上类型的数组」
如果使用了上面以外的其他类型,那么编译器就会报错。而且要注意的是,「也不能使用基本类型的包装类型」
默认值的限制
上述例子中我们也看到了,我们可以在使用注解的时候给注解属性赋值,也可以在定义注解的时候给注解一个默认值,但是这两者都说明了一件事:「那就是,注解元素不能有不确定的值,要么具有默认值,要么在使用注解时提供元素的值」
基本元素不存在null值,因此对于非基本类型的元素,无论是在使用中声明,还是在定义时声明, 「都不能将 null 值作为其值」。因此在实际开发中,我们往往会定义一些特殊值作为不存在的标识,例如 「负数」 或 「空字符串」
三关之运注帷幄
在前面两关中,我们学会了定义注解和创建注解处理器。接下来我们就要来更加深入掌握注解!
注解也能嵌套
在修饰注解元素的时候我们看到可以使用Annotation来修饰,估计看到那的时候会觉得有点奇怪。在这里就来为你来揭秘。
先来看一组注解:
@Constraints
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Constraints { boolean primaryKey() default false; boolean unique() default false; }
@SQLString
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLString { String name() default ""; Constraints constraints() default @Constraints; }
我们在@SQLString注解中使用Constraints注解元素,并将默认值设为@Constraints。这个时候Constraints中的值都是@Constraints注解中定义的默认值,如果我们要使用自定义的话,做法如下:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLString { String name() default ""; Constraints constraints() default @Constraints(primaryKey = true); }
这样子我们就可以使用自己定义的「value」
注解不支持继承
我们不能使用extends来继承某个@interface,但是可以通过嵌套的方式来解决这一烦恼。
AOP与注解的搭配
「AOP」 在当今开发中我们并不陌生,那么 「AOP」 和 「注解」 能产生什么化学反应呢,请看以下代码:
@ApiLog:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface ApiLog { /** * 接口名称 */ String name(); }
使用:
@GetMapping(value = "/getConfig") @ApiLog(name = "获取系统相关配置") public Result getConfig() throws Exception { return sendOK(SystemService.getConfig(type)); }
Aop使用:
@Aspect @Component public class SysLogAspect { @Autowired private LogService logService; @Pointcut("@annotation(cbuc.life.annotation.ApiLog)") public void logPointCut() { } @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); //执行方法 Object result = point.proceed(); //执行时长(毫秒) long time = System.currentTimeMillis() - beginTime; //保存日志 saveSysLog(point, time); return result; } private void saveSysLog(ProceedingJoinPoint joinPoint, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); LogEntity log = new LogEntity(); ApiLog apiLog = method.getAnnotation(ApiLog.class); if(apiLog != null){ //注解上的描述 log.setMethodDescribe(syslog.value()); } //请求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); log.setMethod(className + "." + methodName + "()"); //请求的参数 Object[] args = joinPoint.getArgs(); String params = JSON.toJSONString(args[0]); log.setParams(params); //获取request HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); //设置IP地址 log.setIp(ServletUtil.getIpAddress(request)); //用户名 String username = LoginInfo.getUsername(); log.setUsername(username); //保存系统日志 logService.save(log); } }
到此,关于“怎么使用注解”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。