java如何编写一个注解

简单演示

在Java中,注解(Annotation)是一种元数据(metadata)的形式,它用于为Java代码(类、方法、变量、参数和包等)提供信息。这些注解可以在编译时被处理,也可以在运行时被反射机制读取。

要编写一个Java注解,你需要使用@interface关键字,而不是通常的interface关键字。以下是一个简单的Java注解定义的例子:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 定义注解
@Retention(RetentionPolicy.RUNTIME) // 注解保留到运行时,以便通过反射读取
@Target({ElementType.METHOD, ElementType.TYPE}) // 注解可以应用于方法和类
public @interface MyAnnotation {
    
    // 定义一个名为value的元素,类型为String,默认值为""
    String value() default "";
    
    // 还可以定义其他元素,例如
    int number() default 0;
    
    // 也可以定义枚举类型的元素
    MyEnum type() default MyEnum.DEFAULT;
    
    // 枚举类型的定义
    enum MyEnum {
        DEFAULT,
        TYPE_A,
        TYPE_B
    }
}
在这个例子中,我们定义了一个名为MyAnnotation的注解,它有三个元素:value、number和type。这些元素都有默认值,所以在使用注解时可以省略它们。

注解的@Retention和@Target元注解用于指定注解的保留策略和可应用的目标。@Retention(RetentionPolicy.RUNTIME)表示这个注解在运行时仍然保留,这意味着你可以通过反射机制读取它。@Target用于指定这个注解可以应用于哪些Java元素,例如方法、类、字段等。在这个例子中,MyAnnotation可以应用于方法和类。

使用注解的示例:

@MyAnnotation(value = "example", number = 10, type = MyAnnotation.MyEnum.TYPE_A)
public class MyClass {
    
    @MyAnnotation(value = "anotherExample")
    public void myMethod() {
        // 方法体
    }
}

常用元注解

在Java中,编写自定义注解时,通常需要使用到几个基本的元注解(Meta-Annotation),这些元注解用于为自定义注解提供额外的信息或指定其行为。以下是这些元注解的详细解释:

@Target

说明注解可以被用在哪些元素上,例如类、方法、字段等。
取值:

  • ElementType.TYPE(类、接口(包括注解类型)或enum声明)、
  • ElementType.FIELD(字段,包括enum实例)、
  • ElementType.METHOD(方法)、
  • ElementType.PARAMETER(参数)、
  • ElementType.CONSTRUCTOR(构造器)、
  • ElementType.LOCAL_VARIABLE(局部变量)、
  • ElementType.ANNOTATION_TYPE(另一个注解)、
  • ElementType.PACKAGE(包)、
  • ElementType.TYPE_PARAMETER(类型参数)、
  • ElementType.TYPE_USE(类型使用声明)。

示例:

@Target(ElementType.METHOD)
public @interface MyAnnotation {
    // ...
}

@Retention

定义了注解的生命周期,即注解在何时生效。

取值:RetentionPolicy.SOURCE(注解只保留在源码中,在编译时会被丢弃,不包含在编译后的class文件中)、RetentionPolicy.CLASS(注解被保留在class文件中,但JVM在运行时忽略,这是默认的保留策略)、RetentionPolicy.RUNTIME(注解被保留在class文件中,并且可以被JVM在运行时读取)。
示例:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // ...
}

@Documented

表示该注解会被javadoc和类似的工具记录,因此可以通过javadoc命令生成API文档时包含此注解。

示例:

@Documented
public @interface MyAnnotation {
    // ...
}

@Inherited

表示如果一个注解类型被声明为@Inherited,那么它将被用于类声明时,这个注解类型将被自动地继承到子类中去。

示例:

@Inherited
public @interface MyAnnotation {
    // ...
}

@Repeatable(Java 8引入)

  • 表示该注解类型可以多次应用于同一个声明类型上。
  • 需要定义一个容器注解(Containing Annotation)来存放多个相同的注解实例。

示例:

@interface MyAnnotations {
    MyAnnotation[] value();
}

@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {
    String value() default "";
}

// 使用时
@MyAnnotation("annotation1")
@MyAnnotation("annotation2")
public class MyClass {
    // ...
}

// 或者使用容器注解
@MyAnnotations({
    @MyAnnotation("annotation1"),
    @MyAnnotation("annotation2")
})
public class MyClass {
    // ...
}

注意:在编写自定义注解时,并不是所有元注解都是必需的,通常你需要根据你的需求来决定使用哪些元注解。例如,如果你只是想在编译时检查某些代码,那么可能只需要@Target和@Retention(RetentionPolicy.CLASS)就足够了。如果你希望注解在运行时能够被读取,那么就需要@Retention(RetentionPolicy.RUNTIME)。

高级示例代码

在Java中,通过自定义注解和AspectJ或Spring
AOP实现AOP(面向切面编程)是一种常见的做法。下面我将给出一个基于Spring AOP的示例代码,并解释说明每一步的操作。

1. 定义自定义注解

首先,我们需要定义一个自定义注解,比如@MyAnnotation。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
    // 可以定义一些属性,例如value、description等
    String value() default "";
}

2. 编写切面类

然后,我们编写一个切面类,并使用@Aspect和@Component(如果集成在Spring中)注解。

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {

    @Before("@annotation(myAnnotation)")
    public void beforeAdvice(JoinPoint joinPoint, MyAnnotation myAnnotation) {
        System.out.println("Method " + joinPoint.getSignature().getName() + " is about to execute with annotation value: " + myAnnotation.value());
    }
}

在上面的代码中,@Before注解标识了一个前置通知,当目标方法被调用之前,会执行这个通知。@annotation(myAnnotation)是一个切点表达式,表示要拦截所有带有@MyAnnotation注解的方法。

3. 在目标类上使用注解

现在,我们可以在目标类的方法上使用我们定义的注解。

@Component
public class MyService {

    @MyAnnotation(value = "Test Annotation")
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

4. 配置Spring AOP

在Spring项目中,需要启用AOP支持。这通常通过在配置类上使用@EnableAspectJAutoProxy注解来实现。

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = "com.example.aop") // 替换为你的包名
@EnableAspectJAutoProxy
public class AppConfig {
    // ... 其他bean定义 ...
}

5. 运行和测试

现在,当你运行你的Spring应用程序并调用MyService的doSomething方法时,你应该会在控制台看到MyAspect的beforeAdvice方法打印出的日志信息。

6.解释说明

  • @MyAnnotation 是一个自定义注解,用于标记需要特殊处理的方法。

  • @Aspect 标记了一个类作为切面类,它包含了通知(Advice)和切点(Pointcut)的定义。

  • @Before 是一个通知注解,表示在方法执行之前执行某个操作。

  • @annotation(myAnnotation) 是一个切点表达式,它指定了要拦截的方法必须带有@MyAnnotation注解。

在切面类中,我们定义了一个前置通知方法beforeAdvice,当满足切点条件时,该方法会被执行。这个方法接收两个参数:一个是JoinPoint对象,用于访问被拦截方法的元数据;另一个是MyAnnotation对象,它是被拦截方法上的注解实例。

在目标类MyService中,我们在doSomething方法上使用了@MyAnnotation注解,并指定了一个值。

最后,我们需要在Spring配置中启用AOP支持,以便Spring容器能够扫描和注册切面类和通知。这通常通过@EnableAspectJAutoProxy注解来实现。

  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是一个比较全的Java日志注解的示例代码: ```java import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) // 该注解可以用于类和方法上 @Retention(RetentionPolicy.RUNTIME) // 该注解在运行时保留 @Documented // 该注解将被包含在javadoc中 public @interface Log { String value() default ""; // 日志信息 LogLevel level() default LogLevel.INFO; // 日志级别,默认为INFO boolean printParams() default true; // 是否打印方法参数,默认为true boolean printResult() default true; // 是否打印方法返回值,默认为true boolean printTime() default true; // 是否打印方法执行时间,默认为true boolean printThread() default true; // 是否打印线程信息,默认为true boolean printException() default true; // 是否打印异常信息,默认为true } // 日志级别枚举类 enum LogLevel { DEBUG, INFO, WARN, ERROR } ``` 在上述代码中,我们定义了一个名为Log的注解,它可以用于类和方法上,并且在运行时保留。该注解包含了一些属性,用来指定日志级别、是否打印方法参数、是否打印方法返回值、是否打印方法执行时间、是否打印线程信息以及是否打印异常信息。下面是一个使用该注解的示例: ```java public class Demo { @Log(value = "执行了方法A", level = LogLevel.INFO, printParams = true, printResult = true) public void methodA(int a, int b) { // 方法A的实现 } } ``` 在上述示例中,我们在方法methodA上使用了Log注解,并指定了一些属性。接下来,我们可以根据注解上的属性来打印日志: ```java import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Method; public class LogUtil { private static final Logger LOGGER = LoggerFactory.getLogger(LogUtil.class); // 使用SLF4J记录日志 public static void log(Method method, Object[] args, Object result, Throwable e) { Log log = method.getAnnotation(Log.class); if (log != null) { LogLevel level = log.level(); String message = log.value(); long startTime = System.currentTimeMillis(); if (level == LogLevel.DEBUG) { LOGGER.debug(getLogMessage(method, args, result, e, message, startTime)); } else if (level == LogLevel.WARN) { LOGGER.warn(getLogMessage(method, args, result, e, message, startTime)); } else if (level == LogLevel.ERROR) { LOGGER.error(getLogMessage(method, args, result, e, message, startTime)); } else { LOGGER.info(getLogMessage(method, args, result, e, message, startTime)); } } } private static String getLogMessage(Method method, Object[] args, Object result, Throwable e, String message, long startTime) { StringBuilder sb = new StringBuilder(); sb.append(message).append(" 开始执行..."); if (args != null && args.length > 0) { sb.append("方法参数:["); for (int i = 0; i < args.length; i++) { sb.append(args[i]); if (i < args.length - 1) { sb.append(", "); } } sb.append("]"); } if (result != null) { sb.append("方法返回值:").append(result); } if (e != null) { sb.append("方法执行出现异常:").append(e); } if (log.printTime()) { long endTime = System.currentTimeMillis(); sb.append("方法执行时间:").append(endTime - startTime).append("ms"); } if (log.printThread()) { sb.append("方法执行线程:").append(Thread.currentThread().getName()); } return sb.toString(); } } ``` 在上述代码中,我们定义了一个名为LogUtil的工具类,其中log方法接受一个Method类型的参数,表示需要打印日志的方法。我们首先通过getAnnotation方法获取该方法上的Log注解,如果存在,则根据注解上的属性来打印日志信息。在打印日志信息时,我们使用了SLF4J记录日志。getLogMessage方法用于构造日志信息字符串,它将方法参数、方法返回值、异常信息、方法执行时间和线程信息等信息拼接在一起。 最后,我们可以在调用需要打印日志的方法时,使用LogUtil工具类来实现日志打印: ```java public class Main { public static void main(String[] args) { Demo demo = new Demo(); Method method = Demo.class.getMethod("methodA", int.class, int.class); Object[] params = {1, 2}; Object result = null; try { result = method.invoke(demo, params); } catch (Exception e) { e.printStackTrace(); } LogUtil.log(method, params, result, null); } } ``` 在上述代码中,我们首先创建了一个Demo对象,然后通过反射获取methodA方法,并将其传入LogUtil.log方法中。在调用methodA方法时,我们需要传入参数,并将返回值保存。最后,我们调用LogUtil.log方法,并将method、params、result和异常信息传入。LogUtil.log方法会根据注解上的属性来打印日志信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个双鱼座的测开

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值