首先我们要知道背景知识:
废话不说上我自己定义的代码,然后一个一个说明。
package com.springtest.demo.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Fruit {
String value() default "";
String name() default "";
enum FruitType {
APPLE, PEACH, PEAR, WATERMELON
}
FruitType type() default FruitType.APPLE;
}
package com.springtest.demo.entity.fruit;
import com.springtest.demo.annotation.Fruit;
import com.springtest.demo.annotation.Scope;
@Scope(Scope.SCOPE_PROTOTYPE)
public class Pear {
@Fruit(value = "pear")
private String name;
@Fruit(type = Fruit.FruitType.PEAR)
private String type;
}
package com.springtest.demo.entity.fruit;
import com.springtest.demo.annotation.Fruit;
import com.springtest.demo.annotation.Scope;
@Scope(Scope.SCOPE_PROTOTYPE)
public class Apple {
@Fruit(value = "apple")
private String name;
@Fruit(type = Fruit.FruitType.APPLE)
private String type;
}
以上就是我做的最简单的demo,定义并应用了一个注解。我们来看看Fruit注解反编译的结果是什么就能大概知道这故事背后的作用。
public interface Fruit
extends Annotation
{
public static final class FruitType extends Enum
{
public static final FruitType APPLE;
public static final FruitType PEACH;
public static final FruitType PEAR;
public static final FruitType WATERMELON;
private static final FruitType $VALUES[];
public static FruitType[] values()
{
return (FruitType[])$VALUES.clone();
}
public static FruitType valueOf(String name)
{
return (FruitType)Enum.valueOf(com/springtest/demo/annotation/Fruit$FruitType, name);
}
static
{
APPLE = new FruitType("APPLE", 0);
PEACH = new FruitType("PEACH", 1);
PEAR = new FruitType("PEAR", 2);
WATERMELON = new FruitType("WATERMELON", 3);
$VALUES = (new FruitType[] {
APPLE, PEACH, PEAR, WATERMELON
});
}
private FruitType(String s, int i)
{
super(s, i);
}
}
public abstract String value();
public abstract String name();
public abstract FruitType type();
}
查看代码我们发现所谓注解的本质还是很简单的其实就是一个继承了Annotation的接口,然后内部定义了一些默认的抽象类而已。
由于因为我们知道其实一个注解本质上就只是一个标记,这个标记要怎么使用,什么时候使用是我们的编译器和jvm决定的,也就意味着一个注解通常会有一个目的,或者我们叫作用域。通常分为三类:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
/**
* Annotation retention policy. The constants of this enumerated type
* describe the various policies for retaining annotations. They are used
* in conjunction with the {@link Retention} meta-annotation type to specify
* how long annotations are to be retained.
*
* @author Joshua Bloch
* @since 1.5
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
同时由于我们的注解是可能被写在各种地方的,因此我们需要定义我们这个参数的作用域。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
最后就是一个是否需要被Javadoc记录的标记位@Documented。
基于遇上三点,也就是为什么我们常见的注解,头上都会有这三个标记的原因。因此,我们再手动实现另一个注解,可以再理解一下:
package com.springtest.demo.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
String SCOPE_SINGLETON = "singleton";
String SCOPE_PROTOTYPE = "prototype";
// 默认是单例
String value() default Scope.SCOPE_SINGLETON;
}
看看上面这个注解,是不是很眼熟?~~
没错,就是我们在Spring中常用的@Scope
注解的照搬版~试试分析看这个注解是怎么表达的。
接下来开始进行注解的解析,这里因为我们直接定义成运行时,所以可以在运行中通过类的反射机制,找到我们这个注解对应的作用域,如果被我们注解了,并且注解内的条件达到了,我们就对这个作用域内的对象进行某些我们想要的操作就行了。下面附上我的源代码以及编译器编译后的class的反编译结果。
package com.springtest.demo.annotation;
import com.springtest.demo.config.YunyaoBeanPostProcessor;
import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
/**
* 注解驱动
*/
@Scope(Scope.SCOPE_PROTOTYPE)
public class FruitInfoUtil {
public static void getFruitInfo(Object obj) {
String strFruitName = " 水果名称:";
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Fruit.class)) {
Fruit fruit = field.getAnnotation(Fruit.class);
strFruitName = strFruitName + fruit.value() + fruit.type().name();
System.out.println(strFruitName);
}
}
}
// 注:同一个对象,被实力出多个不同value的单例时,PostConstruct只会被执行一次
@PostConstruct
public void postConstruct() {
System.out.println("PostConstruct被执行..." + this.getClass().getName());
}
}
public static void getFruitInfo(Object obj)
{
String strFruitName = " \u6C34\u679C\u540D\u79F0\uFF1A";
Field fields[] = obj.getClass().getDeclaredFields();
Field afield[] = fields;
int i = afield.length;
for(int j = 0; j < i; j++)
{
Field field = afield[j];
if(field.isAnnotationPresent(com/springtest/demo/annotation/Fruit))
{
Fruit fruit = (Fruit)field.getAnnotation(com/springtest/demo/annotation/Fruit);
strFruitName = (new StringBuilder()).append(strFruitName).append(fruit.value()).append(fruit.type().name()).toString();
System.out.println(strFruitName);
}
}
}
public void postConstruct()
{
System.out.println((new StringBuilder()).append("PostConstruct\u88AB\u6267\u884C...").append(getClass().getName()).toString());
}
其实说到最后,注解真的很容易,而且很简单易用,只要我们搞清楚: