@Target(ElementType.ANNOTATION_TYPE)的工作原理

时间:2013-02-26 22:59:54

标签: java annotations

Java注释标有@Target注释,用于声明可以通过该注释进行修饰的可能连接点。 TYPE枚举的FIELDMETHODElementType等值清晰且易懂。

问题

为什么使用@Target(ANNOTATION_TYPE)值?带注释的注释有哪些优点?他们的贡献是什么?给我一个解释一个想法如何工作以及为什么我应该使用它。一些已经存在且众所周知的使用例子也很棒。

5 个答案:

答案 0 :(得分:35)

您可以使用带注释的注释来创建元注释,例如在Spring中考虑@Transactional的这种用法:

/**
 * Shortcut and more descriptive "alias" for {@code @Transactional(propagation = Propagation.MANDATORY)}.
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(propagation = Propagation.MANDATORY)
public @interface RequiresExistingTransaction {
}

当您启用Spring来处理@Transactional注释时,它将查找带有@Transactional 任何元注释的类和方法(注释是用@Transactional注释。

无论如何,这只是一个如何使用带注释的注释的具体示例。我想这主要是像Spring这样的框架,使用它们是有意义的。

答案 1 :(得分:3)

@Target(ElementType.ANNOTATION_TYPE)注释的每个注释都称为Meta-annotation。这意味着,您可以定义自己的自定义注释,这些注释是多个注释的合并,组合成一个注释以创建composed annotations

来自Android世界的一个很好的例子是StringDef

  

表示带注释的String元素表示逻辑类型,并且其值应该是显式命名的常量之一。

@Retention(SOURCE)
@Target({ANNOTATION_TYPE})
public @interface StringDef {
    /** Defines the allowed constants for this element */
    String[] value() default {};
}
//--
@Retention(SOURCE)
@StringDef({POWER_SERVICE, WINDOW_SERVICE, LAYOUT_INFLATER_SERVICE}) 
public @interface ServicesName {}

public static final String POWER_SERVICE = "power";
public static final String WINDOW_SERVICE = "window";
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
//--
@StringDef({SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY})
@Retention(RetentionPolicy.SOURCE)
public @interface WeekDays {}

public static final String SUNDAY = "sunday";
public static final String MONDAY = "monday";
public static final String TUESDAY = "tuesday";
public static final String WEDNESDAY = "wednesday";
public static final String THURSDAY = "thursday";
public static final String FRIDAY = "friday";
public static final String SATURDAY = "saturday";

...
public abstract Object getSystemService(@ServicesName String serviceName);
public abstract Object getDayOfWeek(@WeekDays String weekDay);

代码检查员将以与@ServicesName相同的方式处理@WeekDays@StringDef。 因此,我们可以根据需要创建尽可能多的命名 StringDef并覆盖常量集。 @Target(ElementType.ANNOTATION_TYPE)它是一个允许扩展注释使用的工具。

答案 2 :(得分:1)

例如,如果注释看起来像

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeAnnotation {

    String description() default "This is example for class annotation";
}

编译器会在这种情况下抱怨

@SomeAnnotation
public class SomeClass {

    @SomeAnnotation    // here it's complaning
    public void someMethod(){}
}

如果你改变了

@Target(ElementType.TYPE) 

@Target({ElementType.METHOD, ElementType.TYPE}) 

它不会再抱怨了。

答案 3 :(得分:1)

  1. 为什么要使用@Target(ANNOTATION_TYPE)值?

简而言之:当需要将注释应用于另一个注释时。如果查看常见Java批注的源代码,通常会看到以下代码模式:

@Target(ANNOTATION_TYPE)
public @interface TheAnnotation
{
  ...
}

例如,

@Documented 
@Target({ ANNOTATION_TYPE }) 
@Retention(RUNTIME) 
public @interface Constraint { 
public Class<? extends ConstraintValidator<?, ?>>[] validatedBy(); 
}
  1. 带注释的注释有什么用处?

如果在另一个注释(定义)类中使用了该注释,则它们是很好的或更确切地说是必需的。

  1. 他们的贡献是什么?

它们使直接将注释应用于另一个注释成为可能,这与将注释应用于标准Java类或类方法等不同。

  1. 请给我解释一下它如何工作以及为什么要使用它的想法。

例如,如果您创建数据模型类并希望其检查数据有效性,则某些注释定义类中的@Target注释中可能需要具有该值。通过在类中添加注释,可以很容易地包括这些数据有效性检查:某些值不为null(@notNull),电子邮件有效(@ValidEmail),字段长度超过x个字符(@Size)等。有许多现成的批注可以进行数据有效性检查,但不一定用于所有目的,例如,如果您想通过添加批注@PasswordMatches来检查密码及其matchingPassword是否相同。通过创建注释类PasswordMatches可以实现:

@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordMatchesValidator.class)
@Documented
public @interface PasswordMatches {

    String message() default "Passwords don't match";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

 }

请注意有行

@Constraint(validatedBy = PasswordMatchesValidator.class)

即批注包含在此批注定义类中。

PasswordMatchesValidator类可能如下所示:

public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {

@Override
public void initialize(final PasswordMatches constraintAnnotation) {}

@Override
public boolean isValid(final Object obj, final ConstraintValidatorContext context) {
    final UserDto user = (UserDto) obj;
    return user.getPassword().equals(user.getMatchingPassword());
    }
}

现在只需添加批注@PasswordMatches,即可轻松地将密码相等性检查包括到数据模型类中:

@PasswordMatches 
public class UserDto {
... 
}

请注意,只有通过在类中添加注释@PasswordMatches才能进行密码检查,并且PasswordMatches注释类需要具有@Constraint(validatedBy = PasswordMatchesValidator.class)行。换句话说,注释用于注释另一个注释。这是可能的,因为@Constraint批注(定义)类的行为@Target({ANNOTATION_TYPE})。

注释注释的主要原因是可以在注释(定义)类中使用注释。尤其是,如果要使用注释自定义注释,则需要在自定义注释类中将ANNOTATION_TYPE包含在@Target注释值数组中。

  1. 一些已经存在并且众所周知的用法示例也是很好的。

第4项中有一个非常著名的示例,但是用于注释(其实现必须具有@Target(ANNOTATION_TYPE)行)的另一个已知注释是@ Retention,@ Documented和@Target本身。

答案 4 :(得分:-3)

注释基本上是与您的代码一起使用的附加元数据(信息)。它可以放在旁边类型(类,接口),方法和参数中。

在编译时和运行时通常很有用。许多流行的API(如Java EE 5 +,Spring,AspectJ)都利用注释来实现代码清晰度和一致性。

使用注释通常可以使代码更易读,更容易理解。

我建议您read through the annotation chapter on Java tutorial

过去,元数据通常以xml文件形式提供,如果有人必须查找不同的xml配置文件,则很难尝试理解代码。 latest Java servlet API allows mapping of servlet simply by using annotation -- as opposed of web.xml mapping

@WebServlet("/response")
public class ResponseServlet extends HttpServlet {
  // servlet code here...
}