ホームページ  >  記事  >  Java  >  Java アノテーションの定義と使用の概要 (コード例)

Java アノテーションの定義と使用の概要 (コード例)

不言
不言転載
2019-02-21 14:41:002519ブラウズ

この記事では Java アノテーションの定義と使用法 (コード例) を紹介します。必要な方は参考にしていただければ幸いです。

Java アノテーションは、特に Spring を使用した後、実際のプロジェクトでよく使用されます。

この記事では、Java アノテーションの構文と Spring でのアノテーションの使用例を紹介します。

アノテーションの構文

アノテーションの例

例として Junit の @Test アノテーションを取り上げます

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
    long timeout() default 0L;
}

@Test を確認できます。アノテーション @Target()@Retention() の 2 つのアノテーションがあります。
このようにアノテーションを付けるアノテーションを meta-annotation と呼びます。
メタデータと呼ばれるデータを宣言するデータと同じ意味です。

の後のアノテーションの形式は

修饰符 @interface 注解名 {   
    注解元素的声明1 
    注解元素的声明2   
}

アノテーション の要素は 2 つの形式で宣言されます

type elementName();
type elementName() default value;  // 带默认值

共通メタアノテーション

@TargetAnnotation

@Target 注釈は、@Target のない注釈を適用できる項目を制限するために使用されます。どのアイテムにも適用できます。

java.lang.annotation.ElementType クラスの @Target

  • # によって受け入れられるすべての項目を確認できます。 ##TYPE [クラス、インターフェイス、アノテーション] で

  • ##FIELD

    を使用 [フィールド、列挙定数] で ## を使用

  • #METHOD
  • [メソッド] で

    ## を使用します [パラメータ] で
  • ## を使用します
  • #CONSTRUCTOR [コンストラクター] で使用します

  • ##LOCAL_VARIABLE [ローカル変数] で使用します

  • ANNOTATION_TYPE [アノテーション] で

  • を使用 [パッケージ] で を使用

  • TYPE_PARAMETER

    Java で導入[型パラメータ] の 1.8

  • ##TYPE_USE
  • [宣言された型 Where] Java 1.8 を使用した

  • #@Test
  • アノテーションはメソッドでのみ使用できます。

    @Target(ElementType.METHOD)
    public @interface Test { ... }
    複数の項目をサポートする場合は、複数の値を渡します。
    @Target({ElementType.TYPE, ElementType.METHOD})
    public @interface MyAnnotation { ... }
    さらに、メタアノテーションもアノテーションであり、
  • @Target
アノテーションなどのアノテーションの構文に準拠します。

@Target(ElementType.ANNOTATION_TYPE)

@Target

注釈が注釈でのみ使用できることを示します。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

@RetentionAnnotation
@Retention注釈を保持する期間を指定します。デフォルトは RetentionPolicy.CLASS## です。 #。

すべての項目は

java.lang.annotation.RetentionPolicy で確認できます

SOURCE はクラス ファイルに含まれていません

CLASS
    クラス ファイルに含まれます。仮想マシンをロードしません。
  • RUNTIME
  • に含まれます。仮想マシンによってロードされるクラス ファイルでは、リフレクション API を使用して、仮想マシンにロードされる
  • @Test
  • アノテーションを取得できます。コード
  • @Retention(RetentionPolicy.RUNTIME)
    public @interface Test { ... }

    @DocumentedAnnotation

  • は主にアーカイブ ツールの識別に使用されます。注釈付きの要素は、
Javadoc

または同様のツールを使用して文書化できます。

@Inherited

Annotation

@Inherited

アノテーションのアノテーションを追加しました。アノテーションが付けられたクラスのサブクラスにもこのアノテーションが付けられます Annotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation { ... }

親クラス<pre class="brush:php;toolbar:false">@MyAnnotation  class Parent { ... }</pre>サブクラス

Child

は、@MyAnnotation

Parent

Inherited

に追加します。
class Child extends Parent { ... }

@Repeatable

AnnotationsJava 1.8 で導入されたアノテーションは、アノテーションを再利用可能としてマークします。 注 1<pre class="brush:php;toolbar:false">public @interface MyAnnotations {        MyAnnotation[] value();    }</pre>注 2

@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {   
    int value();
}

@Repeatable() を使用する場合に使用されます

@MyAnnotation(1)
@MyAnnotation(2)
@MyAnnotation(3)
public class MyTest { ... }

使用されません## を使用する場合#@Repeatable()

,

@MyAnnotation

remove

@Repeatable

meta-annotation

@MyAnnotations({
    @MyAnnotation(1), 
    @MyAnnotation(2),
    @MyAnnotation(3)})
public class MyTest { ... }
このアノテーションは依然として非常に有用であり、コードを変更する必要があります。 Spring

@ComponentScan アノテーションもこのメタ アノテーションを使用します。 要素の種類サポートされる要素の種類


8 つの基本データ型 (byteshort

) 、

char

int

    long
  • floatdoubleboolean )文字列##クラス

  • enum

  • アノテーション タイプ

  • 配列 (上記のタイプのすべての配列)
  • #Example

    列挙型クラス
public enum Status {
    GOOD,
    BAD
}
  • 注1

    @Target(ElementType.ANNOTATION_TYPE)
    public @interface MyAnnotation1 {
        int val();
    }
    注2
  • @Target(ElementType.TYPE)
    public @interface MyAnnotation2 {
        
        boolean boo() default false;
        
        Class<?> cla() default Void.class;
        
        Status enu() default Status.GOOD;
        
        MyAnnotation1 anno() default @MyAnnotation1(val = 1);
        
        String[] arr();
        
    }
  • 使用する場合、デフォルト値のない要素は値を渡す必要があります

    @MyAnnotation2(
            cla = String.class,
            enu = Status.BAD,
            anno = @MyAnnotation1(val = 2),
            arr = {"a", "b"})
    public class MyTest { ... }
  • Java組み込みアノテーション

    @Override

    Annotations

    は、これが親クラスをオーバーライドするメソッドであることをコンパイラーに伝えます。 。親クラスがこのメソッドを削除すると、サブクラスはエラーを報告します。

    @Deprecated

    Annotation

    は、注釈が付けられた要素が非推奨になったことを示します。

    @SuppressWarnings 注釈

    警告を無視するようにコンパイラに指示します。

    @FunctionalInterfaceアノテーション

    Java 1.8 で導入されたアノテーション。このアノテーションにより、コンパイラ

    javac

    は、インターフェイスが関数型インターフェイス標準に準拠しているかどうかを確認するようになります。

    特别的注解

    有两种比较特别的注解

    • 标记注解 : 注解中没有任何元素,使用时直接是 @XxxAnnotation, 不需要加括号

    • 单值注解 : 注解只有一个元素,且名字为value,使用时直接传值,不需要指定元素名@XxxAnnotation(100)

    利用反射获取注解

    JavaAnnotatedElement接口中有getAnnotation()等获取注解的方法。
    MethodFieldClassPackage等类均实现了这个接口,因此均有获取注解的能力。

    例子

    注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
    public @interface MyAnno {   
        String value();   
    }

    被注解的元素

    @MyAnno("class")
    public class MyClass {
        
        @MyAnno("feild")
        private String str;
        
        @MyAnno("method")
        public void method() { }
        
    }

    获取注解

    public class Test {
        
        public static void main(String[] args) throws Exception {
        
            MyClass obj = new MyClass();
            Class<?> clazz = obj.getClass();
            
            // 获取对象上的注解
            MyAnno anno = clazz.getAnnotation(MyAnno.class);
            System.out.println(anno.value());
            
            // 获取属性上的注解
            Field field = clazz.getDeclaredField("str");
            anno = field.getAnnotation(MyAnno.class);
            System.out.println(anno.value());
            
            // 获取方法上的注解
            Method method = clazz.getMethod("method");
            anno = method.getAnnotation(MyAnno.class);
            System.out.println(anno.value());
        }
        
    }

    Spring中使用自定义注解

    注解本身不会有任何的作用,需要有其他代码或工具的支持才有用。

    需求

    设想现有这样的需求,程序需要接收不同的命令CMD
    然后根据命令调用不同的处理类Handler
    很容易就会想到用Map来存储命令和处理类的映射关系。

    由于项目可能是多个成员共同开发,不同成员实现各自负责的命令的处理逻辑。
    因此希望开发成员只关注Handler的实现,不需要主动去Map中注册CMDHandler的映射。

    最终效果

    最终希望看到效果是这样的

    @CmdMapping(Cmd.LOGIN)
    public class LoginHandler implements ICmdHandler {
        @Override
        public void handle() {
            System.out.println("handle login request");
        }
    }
    
    @CmdMapping(Cmd.LOGOUT)
    public class LogoutHandler implements ICmdHandler {
        @Override
        public void handle() {
            System.out.println("handle logout request");
        }
    }

    开发人员增加自己的Handler,只需要创建新的类并注上@CmdMapping(Cmd.Xxx)即可。

    具体做法

    具体的实现是使用Spring和一个自定义的注解
    定义@CmdMapping注解

    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Component
    public @interface CmdMapping {
        int value();   
    }

    @CmdMapping中有一个int类型的元素value,用于指定CMD。这里做成一个单值注解。
    这里还加了Spring@Component注解,因此注解了@CmdMapping的类也会被Spring创建实例。

    然后是CMD接口,存储命令。

    public interface Cmd {
        int REGISTER = 1;
        int LOGIN    = 2;
        int LOGOUT   = 3;
    }

    之后是处理类接口,现实情况接口会复杂得多,这里简化了。

    public interface ICmdHandler { 
        void handle();   
    }

    上边说过,注解本身是不起作用的,需要其他的支持。下边就是让注解生效的部分了。
    使用时调用handle()方法即可。

    @Component
    public class HandlerDispatcherServlet implements 
        InitializingBean, ApplicationContextAware {
    
        private ApplicationContext context;
    
        private Map<Integer, ICmdHandler> handlers = new HashMap<>();
        
        public void handle(int cmd) {
            handlers.get(cmd).handle();
        }
        
        public void afterPropertiesSet() {
            
            String[] beanNames = this.context.getBeanNamesForType(Object.class);
    
            for (String beanName : beanNames) {
                
                if (ScopedProxyUtils.isScopedTarget(beanName)) {
                    continue;
                }
                
                Class<?> beanType = this.context.getType(beanName);
                
                if (beanType != null) {
                    
                    CmdMapping annotation = AnnotatedElementUtils.findMergedAnnotation(
                            beanType, CmdMapping.class);
                    
                    if(annotation != null) {
                        handlers.put(annotation.value(), (ICmdHandler) context.getBean(beanType));
                    }
                }
            }
            
        }
    
        public void setApplicationContext(ApplicationContext applicationContext)
                throws BeansException {   
            this.context = applicationContext;
        }
    
    }

    主要工作都是Spring做,这里只是将实例化后的对象putMap中。

    测试代码

    @ComponentScan("pers.custom.annotation")
    public class Main {
    
        public static void main(String[] args) {
            
            AnnotationConfigApplicationContext context 
                = new AnnotationConfigApplicationContext(Main.class);
                
            HandlerDispatcherServlet servlet = context.getBean(HandlerDispatcherServlet.class);
            
            servlet.handle(Cmd.REGISTER);
            servlet.handle(Cmd.LOGIN);
            servlet.handle(Cmd.LOGOUT);
    
            context.close();
        }
    }

    > 完整项目

    总结

    可以看到使用注解能够写出很灵活的代码,注解也特别适合做为使用框架的一种方式。
    所以学会使用注解还是很有用的,毕竟这对于上手框架或实现自己的框架都是非常重要的知识。

    以上がJava アノテーションの定義と使用の概要 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。