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日志并进行多维度分析。
相关文章
|
26天前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
86 43
Java学习十六—掌握注解:让编程更简单
|
20天前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
43 14
|
20天前
|
前端开发 Java
[Java]讲解@CallerSensitive注解
本文介绍了 `@CallerSensitive` 注解及其作用,通过 `Reflection.getCallerClass()` 方法返回调用方的 Class 对象。文章还详细解释了如何通过配置 VM Options 使自定义类被启动类加载器加载,以识别该注解。涉及的 VM Options 包括 `-Xbootclasspath`、`-Xbootclasspath/a` 和 `-Xbootclasspath/p`。最后,推荐了几篇关于 ClassLoader 的详细文章,供读者进一步学习。
29 12
|
14天前
|
Java 编译器
Java进阶之标准注解
Java进阶之标准注解
27 0
|
1月前
|
JSON Java 数据库
java 常用注解大全、注解笔记
关于Java常用注解的大全和笔记,涵盖了实体类、JSON处理、HTTP请求映射等多个方面的注解使用。
34 0
java 常用注解大全、注解笔记
|
2月前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
1月前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
16 0
|
2月前
|
Java 编译器 程序员
Java注解,元注解,自定义注解的使用
本文讲解了Java中注解的概念和作用,包括基本注解的用法(@Override, @Deprecated, @SuppressWarnings, @SafeVarargs, @FunctionalInterface),Java提供的元注解(@Retention, @Target, @Documented, @Inherited),以及如何自定义注解并通过反射获取注解信息。
Java注解,元注解,自定义注解的使用
|
1月前
|
XML Java 数据格式
Java-spring注解的作用
Java-spring注解的作用
22 0
|
2月前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit