JDK:注解(Annotation)

1.Annotation(注解)

注解在jdk1.5被引入,可以为类、字段和方法提供自定义的元数据(一种描述数据)标注。

举例,@Override用于标注子类的重写方法:

@Override

public String toString() {

    return "This is String Representation of current object.";

}

 

 

2.注解的使用

2.1 自定义注解的创建

自定义注解需要通过@interface进行声明(其实质是extends Annotation接口), like:

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.SOURCE)

public @interface Override {

}

 

2.2 注解的无参方法

在自定义的注解中可以声明无参方法,但方法的返回类型被限定为以下3类:

  • 基本类型
  • String
  • enum类型

在jdk1.8提供default关键字后,可以为声明方法提供默认返回值。

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.SOURCE)

public @interface MyAnnotation {

 

    String name() default "";

 

    int value() default 0;

}

 

2.3 元注解

JDK的元注解用于描述自定义注解的使用范围,是否被继承,生命周期等特性,包括以下4种:

  • @Target:注解的使用范围
  • @Retention: 注解的生命周期
  • @Inherited:注解的继承特性
  • @Documented:注解是否将包含在JavaDoc中

2.3.1 @Target

@Target用于描述自定义注解的使用范围,通过enum类型ElementType给定:

public enum ElementType {

    /** 声明在Class, interface (包括annotation), 或enum类型 */

    TYPE,

 

    /** 声明在字段 */

    FIELD,

 

    /** 声明在方法 */

    METHOD,

 

    /** 声明在方法参数 */

    PARAMETER,

 

    /** 声明在构造函数 */

    CONSTRUCTOR,

 

    /** 声明在局部变量 */

    LOCAL_VARIABLE,

 

    /** 声明在注解类型 */

    ANNOTATION_TYPE,

 

    /** 声明在包上 */

    PACKAGE,

 

    /**

     * Type parameter declaration

     * @since 1.8

     */

    TYPE_PARAMETER,

 

    /**

     * Use of a type

     * @since 1.8

     */

    TYPE_USE

}

若不指定自定义注解的使用范围,则默认可以使用在任何地方。

 

2.3.2 @Retention

@Retention用于描述自定义注解的生命周期,由enum类型RetentionPolicy指定:

public enum RetentionPolicy {

    /**

     * 编译时有效,编译后不保存在class文件中

     */

    SOURCE,

 

    /**

     * 保存在class文件中,但JVM运行时不保留为字节码(默认生命周期)。

     */

    CLASS,

 

    /**

     * 保存在class文件中,并在JVM运行时保留为字节码,通过反射机制方式获取。

     */

    RUNTIME

}

由于生命周期长度 RUNTIME > CLASS > SOURCE,所以前者的作用范围一定大于后者。

如果需要在运行时利用反射机制获取注解,用 RUNTIME;

如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS;

如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE。

 

2.3.3 @Inherited

@Inherited用于描述自定义注解的继承特性(是否可以被子类继承)

JDK:注解(Annotation)

 

2.4 获取类、字段、方法和方法参数上的注解

 

 

  • Method类还提供了Annotation[][] getParameterAnnotations()方法用于获取方法参数上的注解。
  • Class类、Method类和Field类都分别提供了getAnnotation(Class<T> annotationClass)方法和getAnnotations方法用于获取注解;
  • JVM运行时希望获得类、字段、方法和方法参数上的注解,该注解必须是@Retention(RetentionPolicy.RUNTIME)的;

 

2.6 使用实例

在Spring框架中,自定义注解与动态代理有着大量结合使用的案例;

以下代码是利用自定义注解,简单实现了Spring IOC容器的bean扫描、创建和注册过程,并提供了方法的事务注解:

  • 创建自定义注解@Service,用于bean的扫描发现
  • 创建自定义注解@Transactional,用于bean的动态代理实现
  • 创建TransactionProxy,动态代理实现类;
  • 相关测试代码

 

/**

 * 模拟服务注解

 *

 * @author jiangshenjie

 */

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface Service {

    String name() default "";

}

 

/**

 * 模拟事务注解

 * @author jiangshenjie

 */

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Transactional {

 

    int id() default 0;

}

 

/**

 * @author jiangshenjie

 * "@Transactional" 的动态代理实现类

 */

public class TransactionProxy<T> implements InvocationHandler {

    private final T object;

 

    public TransactionProxy(T object) {

        this.object = object;

    }

 

    /**

     * 创建动态代理类

     * @param object

     * @param <T>

     * @return

     */

    public static <T> T bind(T object) {

        Class<?> clazz = object.getClass();

        return (T) Proxy.newProxyInstance(clazz.getClassLoader()

                , clazz.getInterfaces(), new TransactionProxy<>(object));

    }

 

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Class<?> clazz = this.object.getClass();

        Class<?>[] argClasses = null;

        if (args != null) {

            argClasses = new Class[args.length];

            for (int i = 0; i <= args.length; i++) {

                argClasses[i] = args[i].getClass();

            }

        }

        Method implementClassMethod = clazz.getMethod(method.getName(), argClasses);

        Transactional transactional = implementClassMethod.getAnnotation(Transactional.class);

        Object result;

        //若未修饰@Transactional注解,则执行原来的调用逻辑

        if (transactional == null) {

            result = method.invoke(object, args);

            return result;

        }

        //执行代理逻辑

        System.out.printf("Transaction id:[%d] start.\n", transactional.id());

        result = method.invoke(object, args);

        System.out.printf("Transaction id:[%d] end.\n", transactional.id());

        return result;

    }

}

 

/**

 * 模拟service接口

 *

 * @author jiangshenjie

 */

public interface BasicService {

    /**

     * 模拟service执行

     */

    public void doService();

}

 

/**

 * 模拟的service bean

 *

 * @author jiangshenjie

 */

@Service

public class BasicServiceImpl implements BasicService {

    @Override

    @Transactional(id = 1)

    public void doService() {

        System.out.println("Provide basic service.");

    }

}

 

/**

 * 利用自定义注解,简单模仿Spring IOC容器的bean创建和注册的过程

 *

 * @author jiangshenjie

 */

public class AnnotationDemo {

    private static Map<Class<?>, Object> applicationContext = new HashMap<>(8);

 

    public AnnotationDemo() {

    }

 

    public static void main(String[] args) {

        String rootPath = AnnotationDemo.class.getResource("").getPath();

        String packageName = AnnotationDemo.class.getPackage().getName();

        //扫描并创建bean

        autoWired(rootPath, packageName);

        //查询对应类型的bean

        BasicService service = getBean(BasicService.class);

        //调用方法

        service.doService();

    }

 

    /**

     * 获取已注册的bean

     *

     * @param clz

     * @param <T>

     * @return

     */

    private static <T> T getBean(Class<T> clz) {

        return (T) applicationContext.get(clz);

    }

 

    /**

     * 根据@Service注解扫描包,并创建和注册对应bean

     */

    private static void autoWired(String rootPath, String scanPackageName) {

        File file = new File(rootPath);

        String[] paths = file.list();

        if (paths == null) {

            return;

        }

        Arrays.stream(paths)

                .filter(path -> path.endsWith(".class"))

                .forEach((path) -> {

                    String className = String.format("%s.%s", scanPackageName, path.substring(0, path.indexOf(".class")));

                    try {

                        Class<?> clz = Class.forName(className);

                        Service serviceAnnotation = clz.getAnnotation(Service.class);

                        if (serviceAnnotation != null) {

                            //生成动态代理类并注册

                            applicationContext.put(clz.getInterfaces()[0], proxy(clz.newInstance()));

                        }

                    } catch (ClassNotFoundException c) {

                        c.printStackTrace();

                    }

                });

    }

 

    public static <T> T proxy(T object) {

        return TransactionProxy.bind(object);

    }

}

 

测试结果:

Transaction id:[1] start.

Provide basic service.

Transaction id:[1] end.

完成了bean的扫描和创建注册,并实现了方法的事务动态代理。