Java注解怎么用

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Java注解怎么用

什么是注解

Java的注解(Annotation)是一种元数据,它可以提供程序的额外信息,帮助程序员更好地管理程序。注解通常被用作代码的标记或者指定某些行为的方式。在Java中,注解以@符号开头,放在代码的各个位置,包括类、方法、成员变量、参数等地方。注解可以通过反射机制在程序运行时获取到,并能对程序的执行产生一定的影响。Java提供了一些系统注解,例如@Override、@Deprecated等等,同时也支持自定义注解。

注解和注释有什么区别

注解和注释看似相似,但它们在意义和使用方式上有很大的不同。


注释(Comment)是程序员在代码中添加的一些说明信息,它们并不会对程序的运行产生任何影响,仅仅是为了方便程序员对代码的理解和维护。注释可以是单行或多行,以 // 或 /.../ 形式添加。


注解(Annotation)则是一种用来标记程序元素和提供编译器和框架额外信息的元数据。注解以 @ 符号开头,可以加在类、方法、变量等各种程序元素上,它们能够为程序的开发、维护、测试和部署等各个环节提供很多便利。注解的作用不限于提供说明信息,它还可以通过反射机制在程序运行时动态地检查和操作程序元素。


因此,注解和注释的主要区别在于:注释只是为了代码的可读性和辅助程序员理解,没有实际的功能;而注解则具有明确的语义和作用,并能够为程序的开发和维护提供各种服务。


自定义注解怎么用

自定义注解需要使用@interface定义,其内容可以由编程人员自行定义。下面是一个简单的自定义注解的例子:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

该注解被定义为@Log,可以用于方法上,它有一个可选的value属性供注解使用者添加一些描述信息。


注意,以上代码是不能直接运行的,因为还涉及到aop切面和反射的知识,完整示例程序看文末。


自定义注解在实际开发中可以有很多用途,下面举几个例子:


@ParamName注解

在Java中,方法的参数没有名称,只能通过索引来访问。有时候代码可读性会因为这个问题而受到影响,因此我们可以用自定义注解来为方法的参数添加名称:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ParamName {
    String value();
}

使用方式:

public void foo(@ParamName("param1") int param1, @ParamName("param2") double param2) {
    // do something
}

这样,在运行时我们就可以通过反射机制获取到每个参数的名称。

@Cacheable注解

在实际开发中,我们往往需要对一些计算量较大或者耗时较长的方法进行缓存,用自定义注解可以非常方便地实现这个功能:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
    String value();
}

然后我们可以用反射机制获取到被@Cacheable注解的方法,对其返回值做缓存。

@Encrypt注解

在实际开发中,我们可能需要对某些敏感数据进行加密,而不是直接存储明文。用自定义注解可以为某些变量添加自动加密的功能,例如:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Encrypt {
    boolean enable() default true;
}

然后我们可以通过反射机制获取到被@Encrypt注解的变量,如果该注解的enable属性为true,就对该变量进行加密操作。


SpringBoot中的常见注解

@SpringBootApplication:该注解为Spring Boot的入口注解,整合了@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解。


@RestController:声明一个控制器类,并且该类中的所有方法都以JSON的形式返回。


@RequestBody:该注解用于接收请求体的数据,并将其转换为Java对象。


@RequestMapping:定义访问路由,可以设置请求方法、参数、请求头等。


@PathVariable:获取RESTful接口中的路径参数。


@RequestParam:获取请求参数。


@Autowired:该注解用于自动注入依赖,可以配合@Qualifier注解进行精确匹配。


@Value:该注解用于获取配置文件中的属性值。


@ConfigurationProperties:该注解用于将配置文件中的属性值注入到Java Bean中。


@EnableAutoConfiguration:该注解自动配置Spring应用程序,简化了Spring应用程序的配置。


@ConditionalOnProperty:该注解用于控制某个配置项是否启用,可以设置默认值、匹配规则等。


@EnableAsync:该注解启用异步调用。


@Async:该注解将标记的方法异步执行。


@Scheduled:该注解用于定时任务。


我们日常开发会怎么用到自定义注解

校验参数

我们可以通过自定义注解来对控制器方法的参数做参数校验,例如:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidParam {
    String value();
}
public void update(@ValidParam("id") long id, @ValidParam("name") String name) {
    // do something
}

然后在方法执行前,我们可以根据@ValidParam注解的值对参数进行校验。


日志输出

我们可以通过自定义注解来标注一些需要进行日志输出的方法,例如:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    String value();
}
@Log("更新订单状态")
public void updateOrderStatus() {
    // do something
}

然后在方法执行时,我们可以根据@Log注解的值来输出相应的日志。


缓存管理

我们可以通过自定义注解来标注一些需要进行缓存的方法,例如:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cache {
    long expire() default 3600; // 缓存失效时间,默认为3600秒
    String key() default ""; // 缓存key
}
@Cache(expire = 1800, key = "user_{#id}")
public User getUserById(long id) {
    // do something
}

然后在方法执行前,我们可以根据@Cache注解的值来判断是否从缓存中获取数据。

Spring AOP

我们可以通过自定义注解来搭配Spring AOP完成一些需求,例如:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckLogin {
    boolean required() default true; // 该接口是否需要登录
}
@CheckLogin
@RequestMapping("/getUserInfo")
public UserInfo getUserInfo() {
    // do something
}

然后在AOP切面中,我们可以通过@CheckLogin注解的值来判断是否需要进行登录验证。

完整实例代码

以第一个Log注解为例,让我们来看看怎么把程序跑起来。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

用idea创建一个maven工程,打开pom.xml,添加spring的aop模块。除此之外,还需要引入Spring IoC容器和AspectJ依赖。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.9</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.9</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

Log注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Log {
    String value();
}

Log切面

@Aspect
public class LogAspect {
    @Around("@annotation(com.zhujie.Log)")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log logAnnotation = method.getAnnotation(Log.class);
        if (logAnnotation != null) {
            String value = logAnnotation.value();
            System.out.println("Log: " + value);
        }
        return joinPoint.proceed();
    }
}

@Around

@Around("@annotation(com.zhujie.Log)")

这个@Around注解中的参数是切点表达式,用来匹配需要被切入的方法。其中"@annotation(com.example.Log)"这部分是一个注解切点,表示需要匹配所有被@Log注解修饰的方法。


具体来说,@Around表示在方法执行前后都执行一段代码,包裹着原本要执行的方法。在执行这段代码时,可以获取到方法和参数的信息,对其进行处理或者记录日志等操作。


@annotation表示对注解的切面,后面跟着注解的类型,比如@annotation(com.zhujie.Log)就是表示对@Log注解的切面。这个切面就是指定了需要拦截所有被@Log注解修饰的方法,用来完成相应的操作。


UserService


@Service
class UserService {
    @Log("用户新增")
    public void add() {
    }
}

给add方法加上了@Log注解,只要add方法一执行,就会打印用户新增。

启动类:

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class AppConfig {
    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }
    public static void main(String[] args) {
        // 使用Spring上下文来管理Bean
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        // 获取LogService Bean
        UserService userService = context.getBean(UserService.class);
        // 调用含有@Log注解的方法
        userService.add();
    }
}

@ComponentScan注解是用来扫描指定包及其子包下的所有组件,自动将其注入到容器中。但是,对于一些比较特殊的组件,如AOP切面等,需要手动在配置类中将其注入到容器中。


在使用AOP时,有两种方式:一种是使用XML配置来创建切面,另一种是使用注解方式来创建切面。使用注解方式创建切面时,需要在配置类中使用@Bean注解来创建切面实例,并使用@EnableAspectJAutoProxy注解来开启AOP代理支持。


运行AppConfig,效果如预期。


0.png


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
16天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
51 7
|
2月前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
98 43
Java学习十六—掌握注解:让编程更简单
|
21天前
|
Java 编译器 数据库
Java 中的注解(Annotations):代码中的 “元数据” 魔法
Java注解是代码中的“元数据”标签,不直接参与业务逻辑,但在编译或运行时提供重要信息。本文介绍了注解的基础语法、内置注解的应用场景,以及如何自定义注解和结合AOP技术实现方法执行日志记录,展示了注解在提升代码质量、简化开发流程和增强程序功能方面的强大作用。
59 5
|
1月前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
57 14
|
1月前
|
前端开发 Java
[Java]讲解@CallerSensitive注解
本文介绍了 `@CallerSensitive` 注解及其作用,通过 `Reflection.getCallerClass()` 方法返回调用方的 Class 对象。文章还详细解释了如何通过配置 VM Options 使自定义类被启动类加载器加载,以识别该注解。涉及的 VM Options 包括 `-Xbootclasspath`、`-Xbootclasspath/a` 和 `-Xbootclasspath/p`。最后,推荐了几篇关于 ClassLoader 的详细文章,供读者进一步学习。
36 12
|
1月前
|
Java 编译器
Java进阶之标准注解
Java进阶之标准注解
32 0
|
2月前
|
JSON Java 数据库
java 常用注解大全、注解笔记
关于Java常用注解的大全和笔记,涵盖了实体类、JSON处理、HTTP请求映射等多个方面的注解使用。
43 0
java 常用注解大全、注解笔记
|
3月前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
2月前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
19 0
|
3月前
|
Java 编译器 程序员
Java注解,元注解,自定义注解的使用
本文讲解了Java中注解的概念和作用,包括基本注解的用法(@Override, @Deprecated, @SuppressWarnings, @SafeVarargs, @FunctionalInterface),Java提供的元注解(@Retention, @Target, @Documented, @Inherited),以及如何自定义注解并通过反射获取注解信息。
Java注解,元注解,自定义注解的使用
下一篇
DataWorks