自定义注解:让代码更加简洁优雅
一:前言
在 Java 开发中,注解是一种非常常见的语言特性。Java 自带了一些常用的注解,如 @Override、@Deprecated 等。但有时候我们需要自己定义一些注解,来标注我们自己的业务逻辑,或者是对第三方库的使用进行规范等。
自定义注解是一种非常强大的工具,可以让我们的代码更加简洁、优雅、易读,提高代码的可维护性。本文将通过一个例子来介绍如何自定义注解。
二:Java内什么是注解
在Java中,注解(Annotation)是一种元数据,它提供了关于程序代码的额外信息。注解可以用来描述类、方法、变量、参数等元素,它们不会直接影响程序代码的运行,但可以提供给编译器、解释器或其他工具使用。
注解使用@符号作为标记,后面紧跟着注解的名称和一组参数。例如,@Override注解用于指示方法是覆盖了父类中的方法。
Java中的注解有很多种类型,包括预定义的注解,如@Override和@Deprecated,还有自定义注解。自定义注解可以通过定义一个@interface来创建,并且可以指定一些元素来描述注解的属性。
注解在Java编程中的应用非常广泛,例如用于标记测试用例、配置文件、RESTful服务等等。通过注解,可以使程序代码更加简洁、清晰,同时也可以提高代码的可维护性和可读性。
三:打印日志准备
在我们的日常开发中,经常需要打印日志,以便我们能够了解程序的运行情况。我们可以使用 Java 自带的日志库 java.util.logging,也可以使用第三方的日志库,如 Log4j、Slf4j 等。
无论我们使用什么日志库,都需要写出类似下面的代码:
public class MyClass { private static final Logger logger = LoggerFactory.getLogger(MyClass.class); public void myMethod() { logger.debug("debug message"); logger.info("info message"); logger.warn("warn message"); logger.error("error message"); } }
每个方法都需要写出类似的代码,这显然很冗长。我们可以使用自定义注解来简化这个过程。
四:自定义注解
4.1 定义注解代码
首先我们需要定义一个注解:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String value(); }
这个注解的作用是在方法上打印日志,value() 方法返回需要打印的日志信息。
4.2 注解代码重点解释
1.@Target注解用于指定其他注解可以应用于哪些程序元素,它本身是一个元注解(Meta-Annotation),用于修饰其他注解。它的参数是一个ElementType数组,表示可以使用该注解的程序元素的类型。
2.@Retention注解用于指定注解的保留策略,即该注解是否会被编译器保留在class文件中,并且能否在运行时通过反射获取到。
在Java中,有三种保留策略,分别为:
SOURCE:该注解仅在源代码中保留,编译器会将其丢弃,不会保留到class文件中。
CLASS:该注解会被保留到class文件中,但不会被加载到内存中,因此无法在运行时获取到。
RUNTIME:该注解会被保留到class文件中,并且可以在运行时通过反射获取到。
4.3 使用此注解
接下来,我们可以在需要打印日志的方法上使用这个注解:
public class MyClass { private static final Logger logger = LoggerFactory.getLogger(MyClass.class); @Log("myMethod start") public void myMethod() { // do something } }
当我们调用 myMethod() 方法时,注解 @Log 将自动打印日志。这样,我们就不需要在每个方法中手动写出日志打印的代码了。
五:AOP切面实现读取注解
关于aop的知识大家可以看我的这篇文章:利用Spring框架实现横向关注点
下面是一个简单的实现。我们需要定义一个切面,来处理这个注解:
@Aspect @Component public class LogAspect { private static final Logger logger = LoggerFactory.getLogger(LogAspect.class); // 使用@Around注解,指定拦截含有@Log注解的方法 @Around("@annotation(com.example.Log)") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { // 获取方法签名和方法对象 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); // 获取@Log注解的值 Log logAnnotation = method.getAnnotation(Log.class); String logMsg = logAnnotation.value(); // 记录方法开始的日志信息 logger.info("{}.{} - start: {}", joinPoint.getTarget().getClass().getSimpleName(), method.getName(), logMsg); // 执行原方法 Object result = joinPoint.proceed(); // 记录方法结束的日志信息 logger.info("{}.{} - end", joinPoint.getTarget().getClass().getSimpleName(), method.getName()); // 返回方法的执行结果 return result; } }
这个切面使用了 AspectJ 的注解机制,使用 @Aspect 注解标注这是一个切面,使用 @Component 注解将这个切面注入到 Spring 容器中。
@Around 注解用于定义一个环绕通知,可以在方法执行前后进行一些操作。这个环绕通知中,我们通过 @annotation(com.example.Log) 注解来获取方法上的 @Log 注解,并从中获取日志信息。然后在方法执行前后打印日志。
六:具体使用
我们需要在 Spring 配置文件中开启注解扫描,以便 Spring 能够扫描到我们定义的注解和切面:
<context:component-scan base-package="com.example" />
然后我们就可以在代码中使用我们定义的 @Log 注解了:
public class MyClass { @Log("myMethod start") public void myMethod() { // do something } }
当我们调用 myMethod() 方法时,注解 @Log 将自动打印日志。
七:总结
自定义注解是一种非常强大的工具,可以让我们的代码更加简洁、优雅、易读,提高代码的可维护性。本文通过一个例子介绍了如何定义和使用自定义注解。在实际开发中,我们可以结合 AOP、反射等技术,使用自定义注解来实现更多的功能。