Springboot 注解类里面public @interface xxx 什么意思

本文深入解析Java注解的使用和实现机制,包括@interface的定义、元注解的用途、注解的目标类型及如何通过反射读取注解信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@interface 不是interface,是注解类  定义注解
是jdk1.5之后加入的,java没有给它新的关键字,所以就用@interface 这么个东西表示了 
这个注解类,就是定义一个可用的注解,包括这个注解用于什么地方,是类,还是方法,还是property,还是方法入参等等

@Inherited       //这个Annotation 可以被继承  

@Documented  //这个Annotation可以被写入javadoc  

@Retention(RetentionPolicy.RUNTIME)

注解@Retention可以用来修饰注解,是注解的注解,称为元注解。

public enum RetentionPolicy {   
    SOURCE, // 编译器处理完Annotation后不存储在class中   
    CLASS, // 编译器把Annotation存储在class中,这是默认值   
    RUNTIME // 编译器把Annotation存储在class中,可以由虚拟机读取,反射需要   
} 

@Target:注解的作用目标

@Target(ElementType.TYPE)   //接口、类、枚举、注解
        @Target(ElementType.FIELD) //字段、枚举的常量
        @Target(ElementType.METHOD) //方法
        @Target(ElementType.PARAMETER) //方法参数
        @Target(ElementType.CONSTRUCTOR)  //构造函数
        @Target(ElementType.LOCAL_VARIABLE)//局部变量
        @Target(ElementType.ANNOTATION_TYPE)//注解
        @Target(ElementType.PACKAGE) ///包   

ElementType源码:

public enum ElementType {   
    TYPE, // 指定适用点为 class, interface, enum   
    FIELD, // 指定适用点为 field   
    METHOD, // 指定适用点为 method   
    PARAMETER, // 指定适用点为 method 的 parameter   
    CONSTRUCTOR, // 指定适用点为 constructor   
    LOCAL_VARIABLE, // 指定使用点为 局部变量   
    ANNOTATION_TYPE, //指定适用点为 annotation 类型   
    PACKAGE // 指定适用点为 package   
}  
 

 Annotation的一般形式是 :

public @interface MyAnnotation {  
    String value() default "hahaha";  
} 

其实等同于

public class MyAnnotation extends java.lang.annotation.Annotation{   
     private String value = "hahaha";   
     public void setValue(String value){   
          this.value = value;   
     }   
     public String getValue(){   
          return value;   
      }   
} 
@MyAnnotation(value="hello")     //对应Bean的set方法   
Method method = AnnotationTest.class.getMethod("doSomthing", null);   //取得被注释的方法,AnnotationTest.class为该方法所在的类   
MyRetention mr = method.getAnnotation(MyRetention.class); //取得注释对象   
String value = mr.value();    //取得value的值,对应Bean的get方法. 

  java.lang.annotation.Retention告诉编译器如何对待 Annotation,使用Retention时,需要提供java.lang.annotation.RetentionPolicy的 枚举值.

package com.self;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
 
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTarget
{ }
定义个一注解@MyTarget,用RetentionPolicy.RUNTIME修饰;
package com.self;
import java.lang.reflect.Method;
public class MyTargetTest
{
 @MyTarget
 public void doSomething()
 {
  System.out.println("hello world");
 }
 
 public static void main(String[] args) throws Exception
 {
  Method method = MyTargetTest.class.getMethod("doSomething",null);
  if(method.isAnnotationPresent(MyTarget.class))//如果doSomething方法上存在注解@MyTarget,则为true
  {
   System.out.println(method.getAnnotation(MyTarget.class));
  }
  }
}
上面程序打印:@com.self.MyTarget(),如果RetentionPolicy值不为RUNTIME,则不打印。
 
 @Retention(RetentionPolicy.SOURCE )
public @interface Override
 
@Retention(RetentionPolicy.SOURCE )
public @interface SuppressWarnings
 
@Retention(RetentionPolicy.RUNTIME )
public @interface Deprecated
由上可以看出,只有注解@Deprecated在运行时可以被JVM读取到
 
注解中可以定义属性,看例子:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation
{
 String hello() default "gege";
  String world();
  int[] array() default { 2, 4, 5, 6 };
  EnumTest.TrafficLamp lamp() ;
  TestAnnotation lannotation() default @TestAnnotation(value = "ddd");
  Class style() default String.class;
}
上面程序中,定义一个注解@MyAnnotation,定义了6个属性,他们的名字为:
hello,world,array,lamp,lannotation,style.
属性hello类型为String,默认值为gege
属性world类型为String,没有默认值
属性array类型为数组,默认值为2,4,5,6
属性lamp类型为一个枚举,没有默认值
属性lannotation类型为注解,默认值为@TestAnnotation,注解里的属性是注解
属性style类型为Class,默认值为String类型的Class类型
 
看下面例子:定义了一个MyTest类,用注解@MyAnnotation修饰,注解@MyAnnotation定义的属性都赋了值
@MyAnnotation(hello = "beijing", world="shanghai",array={},lamp=TrafficLamp.RED,style=int.class)
public class MyTest
{
 @MyAnnotation(lannotation=@TestAnnotation(value="baby"), world = "shanghai",array={1,2,3},lamp=TrafficLamp.YELLOW)
 @Deprecated
 @SuppressWarnings("")
 public void output()
 {
  System.out.println("output something!");
 }
}
 接着通过反射读取注解的信息:
public class MyReflection
{
 public static void main(String[] args) throws Exception
 {
  MyTest myTest = new MyTest();
    Class<MyTest> c = MyTest.class;
    Method method = c.getMethod("output", new Class[] {});
       //如果MyTest类名上有注解@MyAnnotation修饰,则为true
  if(MyTest.class.isAnnotationPresent(MyAnnotation.class))
  {
   System.out.println("have annotation");
  }
   if (method.isAnnotationPresent(MyAnnotation.class))
   {
   method.invoke(myTest, null); //调用output方法
   //获取方法上注解@MyAnnotation的信息
     MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
    String hello = myAnnotation.hello();
   String world = myAnnotation.world();
   System.out.println(hello + ", " + world);//打印属性hello和world的值
   System.out.println(myAnnotation.array().length);//打印属性array数组的长度
   System.out.println(myAnnotation.lannotation().value()); //打印属性lannotation的值
   System.out.println(myAnnotation.style());
   }
    //得到output方法上的所有注解,当然是被RetentionPolicy.RUNTIME修饰的
     Annotation[] annotations = method.getAnnotations();
      for (Annotation annotation : annotations)
  {
   System.out.println(annotation.annotationType().getName());
  }
   }
}
上面程序打印:
have annotation
output something!
gege, shanghai
3
baby
class java.lang.String
com.heima.annotation.MyAnnotation
java.lang.Deprecated
如果注解中有一个属性名字叫value,则在应用时可以省略属性名字不写。
可见,@Retention(RetentionPolicy.RUNTIME )注解中,RetentionPolicy.RUNTIME是注解属性值,属性名字是value,
属性的返回类型是RetentionPolicy,如下:
public @interface MyTarget
{
    String value();
}
可以这样用:
  @MyTarget("aaa")
 public void doSomething()
 {
  System.out.println("hello world");
 }
 
注解@Target也是用来修饰注解的元注解,它有一个属性ElementType也是枚举类型,
值为:ANNOTATION_TYPE CONSTRUCTOR  FIELD LOCAL_VARIABLE METHOD PACKAGE PARAMETER TYPE
如@Target(ElementType.METHOD) 修饰的注解表示该注解只能用来修饰在方法上。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTarget
{
 String value() default "hahaha";
}
如把@MyTarget修饰在类上,则程序报错,如:
@MyTarget
public class MyTargetTest

 

 

 

<think>嗯,用户问的是Spring Boot中自定义注解能不能用${xxx}表达式。首先,我得确认用户指的是在自定义注解的属性里使用类似Spring的属性占位符,比如@Value("${property}")那样的用法。对吧? 那问题来了,通常自定义注解的属性值在编译时就需要确定,而${}这种占位符是Spring在运行时解析的。所以,默认情况下,自定义注解的属性可能不支持直接使用${}表达式。但是有没有办法绕过这个限制呢? 记得Spring有个功能叫做@AliasFor,或者可能用Spring的Environment来手动解析属性。另外,可能需要通过后处理器或者AOP来处理注解,并在运行时动态替换属性值。比如,自定义一个BeanPostProcessor,在bean初始化后,通过反射读取注解中的属性,然后用Environment来解析里面的占位符。 不过用户可能想知道具体怎么实现,所以需要分步骤说明。比如,首先定义一个注解,然后如何处理这个注解中的属性值,可能涉及到获取Environment实例,替换${}为实际值。同时,还要注意属性值的类型,可能需要处理默认值的情况。 另外,可能还要考虑是否需要在注解属性中使用EL表达式,或者结合@Value注解一起使用。不过用户明确问的是自定义注解能否使用${}表达式,所以重点应该放在如何让自定义注解支持这种占位符的解析。 需要检查一下Spring的文档,或者相关的示例,确认是否有这样的实现方式。比如,可能通过实现BeanFactoryPostProcessor或者使用@PostConstruct来处理。或者,是否可以通过在注解的属性中使用字符串,然后在代码中手动调用Environment.resolvePlaceholders()方法。 举个例子,假设用户定义了一个@MyAnnotation,其中有一个属性value,用户想写成@MyAnnotation("${my.property}")。这时候,在Spring容器启动时,如何将这个${my.property}替换为实际的配置值。 可能需要通过AOP拦截带有该注解的类或方法,在运行时读取注解的属性,并用Environment来解析。或者,在Bean的初始化阶段,通过反射修改注解的属性值。 不过,这里可能会有一些限制,比如注解的属性在JDK中是编译时常量,无法在运行时动态改变。所以可能需要在每次访问注解属性的时候动态解析,而不是直接修改注解本身的值。 总结下来,可能的步骤是:1. 自定义注解;2. 在某个Spring组件(如BeanPostProcessor)中获取所有带有该注解的Bean;3. 使用反射读取注解的属性值;4. 使用Environment解析其中的占位符;5. 将解析后的值存储起来,供后续使用。 需要注意的是,这种方法可能有一定的性能开销,并且需要处理可能的异常情况,比如属性未找到时的默认值处理。同时,要确保Environment已经被正确初始化,以便能够解析占位符。 另外,可能还有其他方法,比如结合@Value和自定义注解,或者在注解的属性中使用SpEL表达式,但用户的问题明确是关于${xxx}表达式的支持,所以需要紧扣这一点。 最后,要给出一个具体的代码示例,让用户能够理解如何实现。比如,定义一个@MyAnnotation,然后编写一个BeanPostProcessor来处理它,使用Environment解析属性值,并可能将解析后的值设置到某个地方,比如Bean的属性中,或者缓存起来供后续逻辑使用。</think>在Spring Boot中,**自定义注解本身不支持直接使用`${xxx}`表达式**,但可以通过结合Spring的运行时处理机制间接实现类似效果。以下是分步骤说明: --- ### 1. 核心思路 `${xxx}`是Spring的属性占位符,通常与`@Value`注解配合使用。要让自定义注解支持类似功能,需: - 通过`Environment`对象手动解析占位符 - 利用Spring的后置处理器(如`BeanPostProcessor`)在Bean初始化时动态处理注解属性 --- ### 2. 实现步骤 #### (1) 定义自定义注解 ```java @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyCustomAnnotation { String value() default ""; // 假设需要支持${xxx}的属性 } ``` #### (2) 实现Bean后置处理器 通过`BeanPostProcessor`在Bean初始化后处理注解: ```java @Component public class MyAnnotationProcessor implements BeanPostProcessor { @Autowired private Environment environment; @Override public Object postProcessAfterInitialization(Object bean, String beanName) { // 检查Bean的类或方法是否带有自定义注解 MyCustomAnnotation annotation = AnnotationUtils.findAnnotation( bean.getClass(), MyCustomAnnotation.class); if (annotation != null) { // 解析注解中的占位符 String resolvedValue = environment.resolvePlaceholders(annotation.value()); // 将解析后的值存入上下文或进行其他操作 System.out.println("Resolved value: " + resolvedValue); } return bean; } } ``` --- ### 3. 使用示例 ```java @MyCustomAnnotation("${my.config.property}") // 使用占位符 @Service public class MyService { // ... } ``` 在`application.properties`中配置: ```properties my.config.property=HelloWorld ``` --- ### 4. 关键点说明 - **手动解析占位符**:通过`environment.resolvePlaceholders()`方法实现动态解析 - **作用范围**:可作用于类级别或方法级别(根据注解定义) - **扩展性**:可结合AOP实现更复杂的逻辑(如方法拦截时动态获取配置值) --- ### 5. 替代方案 若需更简洁的实现,可考虑以下方式: ```java @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { @Value("#{'${my.property}'}") // 结合SpEL表达式 String value(); } ``` 但需注意:这种方式需要确保`@Value`的解析时机与自定义注解的处理逻辑匹配。 --- ### 总结 通过结合Spring的`Environment`和`BeanPostProcessor`,可以实现自定义注解中间接支持`${xxx}`表达式,但需要显式处理占位符解析逻辑。这种方式比直接使用`@Value`更灵活,但代码复杂度会有所增加。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值