徹底的なJAVAアノテーション(Annotation):カスタムアノテーション
I. 基本中の基本:メタアノテーション
アノテーションについて深く学ぶには、自分でアノテーションを定義し、それを使えるようになることが必要です。独自のアノテーションを定義する前に、Javaが提供するメタアノテーションと、それを定義するための関連構文について理解する必要があります。
メタアノテーション
メタアノテーションの役割は、他のアノテーションの注釈を担当することである。Java 5.0では、他のアノテーションタイプの説明を提供するために使用される4つの標準メタアノテーションタイプが定義されています。
1.@Target。
2.@Retention
3.@Documented,
4.継承
これらの型とサポートするクラスは、java.lang.annotation パッケージに含まれています。以下では、それぞれのメタアノテーションの役割と、対応するサブパラメータの使用方法について説明します。
ターゲット
@Targetは、Annotationが修正するオブジェクトの範囲を記述します。Annotationはパッケージ、型(クラス、インターフェース、列挙、Annotation型)、型メンバー(メソッド、コンストラクター、メンバー変数、列挙値)、メソッドパラメーター、ローカル変数(ループ変数、キャッチパラメーターなど)に使用することができます。Annotation型の宣言でtargetを使用することで、何を変更するかが明確になる。
役割 アノテーションのスコープを記述する(記述されているアノテーションが何に使えるか)。
Fetch値(ElementType)は。
1. CONSTRUCTOR: コンストラクタを記述するために使用されます。
2.FIELD:ドメインを記述するために使用します。
3.LOCAL_VARIABLE: ローカル変数を記述するために使用される
4.METHOD:メソッドを記述するために使用される
5.PACKAGE:パッケージを記述するために使用される
6.PARAMETER:パラメータを記述するために使用する
7.TYPE: クラス、インターフェース(注釈付きタイプを含む)、または列挙型宣言を記述するために使用されます。
使用例
@Target(ElementType.TYPE)
public @interface Table {
/**
* Data table name annotation, the default value is the class name
* @return
*/
public String tableName() default "className";
}
@Target(ElementType.FIELD)
public @interface NoDBColumn {
}
<イグ
アノテーションTableはクラス、インターフェース(アノテーションされた型を含む)、enum宣言のアノテーションに使用でき、アノテーションNoDBColumnはクラスのメンバー変数へのアノテーションにのみ使用可能です。
@保持する。
クラスファイルにコンパイルされたAnnotationは、仮想マシンによって無視されるかもしれませんが、他のものはクラスがロードされたときに読み込まれます(Annotationが使用中のクラスから分離されているので、これはクラスの実行に影響を与えないことに注意してください)。このメタアノテーションは、アノテーションのライフサイクルを制限するために使用されます。
内容:アノテーション情報をどのレベルで保存する必要があるかを示し、アノテーションのライフサイクル(記述されているアノテーションが有効である範囲)を記述する。
値(RetentionPoicy)は以下の通り。
1. SOURCE:ソースファイル内で有効(=ソースファイル保持)
2.CLASS:クラスファイル内で有効(=クラスが保存されていること)
3.RUNTIME:ランタイム時に有効(=ランタイムリザーブド)
Retentionメタアノテーション型は、java.lang.annotation.RetentionPolicyの列挙型値から値を取る一意の値をメンバーとして持っています。具体的な例としては、以下のようなものがあります。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
<イグ
ColumnアノテーションのRetentionPolicyプロパティは値RUTIMEを持っているので、アノテーションプロセッサはリフレクションを通じてアノテーションのプロパティ値を取得し、いくつかの実行時ロジックを行うことができる。
@Documented:
Documentedは、アノテーションされたプログラムのメンバーのためのパブリックAPIとして使用されるべきで、したがってjavadocなどのツールでドキュメント化することができる他のタイプのアノテーションを記述するために使用されます。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
<イグ
@継承される。
Inheritedメタアノテーションは、@Inheritedが特定のアノテーションタイプが継承されることを示すマークアップアノテーションです。Inheritedで修飾されたアノテーションタイプがクラスに使用されると、そのクラスのサブクラスにもそのアノテーションが使用されることになります。
注:@Inheritedアノテーションタイプは、アノテーションクラスのサブクラスから継承されます。クラスは、それが実装しているインターフェースからアノテーションを継承せず、メソッドは、それがオーバーライドするメソッドからアノテーションを継承しない。
Reflection APIは、@InheritedアノテーションタイプでアノテーションされたアノテーションのRetentionがRetentionPolicyである場合に、この継承を強化します。java.lang.reflectを使って@Inheritedアノテーションタイプのアノテーションを照会すると、 リフレクションコードチェックは仕事をします: 指定されたアノテーションタイプが見つかるか、 クラス継承構造のトップレベルに到達するまで、 クラスとその親クラスをチェックします。
コード例です。
/**
*
* @author peida
*
*/
@Inherited
public @interface Greeting {
public enum FontColor{ BULE,RED,GREEN};
String name();
FontColor fontColor() default FontColor.GREEN;
}
II. 基本的なこと カスタムアノテーション
@interface カスタムアノテーションを使用すると、java.lang.annotation.Annotation インターフェイスが自動的に継承され、残りの詳細についてはコンパイラが自動的に処理します。アノテーションを定義する場合、他のアノテーションやインターフェイスを継承することはできません。各メソッドが実際に設定パラメータを宣言するアノテーションを宣言する場合は、@interfaceを使用します。メソッド名がパラメータ名、戻り値の型がパラメータの型になります(戻り値の型はbasic, Class, String, enumのみ)。パラメータのデフォルト値は、defaultを介して宣言することができます。
アノテーションの形式を定義します。
public @interface annotation name {definition body}.
アノテーションパラメータでサポートされているデータ型。
1. すべての基本データ型(int, float, boolean, byte, double, char, long, short)
2.文字列型
3.クラス型
4.enum型
5. アノテーションの種類
6. 上記すべての型の配列
Annotation型の内部にパラメータを設定する方法。
まず、アクセス修飾子はpublicかdefaultしか使えません。例えば、String value();では、このメソッドはデフォルトの型であるdefaulに設定されています。
次に、パラメータメンバには、byte, short, char, int, long, float, double, booleanとString, Enum, Class, annotationsなどの基本データ型と、これらの型の配列しか使えません。例えば、String value();は、パラメータメンバがStringである場合。
第三に、パラメータが1つしかない場合は、パラメータ名を"value"とし、その後に括弧を付けるとよい。例 次のFruitNameアノテーションの例では、パラメータ・メンバが1つだけです。
簡単なカスタムアノテーションとアノテーションの使用例です。
package annotation;
import java.lang.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation;
RetentionPolicy; import java.lang.annotation;
Target; import java.lang.annotation;
/**
* fruit name annotation
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
package annotation;
import java.lang.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation;
RetentionPolicy; import java.lang.annotation;
Target; import java.lang.annotation;
/**
* fruit color annotation
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
/**
* Color enumeration
* @author peida
*
*/
public enum Color{ BULE,RED,GREEN};
/**
* color property
* @return
*/
Color fruitColor() default Color.GREEN;
}
package annotation;
import annotation.FruitColor.Color;
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor=Color.)
private String appleColor;
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleName() {
return appleName;
}
public void displayName(){
System.out.println("The name of the fruit is: apple");
}
}
<イグ
1 package testAnnotation;
2
3 import java.lang.annotation;
4 import java.lang.annotation;
RetentionPolicy; 5 import java.lang.annotation;
6
7 @Documented
8 @Retention(RetentionPolicy.RUNTIME)
9 public @interface Person{
10 String name();
11 int age();
12 }
package testAnnotation;
2
3 @Person(name="xingoo",age=25)
4 public class test3 {
5 public static void print(Class c){
6 System.out.println(c.getName());
7
8 // The getAnnotation method of java.lang.Class returns the annotation if it has one. Otherwise return null
9 Person person = (Person)c.getAnnotation(Person.class);
10
11 if(person ! = null){
12 System.out.println("name:"+person.name()+" age:"+person.age());
13 }else{
14 System.out.println("person unknown!");
15 }
16 }
17 public static void main(String[] args){
18 test3.print(test3.class);
19 }
20 }
<イグ
testAnnotation.test3
name:xingoo age:25
<イグ
アノテーションされた要素のデフォルト値。
アノテーション要素は、アノテーションを定義するデフォルト値で指定されるか、アノテーションが使用されるときに明確な値を持つ必要があり、基本型以外のアノテーション要素の値はNULLであってはならない。したがって、デフォルト値として空の文字列または0を使用することが一般的である。この制約により、各アノテーション宣言にはすべての要素が存在し、対応する値を持つため、プロセッサが要素の有無を表現することは困難である。この制約を回避するために、空文字列や負数などの特殊な値を定義して、一度に要素の不在を示す必要があり、これがアノテーションを定義する際の慣例的な用法となっている。
III. カスタムアノテーションの例
以上がアノテーションの基本ですが、ここではカスタムアノテーションの使用方法について少し説明します。一般的に、アノテーションは反射型パーサーと一緒に動作し、クラスのアノテーション内容を見るために反射機構を使用します。以下のように。
package com.newsee.annotation;
import java.lang.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
Inherited; import java.lang.annotation;
RetentionPolicy; import java.lang.annotation;
Target; import java.lang.annotation;
Target; /**
* Determine if you are logged in
*
*/
@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginVerify {
}
<イグ
package com.newsee.annotation;
import java.io.IOException;
import java.lang.reflect;
import javax.annotation.PostConstruct;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import com.newsee.constant.LoginVerifyMapping;
@Component
public class ScanningLoginVerifyAnnotation {
private static final String PACKAGE_NAME = "com.newsee.face";
private static final String RESOURCE_PATTERN = "/*/*.class";
@PostConstruct
public void scanning() throws IOException, SecurityException,
ClassNotFoundException {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(PACKAGE_NAME)
+ RESOURCE_PATTERN;
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resourcePatternResolver.getResources(pattern);
for (Resource resource resource : resources) {
if (resource.isReadable()) {
String className = getClassName(resource.getURL().toString());
Class cls = ScanningRequestCodeAnnotation.class.getClassLoader().loadClass((className));
for (Method method : cls.getMethods()) {
LoginVerify requestCode = method.getAnnotation(LoginVerify.class);
if (requestCode ! = null) {
</span>LoginVerifyMapping.add(className + ". " + method.getName());
}
}
}
}
}
private String getClassName(String resourceUrl) {
String url = resourceUrl.replace("/", ". ");
url = url.replace("\\\", ". ");
url = url.split("com.newsee")[1];
url = url.replace(".class", "");
return "com.newsee" + url.trim();
}
}
実行結果
public class LoginVerifyMapping {
private static Map<String, Boolean> faceFunctionIsNeedLoginVerify = new HashMap<String, Boolean>();
public static void add(String functionName) {
faceFunctionIsNeedLoginVerify.put(functionName, Boolean.TRUE);
}
public static Boolean getFaceFunctionIsNeedLoginVerify(String functionName) {
return faceFunctionIsNeedLoginVerify.get(functionName);
}
}
最後にもう一つ、動作例をご紹介します。
LoginVerifyアノテーションは、アノテーションされたメソッドへのアクセス要求時に、ログイン判定を行うために使用されます。
private ResponseContent handleRequests(RequestContent requestContent) throws ClassNotFoundException,
NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
String requestCode = requestContent.getRequest().getHead().getNWCode();
String className = RequestCodeMapping.getClassName(requestCode);
String beanName = RequestCodeMapping.getBeanName(requestCode);
String functionName = RequestCodeMapping.getFunctionName(requestCode);
Boolean loginVerify = LoginVerifyMapping.getFaceFunctionIsNeedLoginVerify(className + ". " + functionName);
if (loginVerify ! = null && loginVerify) {//requires login verification
boolean isAuthenticated = SecurityUtils.getSubject().isAuthenticated();
if (!isAuthenticated) {
String exId=requestContent.getRequest().getHead().getNWExID();
SystemMobileTokenKeyServiceInter systemMobileTokenKeyServiceInter = (SystemMobileTokenKeyServiceInter) SpringContextUtil
.getBean("systemMobileTokenKeyServiceInter");
SystemMobileTokenKey systemMobileTokenKey=systemMobileTokenKeyServiceInter.getByExId(exId);
if(systemMobileTokenKey==null)
throw new BaseException(ResponseCodeEnum.NO_LOGIN);
Date keyTime = systemMobileTokenKey.getKeyTime();
if (System.currentTimeMillis() - keyTime.getTime() > 1000 * 60 * 60 * 24 * 3)
throw new BaseException(ResponseCodeEnum.NO_LOGIN);
}
}
if (className == null || beanName == null || functionName == null)
throw new BaseException(ResponseCodeEnum.REQUEST_CODE_NOT_EXIST);
Object object = SpringContextUtil.getBean(beanName);
Class cls = Class.forName(className);
Method method = cls.getMethod(functionName, RequestContent.class);
Object response = method.invoke(object, requestContent);
return (ResponseContent) response;
}
}
ScanningLoginVerifyAnnotation の scanning() メソッドは、@PostConstruct によって変更され、次のように示されます。 サーバーがServletをロードするときに実行され、サーバーによって一度だけ実行されます。
<スパン ここで、もう少しだけ科学的な話をします。
PostConstruct と @PreDestroy です。この 2 つのアノテーションは、非静的な void() メソッドを変更するために使用されます。以下の2つの方法で記述します。
ポストコンストラクト
Public void someMethod() {}.
または
public @PostConstruct void someMethod(){}.
PostConstructはコンストラクタの後、init()メソッドの前に実行され、preDestroy()メソッドはdestroy()メソッドの後に実行されます。
スキャンメソッドは、サーブレットがロードされた後にロードされたすべてのクラスを取得し、その中のメソッドを反復処理し、LoginVerifyアノテーションによって変更されたものがあれば、そのメソッド名を静的マップに入れ、保存します。
package com.newsee.annotation;
import java.io.IOException;
import java.lang.reflect;
import javax.annotation.PostConstruct;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import com.newsee.constant.LoginVerifyMapping;
@Component
public class ScanningLoginVerifyAnnotation {
private static final String PACKAGE_NAME = "com.newsee.face";
private static final String RESOURCE_PATTERN = "/*/*.class";
@PostConstruct
public void scanning() throws IOException, SecurityException,
ClassNotFoundException {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(PACKAGE_NAME)
+ RESOURCE_PATTERN;
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resourcePatternResolver.getResources(pattern);
for (Resource resource resource : resources) {
if (resource.isReadable()) {
String className = getClassName(resource.getURL().toString());
Class cls = ScanningRequestCodeAnnotation.class.getClassLoader().loadClass((className));
for (Method method : cls.getMethods()) {
LoginVerify requestCode = method.getAnnotation(LoginVerify.class);
if (requestCode ! = null) {
</span>LoginVerifyMapping.add(className + ". " + method.getName());
}
}
}
}
}
private String getClassName(String resourceUrl) {
String url = resourceUrl.replace("/", ". ");
url = url.replace("\\\", ". ");
url = url.split("com.newsee")[1];
url = url.replace(".class", "");
return "com.newsee" + url.trim();
}
}
LoginVerifyMappingは、LoginVerifyアノテーションによって変更されるメソッドの名前を保持するものです。
public class LoginVerifyMapping {
private static Map<String, Boolean> faceFunctionIsNeedLoginVerify = new HashMap<String, Boolean>();
public static void add(String functionName) {
faceFunctionIsNeedLoginVerify.put(functionName, Boolean.TRUE);
}
public static Boolean getFaceFunctionIsNeedLoginVerify(String functionName) {
return faceFunctionIsNeedLoginVerify.get(functionName);
}
}
次のメソッドは、要求されたメソッドが LoginVerifyMappingであれば、ログイン認証が必要です。
private ResponseContent handleRequests(RequestContent requestContent) throws ClassNotFoundException,
NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
String requestCode = requestContent.getRequest().getHead().getNWCode();
String className = RequestCodeMapping.getClassName(requestCode);
String beanName = RequestCodeMapping.getBeanName(requestCode);
String functionName = RequestCodeMapping.getFunctionName(requestCode);
Boolean loginVerify = LoginVerifyMapping.getFaceFunctionIsNeedLoginVerify(className + ". " + functionName);
if (loginVerify ! = null && loginVerify) {//requires login verification
boolean isAuthenticated = SecurityUtils.getSubject().isAuthenticated();
if (!isAuthenticated) {
String exId=requestContent.getRequest().getHead().getNWExID();
SystemMobileTokenKeyServiceInter systemMobileTokenKeyServiceInter = (SystemMobileTokenKeyServiceInter) SpringContextUtil
.getBean("systemMobileTokenKeyServiceInter");
SystemMobileTokenKey systemMobileTokenKey=systemMobileTokenKeyServiceInter.getByExId(exId);
if(systemMobileTokenKey==null)
throw new BaseException(ResponseCodeEnum.NO_LOGIN);
Date keyTime = systemMobileTokenKey.getKeyTime();
if (System.currentTimeMillis() - keyTime.getTime() > 1000 * 60 * 60 * 24 * 3)
throw new BaseException(ResponseCodeEnum.NO_LOGIN);
}
}
if (className == null || beanName == null || functionName == null)
throw new BaseException(ResponseCodeEnum.REQUEST_CODE_NOT_EXIST);
Object object = SpringContextUtil.getBean(beanName);
Class cls = Class.forName(className);
Method method = cls.getMethod(functionName, RequestContent.class);
Object response = method.invoke(object, requestContent);
return (ResponseContent) response;
}
}
関連
-
ファインバグタイプ
-
eclipse で「アクセス制限: タイプ 'HttpServer' は API ではありません」というプロンプトが表示される。
-
JavaMailのメール送信が失敗するケースとその説明の分析
-
サーブレットクラスのインスタンス化エラーの解決法
-
Methodのinvokeメソッド実装のJavaリフレクション
-
org.glassfish.jersey.servlet.ServletContainer
-
SocketTimeoutExceptionの解決方法です。読み込みがタイムアウトした
-
java -serverコマンドで「Error: no `server' JVM at ... jvm.dll」を解決する方法です。
-
Ali cloud ubuntu16 システムで LAMP を構築し、tomcat、jdk をインストールし、最初の javaweb プロジェクトを tomcat にデプロイする 詳細手順
-
Java文字列プレースホルダー使用
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
スタイルシートとして解釈されるリソースが、MIMEタイプtext/htmlで転送される。
-
スタイルが読み込まれず、ブラウザのコンソールでエラーが報告される。リソースはスタイルシートとして解釈されますが、MIMEタイプtext/htmlで転送されます。
-
Eclipse起動エラー:javaは起動したが、終了コード=1を返した(ネット上の様々な落とし穴)
-
スレッド "main" で例外発生 java.net.BindException: アドレスは既に使用中です。NET_Bind
-
SocketTimeoutExceptionです。読み込みがタイムアウトしました
-
コミットには何も追加されないが、未追跡のファイルが存在し、gitで未追跡のファイルに対する完璧な解決策
-
Maven Pluginの実行がライフサイクル設定の対象外であるエラーの解決
-
「リソースリーク:'scanner'が閉じない」警告、Scannerステートメントでの解決法
-
javaで "Unhandled exception type ...... "を処理するには?
-
Tomcat 8は、「少なくとも1つのJARがTLDをスキャンされたが、TLDが含まれていない」問題を解決します。