java中自定义注解

自定义注解

Java允许开发者在代码中定义自己的注解类型,用于标记和描述程序中的元素。自定义注解可以用于类、方法、字段等各种元素上,通过注解处理器可以在编译时或运行时对注解进行解析和处理。

代码示例1

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) // 指定注解的作用目标为方法
@Documented
public @interface MyAnnotation {
    String value() default ""; // 定义一个成员变量,默认值为空字符串
    int count() default 0; // 定义一个成员变量,默认值为0
}

定义名为MyAnnotation的自定义注解。注解使用@interface关键字进行定义。注解的保留策略通过@Retention注解指定,RetentionPolicy.RUNTIME,表示注解在运行时仍然可用。注解的作用目标通过@Target注解指定ElementType.METHOD,表示注解可以用于方法上。

注解中可以定义成员变量,成员变量的定义方式与接口类似。定义了两个成员变量valuecount,并分别指定了默认值为空字符串和0。

使用自定义注解时,可以在目标元素上使用注解,并为成员变量赋值。例如:

java 复制代码
public class MyClass {
    @MyAnnotation(value = "Hello", count = 5)
    public void myMethod() {
        // 方法体
    }
}

通过反射机制,可以在运行时获取注解的信息,并根据注解进行相应的处理。例如:

java 复制代码
public class AnnotationProcessor {
    public static void processAnnotations(Class<?> clazz) {
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(MyAnnotation.class)) {
                MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
                String value = annotation.value();
                int count = annotation.count();
                // 根据注解进行相应的处理
                System.out.println("Method: " + method.getName() + ", value: " + value + ", count: " + count);
            }
        }
    }
}

定义了AnnotationProcessor类,其中processAnnotations方法用于处理带有@MyAnnotation注解的方法。通过isAnnotationPresent方法判断方法是否带有指定的注解,然后通过getAnnotation方法获取注解的实例,进而获取注解的成员变量的值。

代码示例2

注解中的属性是数组类型,并且给出了默认值:

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)
public @interface MyAnnotation {
    String[] value() default {"default"};
}

定义了一个名为 MyAnnotation 的注解,有一个名为 value 的属性,类型为字符串数组。还给出了一个默认值为 {"default"}

可以在方法上使用这个注解,并为 value 属性提供自定义值,如下所示:

java 复制代码
public class MyClass {
    @MyAnnotation(value = {"value1", "value2"})
    public void myMethod() {
        // do something
    }
}

在上面的代码中,在 myMethod 方法上使用了 MyAnnotation 注解,并为 value 属性提供了自定义值 {"value1", "value2"}

注解的保留策略和作用目标

保留策略参数:

Java中的注解有三种保留策略参数:

java 复制代码
1. SOURCE:注解只保留在源代码中,编译时会被忽略。

2. CLASS:注解会被编译器保留在class文件中,但在运行时不会被JVM保留。

3. RUNTIME:注解会被编译器保留在class文件中,并在运行时被JVM保留,因此可以通过反射机制读取注解信息。

作用目标参数:

Java中的注解有多种作用目标参数,包括:

java 复制代码
1. TYPE:用于类、接口、枚举等类型的声明。

2. FIELD:用于字段的声明。

3. METHOD:用于方法的声明。

4. PARAMETER:用于方法的参数声明。

5. CONSTRUCTOR:用于构造函数的声明。

6. LOCAL_VARIABLE:用于局部变量的声明。

7. ANNOTATION_TYPE:用于注解类型的声明。

8. PACKAGE:用于包的声明。

Documented注解的作用:

java 复制代码
Documented注解是一个标记注解,用于指示被注解的元素应该包含在JavaDoc文档中。当一个注解被标记为Documented时,它将被包含在JavaDoc文档中,以便用户可以查看该注解的文档。

AliasFor注解

AliasFor注解是Spring框架中的一个注解,它用于指定注解属性之间的别名关系。在使用AliasFor注解时,需要指定别名关系的两个属性,其中一个属性作为主属性,另一个属性作为别名属性。当别名属性被设置时,主属性也会被自动设置,反之亦然。

AliasFor注解的使用场景主要有以下两种:

1. 用于注解属性之间的别名关系

在这种场景下,AliasFor注解用于指定注解属性之间的别名关系,以便在使用注解时可以使用别名属性来设置主属性。

java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation{
  
      @AliasFor("locations")
      String value() default "";
  
      @AliasFor("value")
      String locations() default "";
}

@MyAnnotation(value= "test")
public class MyClass {
    // ...
}

MyAnnotation注解,该注解含有value和locations两个属性,分别为value设置别名locations,locations设置别名value。在使用@MyAnnotation注解时无论是为value设置值,还是为locations设置值达到的效果相同。

使用限制:

java 复制代码
 - 别名都是成对出现,为value属性设置locations别名,需要含有locations属性并为其设置value别名;
 - 成对出现的属性返回值相同;
 - 必须为成对出现的属性必须设置默认值;
 - 成对出现的属性的默认值必须相同;
 - AliasFor注解不能设置annotation属性;
 - 用于注解之间的别名关系

2. 用于注解之间的别名关系

在这种场景下,AliasFor注解用于指定不同注解之间的别名关系,以便在使用这些注解时可以使用别名注解来替代主注解。

java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation1 {
    String value() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@MyAnnotation1
public @interface MyAnnotation2 {
    @AliasFor(annotation = MyAnnotation1.class, attribute = "value")
    String name() default "";
}

@MyAnnotation2(name = "test")
public class MyClass {
    // ...
}

MyAnnotation2注解中的name属性和MyAnnotation1注解中的value属性之间存在别名关系。因此,在使用MyAnnotation2注解时,可以使用name属性来替代MyAnnotation1注解中的value属性,如上面的示例所示。

案例

要求:写一个sdk,提供一个color注解,能作用在类或方法上,当color的值为red时,进行拦截,返回500错误

定义注解

java 复制代码
@Documented
@Target({ TYPE, METHOD})
@Retention(RUNTIME)
public @interface ColorAnnotation {

    @AliasFor("color")
    String value() default "green";

    @AliasFor("value")
    String [] color() default "green";
}

定义拦截器

java 复制代码
@Slf4j

public class ColorInterceptor implements HandlerInterceptor {

    //preHandle:在处理请求之前被调用,可以进行一些前置处理操作。如果该方法返回false,则请求将被终止,后续的拦截器和处理器将不会被执行。
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("MyInterceptor preHandle");
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            ColorAnnotation methodAnnotation = handlerMethod.getMethodAnnotation(ColorAnnotation.class);
            // 可以拿注解上的值 当color为red时进行拦截
            if (methodAnnotation != null) {
                String color = methodAnnotation.value();
                if (color.equals("red")){
                    PrintWriter writer = response.getWriter();
                    response.setHeader("content-type", "text/html;charset=UTF-8");
                    writer.write("<html><head><body>" + "{\"code\":500,\"message\":\"color is red,please change " +
                                     "it.\"}" + "</body></html>");
                    return false;
                }
            }
            return true;
        }
        return true;
    }

    //在处理请求之后、渲染视图之前被调用,可以进行一些后置处理操作。可以通过该方法修改ModelAndView对象
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("MyInterceptor postHandle");
    }

    //在整个请求完成之后被调用,可以进行一些资源清理操作
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("MyInterceptor afterCompletion");
    }

}

拦截器注册到spring容器

java 复制代码
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ColorInterceptor())
            .addPathPatterns("/**")
            .excludePathPatterns("/static/**");;
    }
}
java 复制代码
@Configuration
public class ColorConfiguration {

    @Bean
    public WebMvcConfig webMvcConfig(){
        return new WebMvcConfig();
    }
}

注册到spring.factories文件中

java 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.luxifa.config.ColorConfiguration
相关推荐
wainyz6 分钟前
Java NIO操作
java·开发语言·nio
工业3D_大熊11 分钟前
【虚拟仿真】CEETRON SDK在船舶流体与结构仿真中的应用解读
java·python·科技·信息可视化·c#·制造·虚拟现实
喵叔哟15 分钟前
重构代码之用委托替代继承
开发语言·重构
lzb_kkk20 分钟前
【JavaEE】JUC的常见类
java·开发语言·java-ee
SEEONTIME20 分钟前
python-24-一篇文章彻底掌握Python HTTP库Requests
开发语言·python·http·http库requests
起名字真南39 分钟前
【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案
开发语言·c++·leetcode
爬山算法44 分钟前
Maven(28)如何使用Maven进行依赖解析?
java·maven
tyler_download1 小时前
golang 实现比特币内核:实现基于椭圆曲线的数字签名和验证
开发语言·数据库·golang
小小小~1 小时前
qt5将程序打包并使用
开发语言·qt
hlsd#1 小时前
go mod 依赖管理
开发语言·后端·golang