赞
踩
注解是JDK1.5版本开始引入的一个特性,用于对程序代码的说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。
它主要的作用有以下四方面:
注解的常见分类有三种:
接下来我们通过这三种分类来逐一理解注解。
我们先从Java内置注解开始说起,先看下下面的代码:
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-typescript"><span style="color:#0000ff">class</span> <span style="color:#a31515">Parent</span> { <span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">rewriteMethod</span>() { } } <span style="color:#0000ff">class</span> <span style="color:#a31515">Child</span> <span style="color:#0000ff">extends</span> <span style="color:#a31515">Parent</span> { <span style="color:#008000">/** * 重载父类的 rewriteMethod() 方法 */</span> <span style="color:#2b91af">@Override</span> <span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">rewriteMethod</span>() { } <span style="color:#008000">/** * 被弃用的过时方法 */</span> <span style="color:#2b91af">@Deprecated</span> <span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">oldMethod</span>() { } <span style="color:#008000">/** * 忽略告警 * * <span style="color:#808080">@return</span> */</span> <span style="color:#2b91af">@SuppressWarnings</span>(<span style="color:#a31515">"keep run"</span>) <span style="color:#0000ff">public</span> <span style="color:#a31515">List</span> <span style="color:#a31515">infoList</span>() { <span style="color:#a31515">List</span> list = <span style="color:#0000ff">new</span> <span style="color:#a31515">ArrayList</span>(); <span style="color:#0000ff">return</span> list; } } </code></span></span>
Java 1.5开始自带的标准注解,包括@Override、@Deprecated和@SuppressWarnings:
@Override
:表示当前类中的方法定义将覆盖父类中的方法@Deprecated
:表示该代码段被弃用,但是可以使用,只是编译器会发出警告而已@SuppressWarnings
:表示关闭编译器的警告信息我们先来看一下这个注解类型的定义:
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-less"><span style="color:#008000">@Target</span>(ElementType.METHOD)
- <span style="color:#008000">@Retention</span>(RetentionPolicy.SOURCE)
- public <span style="color:#008000">@interface</span> Override {
- }
- </code></span></span>
从它的定义我们可以看到,这个注解可以被用来修饰方法,并且它只在编译时有效,在编译后的class文件中便不再存在。这个注解的作用我们大家都不陌生,那就是告诉编译器被修饰的方法是重写的父类的中的相同签名的方法,编译器会对此做出检查,
若发现父类中不存在这个方法或是存在的方法签名不同,则会报错。
这个注解的定义如下:
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-less"><span style="color:#008000">@Documented</span>
- <span style="color:#008000">@Retention</span>(RetentionPolicy.RUNTIME)
- <span style="color:#008000">@Target</span>(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
- public <span style="color:#008000">@interface</span> Deprecated {
- }
-
- </code></span></span>
从它的定义我们可以知道,它会被文档化,能够保留到运行时,能够修饰构造方法、属性、局部变量、方法、包、参数、类型。这个注解的作用是告诉编译器被修饰的程序元素已被“废弃”,不再建议用户使用。
这个注解我们也比较常用到,先来看下它的定义:
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-less"><span style="color:#008000">@Target</span>({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
- <span style="color:#008000">@Retention</span>(RetentionPolicy.SOURCE)
- public <span style="color:#008000">@interface</span> SuppressWarnings {
- <span style="color:#0000ff">String</span><span style="color:#2b91af">[]</span> <span style="color:#0000ff">value</span>();
- }
-
- </code></span></span>
它能够修饰的程序元素包括类型、属性、方法、参数、构造器、局部变量,只能存活在源码时,取值为String[]。它的作用是告诉编译器忽略指定的警告信息,它可以取的值如下所示:
参数 | 作用 | 原描述 |
---|---|---|
all | 抑制所有警告 | to suppress all warnings |
boxing | 抑制装箱、拆箱操作时候的警告 | to suppress warnings relative to boxing/unboxing operations |
cast | 抑制映射相关的警告 | to suppress warnings relative to cast operations |
dep-ann | 抑制启用注释的警告 | to suppress warnings relative to deprecated annotation |
deprecation | 抑制过期方法警告 | to suppress warnings relative to deprecation |
fallthrough | 抑制确在switch中缺失breaks的警告 | to suppress warnings relative to missing breaks in switch statements |
finally | 抑制finally模块没有返回的警告 | to suppress warnings relative to finally block that don’t return |
hiding | 抑制与隐藏变数的区域变数相关的警告 | to suppress warnings relative to locals that hide variable() |
incomplete-switch | 忽略没有完整的switch语句 | to suppress warnings relative to missing entries in a switch statement (enum case) |
nls | 忽略非nls格式的字符 | to suppress warnings relative to non-nls string literals |
null | 忽略对null的操作 | to suppress warnings relative to null analysis |
rawtype | 使用generics时忽略没有指定相应的类型 | to suppress warnings relative to un-specific types when using |
restriction | 抑制与使用不建议或禁止参照相关的警告 | to suppress warnings relative to usage of discouraged or |
serial | 忽略在serializable类中没有声明serialVersionUID变量 | to suppress warnings relative to missing serialVersionUID field for a serializable class |
static-access | 抑制不正确的静态访问方式警告 | to suppress warnings relative to incorrect static access |
synthetic-access | 抑制子类没有按最优方法访问内部类的警告 | to suppress warnings relative to unoptimized access from inner classes |
unchecked | 抑制没有进行类型检查操作的警告 | to suppress warnings relative to unchecked operations |
unqualified-field-access | 抑制没有权限访问的域的警告 | to suppress warnings relative to field access unqualified |
unused | 抑制没被使用过的代码的警告 | to suppress warnings relative to unused code |
上述内置注解的定义中使用了一些元注解(注解类型进行注解的注解类),在JDK 1.5中提供了4个标准的元注解:@Target,@Retention,@Documented,@Inherited, 在JDK 1.8中提供了两个新的元注解 @Repeatable和@Native。
Target注解的作用是:描述注解的使用范围(即:被修饰的注解可以用在什么地方) 。
Target注解用来说明那些被它所注解的注解类可修饰的对象范围:
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-cpp"><span style="color:#0000ff">public</span> <span style="color:#0000ff">enum</span> <span style="color:#a31515">ElementType</span> {
- TYPE, <span style="color:#008000">// 类、接口、枚举类 </span>
- FIELD, <span style="color:#008000">// 成员变量(包括:枚举常量) </span>
- METHOD, <span style="color:#008000">// 成员方法 </span>
- PARAMETER, <span style="color:#008000">// 方法参数 </span>
- CONSTRUCTOR, <span style="color:#008000">// 构造方法 </span>
- LOCAL_VARIABLE, <span style="color:#008000">// 局部变量 </span>
- ANNOTATION_TYPE, <span style="color:#008000">// 注解类 </span>
- PACKAGE, <span style="color:#008000">// 可用于修饰:包 </span>
- TYPE_PARAMETER, <span style="color:#008000">// 类型参数,JDK 1.8 新增 </span>
- TYPE_USE <span style="color:#008000">// 使用类型的任何地方,JDK 1.8 新增 </span>
- }
- </code></span></span>
Reteniton注解的作用是:描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时) 。
Reteniton注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略,定义在RetentionPolicy枚举中。枚举如下:
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-swift"><span style="color:#0000ff">public</span> <span style="color:#0000ff">enum</span> <span style="color:#a31515">RetentionPolicy</span> {
-
- <span style="color:#a31515">SOURCE</span>, <span style="color:#008000">// 源文件保留</span>
- <span style="color:#a31515">CLASS</span>, <span style="color:#008000">// 编译期保留,默认为该值,CLASS</span>
- <span style="color:#a31515">RUNTIME</span> <span style="color:#008000">// 运行期保留,可通过反射去获取注解信息</span>
- }
- </code></span></span>
我们测试下这三种策略,在定义注解类的时候什么区别:
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-less"><span style="color:#008000">@Retention</span>(RetentionPolicy.SOURCE) public <span style="color:#008000">@interface</span> SourcePolicy { <span style="color:#008000">// 源文件保留策略</span> } <span style="color:#008000">@Retention</span>(RetentionPolicy.CLASS) public <span style="color:#008000">@interface</span> ClassPolicy { <span style="color:#008000">// 编译器保留策略</span> } <span style="color:#008000">@Retention</span>(RetentionPolicy.RUNTIME) public <span style="color:#008000">@interface</span> RuntimePolicy { <span style="color:#008000">// 运行期保留策略</span> } </code></span></span>
上面已经定义好了三个注解类,我们再用这三个注解类再去注解方法,如下:
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-less"><span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> <span style="color:#0000ff">RetentionTest</span> { <span style="color:#008000">@SourcePolicy</span> public void sourcePolicy() { } <span style="color:#008000">@ClassPolicy</span> public void classPolicy() { } <span style="color:#008000">@RuntimePolicy</span> public void runtimePolicy() { } } </code></span></span>
通过执行 javap -verbose RetentionTest命令获取到的RetentionTest 的 class 字节码内容如下。
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-yaml">{ <span style="color:#a31515">public</span> <span style="color:#a31515">retention.RetentionTest();</span> <span style="color:#ff0000">flags:</span> <span style="color:#a31515">ACC_PUBLIC</span> <span style="color:#ff0000">Code:</span> <span style="color:#a31515">stack=1</span>, <span style="color:#a31515">locals=1</span>, <span style="color:#a31515">args_size=1</span> <span style="color:#ff0000">0:</span> <span style="color:#a31515">aload_0</span> <span style="color:#ff0000">1:</span> <span style="color:#a31515">invokespecial</span> <span style="color:#008000">#1 // Method java/lang/Object."<init>":()V</span> <span style="color:#ff0000">4:</span> <span style="color:#a31515">return</span> <span style="color:#ff0000">LineNumberTable:</span> <span style="color:#ff0000">line 3:</span> <span style="color:#880000">0</span> <span style="color:#a31515">public</span> <span style="color:#a31515">void</span> <span style="color:#a31515">sourcePolicy();</span> <span style="color:#ff0000">flags:</span> <span style="color:#a31515">ACC_PUBLIC</span> <span style="color:#ff0000">Code:</span> <span style="color:#a31515">stack=0</span>, <span style="color:#a31515">locals=1</span>, <span style="color:#a31515">args_size=1</span> <span style="color:#ff0000">0:</span> <span style="color:#a31515">return</span> <span style="color:#ff0000">LineNumberTable:</span> <span style="color:#ff0000">line 7:</span> <span style="color:#880000">0</span> <span style="color:#a31515">public</span> <span style="color:#a31515">void</span> <span style="color:#a31515">classPolicy();</span> <span style="color:#ff0000">flags:</span> <span style="color:#a31515">ACC_PUBLIC</span> <span style="color:#ff0000">Code:</span> <span style="color:#a31515">stack=0</span>, <span style="color:#a31515">locals=1</span>, <span style="color:#a31515">args_size=1</span> <span style="color:#ff0000">0:</span> <span style="color:#a31515">return</span> <span style="color:#ff0000">LineNumberTable:</span> <span style="color:#ff0000">line 11:</span> <span style="color:#880000">0</span> <span style="color:#ff0000">RuntimeInvisibleAnnotations:</span> <span style="color:#ff0000">0:</span> <span style="color:#008000">#11()</span> <span style="color:#a31515">public</span> <span style="color:#a31515">void</span> <span style="color:#a31515">runtimePolicy();</span> <span style="color:#ff0000">flags:</span> <span style="color:#a31515">ACC_PUBLIC</span> <span style="color:#ff0000">Code:</span> <span style="color:#a31515">stack=0</span>, <span style="color:#a31515">locals=1</span>, <span style="color:#a31515">args_size=1</span> <span style="color:#ff0000">0:</span> <span style="color:#a31515">return</span> <span style="color:#ff0000">LineNumberTable:</span> <span style="color:#ff0000">line 15:</span> <span style="color:#880000">0</span> <span style="color:#ff0000">RuntimeVisibleAnnotations:</span> <span style="color:#ff0000">0:</span> <span style="color:#008000">#14()</span> } </code></span></span>
从 RetentionTest 的字节码内容我们可以得出以下两点结论:
Documented注解的作用如下:使用 javadoc 工具为类生成帮助文档,并确认是否保留注解信息。
以下代码在使用Javadoc工具可以生成 @DocAnnotation注解信息。
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#0000ff">import</span> java.lang.annotation.Documented;
- <span style="color:#0000ff">import</span> java.lang.annotation.ElementType;
- <span style="color:#0000ff">import</span> java.lang.annotation.Target;
-
- <span style="color:#2b91af">@Documented</span>
- <span style="color:#2b91af">@Target({ElementType.TYPE,ElementType.METHOD})</span>
- <span style="color:#0000ff">public</span> <span style="color:#2b91af">@interface</span> DocAnnotation {
-
- <span style="color:#0000ff">public</span> String <span style="color:#a31515">value</span>() <span style="color:#0000ff">default</span> <span style="color:#a31515">"default"</span>;
- }
-
- </code></span></span>
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-typescript"><span style="color:#2b91af">@DocAnnotation</span>(<span style="color:#a31515">"some method doc"</span>)
- <span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">testMethod</span>() {
- <span style="color:#008000">// 测试方法的文档注解</span>
- }
-
- </code></span></span>
Inherited注解的作用:被它修饰的Annotation将具有继承特性。父类使用了被@Inherited修饰的Annotation,则子类将自动具备该注解。
我们来测试下这个注解:
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-less"><span style="color:#008000">@Inherited</span>
- <span style="color:#008000">@Retention</span>(RetentionPolicy.RUNTIME)
- <span style="color:#008000">@Target</span>({ElementType.TYPE,ElementType.METHOD})
- public <span style="color:#008000">@interface</span> InheritedAnnotation {
- <span style="color:#0000ff">String</span> <span style="color:#2b91af">[]</span> <span style="color:#0000ff">values</span>();
- <span style="color:#0000ff">int</span> <span style="color:#0000ff">number</span>();
- }
-
- </code></span></span>
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-scala"><span style="color:#2b91af">@InheritedAnnotation</span>(values = {<span style="color:#a31515">"brand"</span>}, number = <span style="color:#880000">100</span>) public <span style="color:#0000ff">class</span> <span style="color:#a31515">UserInfo</span> { } <span style="color:#0000ff">class</span> <span style="color:#a31515">Customer</span> <span style="color:#0000ff">extends</span> <span style="color:#a31515">UserInfo</span> { <span style="color:#2b91af">@Test</span> public void testMethod(){ <span style="color:#a31515">Class</span> clazz = <span style="color:#a31515">Student</span>.<span style="color:#0000ff">class</span>; <span style="color:#a31515">Annotation</span>[] annotations = clazz.getAnnotations(); <span style="color:#0000ff">for</span> (<span style="color:#a31515">Annotation</span> annotation : annotations) { <span style="color:#a31515">System</span>.out.println(annotation.toString()); } } } </code></span></span>
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-perl">xxx.InheritedAnnotation(<span style="color:#0000ff">values</span>=[brand], number=<span style="color:#880000">100</span>)
- </code></span></span>
虽然Customer类没有显示地被注解@InheritedAnnotation,但是它的父类UserInfo被注解,而且@InheritedAnnotation被@Inherited注解,因此Customer类自动继承注解
Repeatable是可重复使用的意思,允许在同一声明的类型(类,属性,或方法)中,可以多次使用同一个注解
JDK8之前要想实现注解重复使用,需要组合模式,编写和可读性都不是很好
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-less"><span style="color:#0000ff">public</span> @<span style="color:#0000ff">interface</span> <span style="color:#0000ff">Pet</span> {
- <span style="color:#0000ff">String</span> <span style="color:#0000ff">myPet</span>();
- }
-
- <span style="color:#0000ff">public</span> @<span style="color:#0000ff">interface</span> <span style="color:#0000ff">Pets</span> {
- <span style="color:#0000ff">Pet</span><span style="color:#2b91af">[]</span> <span style="color:#0000ff">value</span>();
- }
-
- <span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> <span style="color:#0000ff">RepeatAnnotationOV</span> {
- <span style="color:#008000">@Pets</span>({<span style="color:#008000">@Pet</span>(myPet=<span style="color:#a31515">"dog"</span>),<span style="color:#008000">@Pet</span>(myPet=<span style="color:#a31515">"cat"</span>)})
- public void workMethod(){
- }
- }
-
- </code></span></span>
由另一个注解来存储重复注解,在使用时候,用存储注解Authorities来扩展重复注解。
Java 8中的做法:
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-less"><span style="color:#008000">@Repeatable</span>(Pets.class) public <span style="color:#008000">@interface</span> Pet { <span style="color:#0000ff">String</span> <span style="color:#0000ff">myPet</span>(); } <span style="color:#0000ff">public</span> @<span style="color:#0000ff">interface</span> <span style="color:#0000ff">Pets</span> { <span style="color:#0000ff">Pet</span><span style="color:#2b91af">[]</span> <span style="color:#0000ff">value</span>(); } <span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> <span style="color:#0000ff">RepeatAnnotationNV</span> { <span style="color:#008000">@Pet</span>(role=<span style="color:#a31515">"dog"</span>) <span style="color:#008000">@Pet</span>(role=<span style="color:#a31515">"cat"</span>) public void workMethod(){ } } </code></span></span>
不同的地方是,创建重复注解Authority时,加上@Repeatable,指向存储注解Authorities,在使用时候,直接可以重复使用Authority注解。从上面例子看出,java 8里面做法更适合常规的思维,可读性强一点
使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用,了解即可
定义注解后,如何获取注解中的内容呢?反射包java.lang.reflect下的AnnotatedElement接口提供这些方法。这里注意:只有注解被定义为RUNTIME后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的方法来访问Annotation信息。我们看下具体的先关接口
当我们理解了内置注解, 元注解和获取注解的反射接口后,我们便可以开始自定义注解了。这个例子我把上述的知识点全部融入进来, 代码很简单:
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#0000ff">package</span> com.helenlyn.common.annotation; <span style="color:#0000ff">import</span> java.lang.annotation.Documented; <span style="color:#0000ff">import</span> java.lang.annotation.ElementType; <span style="color:#0000ff">import</span> java.lang.annotation.Retention; <span style="color:#0000ff">import</span> java.lang.annotation.RetentionPolicy; <span style="color:#0000ff">import</span> java.lang.annotation.Target; <span style="color:#008000">/** * <p>Description: 水果供应者注解 </p> * <p>Copyright: Copyright (c) 2021 </p> * <p>Company: helenlyn Co., Ltd. </p> * * <span style="color:#808080">@author</span> brand * <span style="color:#808080">@date</span> 2021/5/16 16:35 * <p>Update Time: </p> * <p>Updater: </p> * <p>Update Comments: </p> */</span> <span style="color:#2b91af">@Target(ElementType.FIELD)</span> <span style="color:#2b91af">@Retention(RetentionPolicy.RUNTIME)</span> <span style="color:#2b91af">@Documented</span> <span style="color:#0000ff">public</span> <span style="color:#2b91af">@interface</span> FruitProvider { <span style="color:#008000">/** * 供应商编号 * <span style="color:#808080">@return</span> */</span> <span style="color:#0000ff">public</span> <span style="color:#a31515">int</span> <span style="color:#a31515">id</span>() <span style="color:#0000ff">default</span> -<span style="color:#880000">1</span>; <span style="color:#008000">/** * 供应商名称 * <span style="color:#808080">@return</span> */</span> <span style="color:#0000ff">public</span> String <span style="color:#a31515">name</span>() <span style="color:#0000ff">default</span> <span style="color:#a31515">""</span>; <span style="color:#008000">/** * 供应商地址 * <span style="color:#808080">@return</span> */</span> <span style="color:#0000ff">public</span> String <span style="color:#a31515">address</span>() <span style="color:#0000ff">default</span> <span style="color:#a31515">""</span>; } </code></span></span>
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-kotlin"><span style="color:#0000ff">package</span> com.helenlyn.common.dto; <span style="color:#0000ff">import</span> com.helenlyn.common.<span style="color:#0000ff">annotation</span>.FruitColor; <span style="color:#0000ff">import</span> com.helenlyn.common.<span style="color:#0000ff">annotation</span>.FruitName; <span style="color:#0000ff">import</span> com.helenlyn.common.<span style="color:#0000ff">annotation</span>.FruitProvider; <span style="color:#008000">/** * <p>Description: </p> * <p>Copyright: Copyright (c) 2021 </p> * <p>Company: helenlyn Co., Ltd. </p> * * <span style="color:#808080">@author</span> brand * <span style="color:#808080">@date</span> 2021/5/16 16:28 * <p>Update Time: </p> * <p>Updater: </p> * <p>Update Comments: </p> */</span> <span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> <span style="color:#a31515">AppleDto</span> { <span style="color:#2b91af">@FruitName(<span style="color:#3388aa">"Apple"</span>)</span> <span style="color:#0000ff">private</span> String appleName; <span style="color:#2b91af">@FruitColor(fruitColor=FruitColor.Color.RED)</span> <span style="color:#0000ff">private</span> String appleColor; <span style="color:#2b91af">@FruitProvider(id=1,name=<span style="color:#3388aa">"helenlyn 贸易公司"</span>,address=<span style="color:#3388aa">"福州xx路xxx大楼"</span>)</span> <span style="color:#0000ff">private</span> String appleProvider; } </code></span></span>
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-php-template">/** * <span style="color:#0000ff"><<span style="color:#0000ff">p</span>></span>Description: FruitInfoUtil注解实现 <span style="color:#0000ff"></<span style="color:#0000ff">p</span>></span> * <span style="color:#0000ff"><<span style="color:#0000ff">p</span>></span>Copyright: Copyright (c) 2021 <span style="color:#0000ff"></<span style="color:#0000ff">p</span>></span> * <span style="color:#0000ff"><<span style="color:#0000ff">p</span>></span>Company: helenlyn Co., Ltd. <span style="color:#0000ff"></<span style="color:#0000ff">p</span>></span> * * @author brand * @date 2021/5/16 16:37 * <span style="color:#0000ff"><<span style="color:#0000ff">p</span>></span>Update Time: <span style="color:#0000ff"></<span style="color:#0000ff">p</span>></span> * <span style="color:#0000ff"><<span style="color:#0000ff">p</span>></span>Updater: <span style="color:#0000ff"></<span style="color:#0000ff">p</span>></span> * <span style="color:#0000ff"><<span style="color:#0000ff">p</span>></span>Update Comments: <span style="color:#0000ff"></<span style="color:#0000ff">p</span>></span> */ public class FruitInfoUtil { public static String getFruitInfo(Class<span style="color:#2b91af"><?</span>> clazz) { String strFruitName = <span style="color:#a31515">" 水果名称:"</span>; String strFruitColor = <span style="color:#a31515">" 水果颜色:"</span>; String strFruitProvicer = <span style="color:#a31515">"供应商信息:"</span>; Field[] fields = clazz.<span style="color:#a31515">getDeclaredFields</span>(); <span style="color:#0000ff">for</span> (Field field : fields) { <span style="color:#0000ff">if</span> (field.<span style="color:#a31515">isAnnotationPresent</span>(FruitName.<span style="color:#0000ff">class</span>)) { FruitName fruitName = (FruitName) field.<span style="color:#a31515">getAnnotation</span>(FruitName.<span style="color:#0000ff">class</span>); strFruitName += fruitName.<span style="color:#a31515">value</span>(); System.out.<span style="color:#a31515">println</span>(strFruitName); } <span style="color:#0000ff">else</span> <span style="color:#0000ff">if</span> (field.<span style="color:#a31515">isAnnotationPresent</span>(FruitColor.<span style="color:#0000ff">class</span>)) { FruitColor fruitColor = (FruitColor) field.<span style="color:#a31515">getAnnotation</span>(FruitColor.<span style="color:#0000ff">class</span>); strFruitColor += fruitColor.<span style="color:#a31515">fruitColor</span>().<span style="color:#a31515">toString</span>(); System.out.<span style="color:#a31515">println</span>(strFruitColor); } <span style="color:#0000ff">else</span> <span style="color:#0000ff">if</span> (field.<span style="color:#a31515">isAnnotationPresent</span>(FruitProvider.<span style="color:#0000ff">class</span>)) { FruitProvider fruitProvider = (FruitProvider) field.<span style="color:#a31515">getAnnotation</span>(FruitProvider.<span style="color:#0000ff">class</span>); strFruitProvicer = <span style="color:#a31515">" 供应商编号:"</span> + fruitProvider.<span style="color:#a31515">id</span>() + <span style="color:#a31515">" 供应商名称:"</span> + fruitProvider.<span style="color:#a31515">name</span>() + <span style="color:#a31515">" 供应商地址:"</span> + fruitProvider.<span style="color:#a31515">address</span>(); System.out.<span style="color:#a31515">println</span>(strFruitProvicer); } } <span style="color:#0000ff">return</span> String.<span style="color:#a31515">format</span>(<span style="color:#a31515">"%s;%s;%s;"</span>, strFruitName, strFruitColor, strFruitProvicer); } } </code></span></span>
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-sql"><span style="color:#880000">2022</span><span style="color:#880000">-07</span><span style="color:#880000">-09</span> <span style="color:#880000">11</span>:<span style="color:#880000">33</span>:<span style="color:#880000">41.688</span> INFO <span style="color:#880000">5895</span> <span style="color:#008000">--- [TaskExecutor-35] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: cl-debug-rabbitmq-erp-service-7w0cpa.docker.sdp:9146</span>
- Hibernate: <span style="color:#0000ff">update</span> UserBasicInfo <span style="color:#0000ff">set</span> personName<span style="color:#ab5656">=</span>? <span style="color:#0000ff">where</span> personCode<span style="color:#ab5656">=</span>?
- 水果名称:Apple
- 水果颜色:RED
- 供应商编号:<span style="color:#880000">1</span> 供应商名称:helenlyn 贸易公司 供应商地址:福州xx路xxx大楼
- </code></span></span>
ElementType.TYPE_USE(此类型包括类型声明和类型参数声明,是为了方便设计者进行类型检查)包含了ElementType.TYPE(类、接口(包括注解类型)和枚举的声明)和ElementType.TYPE_PARAMETER(类型参数声明), 可以看下面这个例子:
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#008000">// 自定义ElementType.TYPE_PARAMETER注解</span> <span style="color:#2b91af">@Retention(RetentionPolicy.RUNTIME)</span> <span style="color:#2b91af">@Target(ElementType.TYPE_PARAMETER)</span> <span style="color:#0000ff">public</span> <span style="color:#2b91af">@interface</span> MyNotEmpty { } <span style="color:#008000">// 自定义ElementType.TYPE_USE注解</span> <span style="color:#2b91af">@Retention(RetentionPolicy.RUNTIME)</span> <span style="color:#2b91af">@Target(ElementType.TYPE_USE)</span> <span style="color:#0000ff">public</span> <span style="color:#2b91af">@interface</span> MyNotNull { } <span style="color:#008000">// 测试类</span> <span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> <span style="color:#a31515">TypeParameterAndTypeUseAnnotation</span><<span style="color:#2b91af">@MyNotEmpty</span> T>{ <span style="color:#008000">//使用TYPE_PARAMETER类型,会编译不通过</span> <span style="color:#008000">// public @MyNotEmpty T test(@MyNotEmpty T a){</span> <span style="color:#008000">// new ArrayList<@MyNotEmpty String>();</span> <span style="color:#008000">// return a;</span> <span style="color:#008000">// }</span> <span style="color:#008000">//使用TYPE_USE类型,编译通过</span> <span style="color:#0000ff">public</span> <span style="color:#2b91af">@MyNotNull</span> T <span style="color:#a31515">test2</span>(<span style="color:#2b91af">@MyNotNull</span> T a){ <span style="color:#0000ff">new</span> <span style="color:#a31515">ArrayList</span><<span style="color:#2b91af">@MyNotNull</span> String>(); <span style="color:#0000ff">return</span> a; } } </code></span></span>
注解是不支持继承的
不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口。 虽然反编译后发现注解继承了Annotation接口,请记住,即使Java的接口可以实现多继承,但定义注解时依然无法使用extends关键字继承@interface。 区别于注解的继承,被注解的子类继承父类注解可以用@Inherited: 如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。
笔者曾经在 《基于AOP的动态数据源切换》 这篇文章中有个典型的例子,就是使用AOP切面来对多数据源进行使用场景的切换,下面展示下如何通过注解实现解耦的。
- <span style="color:#000000"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#008000">/**
- * <span style="color:#808080">@author</span> brand
- * <span style="color:#808080">@Description</span>: 数据源切换注解
- * <span style="color:#808080">@Copyright</span>: Copyright (c) 2021
- * <span style="color:#808080">@Company</span>: Helenlyn, Inc. All Rights Reserved.
- * <span style="color:#808080">@date</span> 2021/12/15 7:36 下午
- */</span>
- <span style="color:#2b91af">@Target({ ElementType.TYPE, ElementType.METHOD})</span>
- <span style="color:#2b91af">@Retention(RetentionPolicy.RUNTIME)</span>
- <span style="color:#2b91af">@Documented</span>
- <span style="color:#0000ff">public</span> <span style="color:#2b91af">@interface</span> DataSource {
- String <span style="color:#a31515">name</span>() <span style="color:#0000ff">default</span> <span style="color:#a31515">""</span>;
- }
- </code></span></span>
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#008000">/** * <span style="color:#808080">@author</span> brand * <span style="color:#808080">@Description</span>: * <span style="color:#808080">@Copyright</span>: Copyright (c) 2021 * <span style="color:#808080">@Company</span>: Helenlyn, Inc. All Rights Reserved. * <span style="color:#808080">@date</span> 2021/12/15 7:49 下午 */</span> <span style="color:#2b91af">@Aspect</span> <span style="color:#2b91af">@Component</span> <span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> <span style="color:#a31515">DataSourceAspect</span> <span style="color:#0000ff">implements</span> <span style="color:#a31515">Ordered</span> { <span style="color:#008000">/** * 定义一个切入点,匹配到上面的注解DataSource */</span> <span style="color:#2b91af">@Pointcut("@annotation(com.helenlyn.dataassist.annotation.DataSource)")</span> <span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">dataSourcePointCut</span>() { } <span style="color:#008000">/** * Around 环绕方式做切面注入 * <span style="color:#808080">@param</span> point * <span style="color:#808080">@return</span> * <span style="color:#808080">@throws</span> Throwable */</span> <span style="color:#2b91af">@Around("dataSourcePointCut()")</span> <span style="color:#0000ff">public</span> Object <span style="color:#a31515">around</span>(ProceedingJoinPoint point) <span style="color:#0000ff">throws</span> Throwable { <span style="color:#a31515">MethodSignature</span> <span style="color:#008000">signature</span> <span style="color:#ab5656">=</span> (MethodSignature) point.getSignature(); <span style="color:#a31515">Method</span> <span style="color:#008000">method</span> <span style="color:#ab5656">=</span> signature.getMethod(); <span style="color:#a31515">DataSource</span> <span style="color:#008000">ds</span> <span style="color:#ab5656">=</span> method.getAnnotation(DataSource.class); <span style="color:#a31515">String</span> <span style="color:#008000">routeKey</span> <span style="color:#ab5656">=</span> ds.name(); <span style="color:#008000">// 从头部中取出注解的name(basic 或 cloudoffice 或 attend),用这个name进行数据源查找。</span> <span style="color:#a31515">String</span> <span style="color:#008000">dataSourceRouteKey</span> <span style="color:#ab5656">=</span> DynamicDataSourceRouteHolder.getDataSourceRouteKey(); <span style="color:#0000ff">if</span> (StringUtils.isNotEmpty(dataSourceRouteKey)) { <span style="color:#008000">// StringBuilder currentRouteKey = new StringBuilder(dataSourceRouteKey);</span> routeKey = ds.name(); } DynamicDataSourceRouteHolder.setDataSourceRouteKey(routeKey); <span style="color:#0000ff">try</span> { <span style="color:#0000ff">return</span> point.proceed(); } <span style="color:#0000ff">finally</span> { <span style="color:#008000">// 最后做清理,这个步骤很重要,因为我们的配置中有一个默认的数据源,执行完要回到默认的数据源。</span> DynamicDataSource.clearDataSource(); DynamicDataSourceRouteHolder.clearDataSourceRouteKey(); } } <span style="color:#2b91af">@Override</span> <span style="color:#0000ff">public</span> <span style="color:#a31515">int</span> <span style="color:#a31515">getOrder</span>() { <span style="color:#0000ff">return</span> <span style="color:#880000">1</span>; } } </code></span></span>
<span style="color:#000000"><span style="background-color:#ffffff"><code class="language-less"><span style="color:#008000">/** * 无注解默认情况:数据源指向basic * @return */</span> <span style="color:#008000">@RequestMapping</span>(value = <span style="color:#a31515">"/default/{user_code}"</span>, method = RequestMethod.GET) public UserInfoDto getUserInfo(<span style="color:#008000">@PathVariable</span>(<span style="color:#a31515">"user_code"</span>) String userCode) { <span style="color:#0000ff">return</span> <span style="color:#0000ff">userInfoService</span><span style="color:#880000">.getUserInfo</span>(userCode); } <span style="color:#008000">/** * 数据源指向attend * @return */</span> @<span style="color:#0000ff">DataSource</span>(name= Constant.DATA_SOURCE_ATTEND_NAME) @<span style="color:#0000ff">RequestMapping</span>(value = <span style="color:#a31515">"/attend/{user_code}"</span>, method = RequestMethod.GET) <span style="color:#0000ff">public</span> <span style="color:#0000ff">UserInfoDto</span> <span style="color:#0000ff">getUserInfoAttend</span>(<span style="color:#008000">@PathVariable</span>(<span style="color:#a31515">"user_code"</span>) String userCode) { <span style="color:#0000ff">return</span> <span style="color:#0000ff">userInfoService</span><span style="color:#880000">.getUserInfo</span>(userCode); } <span style="color:#008000">/** * 数据源指向cloud * @return */</span> @<span style="color:#0000ff">DataSource</span>(name= Constant.DATA_SOURCE_CLOUD_NAME) @<span style="color:#0000ff">RequestMapping</span>(value = <span style="color:#a31515">"/cloud/{user_code}"</span>, method = RequestMethod.GET) <span style="color:#0000ff">public</span> <span style="color:#0000ff">UserInfoDto</span> <span style="color:#0000ff">getUserInfoCloud</span>(<span style="color:#008000">@PathVariable</span>(<span style="color:#a31515">"user_code"</span>) String userCode) { <span style="color:#0000ff">return</span> <span style="color:#0000ff">userInfoService</span><span style="color:#880000">.getUserInfo</span>(userCode); } </code></span></span>
除此之外,我们可以看到很多日志管理、权限管理,也都是也是通过类似的注解机制来实现的,通过注解+AOP来最终实现模块之间的解耦,以及业务与系统层面的解耦 。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。