SpringAOP02 自定义注解
1 自定义注解
1.1 创建自定义注解
从java5开始就可以利用 @interface 来定义自定义注解
技巧01:注解不能直接干扰程序代码的运行(即:注解的增加和删除操作后,代码都可以正常运行)
技巧02:@Retention 用来声明注解的保留期限
/* * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package java.lang.annotation; /** * 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 }
技巧03:@Target 用来声明使用该注解的目标类型
/* * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package java.lang.annotation; /** * The constants of this enumerated type provide a simple classification of the * syntactic locations where annotations may appear in a Java program. These * constants are used in {@link Target java.lang.annotation.Target} * meta-annotations to specify where it is legal to write annotations of a * given type. * * <p>The syntactic locations where annotations may appear are split into * <em>declaration contexts</em> , where annotations apply to declarations, and * <em>type contexts</em> , where annotations apply to types used in * declarations and expressions. * * <p>The constants {@link #ANNOTATION_TYPE} , {@link #CONSTRUCTOR} , {@link * #FIELD} , {@link #LOCAL_VARIABLE} , {@link #METHOD} , {@link #PACKAGE} , * {@link #PARAMETER} , {@link #TYPE} , and {@link #TYPE_PARAMETER} correspond * to the declaration contexts in JLS 9.6.4.1. * * <p>For example, an annotation whose type is meta-annotated with * {@code @Target(ElementType.FIELD)} may only be written as a modifier for a * field declaration. * * <p>The constant {@link #TYPE_USE} corresponds to the 15 type contexts in JLS * 4.11, as well as to two declaration contexts: type declarations (including * annotation type declarations) and type parameter declarations. * * <p>For example, an annotation whose type is meta-annotated with * {@code @Target(ElementType.TYPE_USE)} may be written on the type of a field * (or within the type of the field, if it is a nested, parameterized, or array * type), and may also appear as a modifier for, say, a class declaration. * * <p>The {@code TYPE_USE} constant includes type declarations and type * parameter declarations as a convenience for designers of type checkers which * give semantics to annotation types. For example, if the annotation type * {@code NonNull} is meta-annotated with * {@code @Target(ElementType.TYPE_USE)}, then {@code @NonNull} * {@code class C {...}} could be treated by a type checker as indicating that * all variables of class {@code C} are non-null, while still allowing * variables of other classes to be non-null or not non-null based on whether * {@code @NonNull} appears at the variable's declaration. * * @author Joshua Bloch * @since 1.5 * @jls 9.6.4.1 @Target * @jls 4.1 The Kinds of Types and Values */ 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 }
坑01:自定义注解允许定义成员,但是这里的成员在进行声明时必须是无入参、无抛出异常的方式进行声明;而且成员只能是方法
坑02:在给成员指定默认值是必须使用default关键字
package cn.test.demo.base_demo.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NeedTest { boolean value() default true; }
1.2 使用注解
1.2.1 创建一个SrpingBoot项目
下载地址:点击前往
1.2.2 新建一个自定义注解类
package cn.test.demo.base_demo.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NeedTest { boolean value() default true; }
1.2.3 新建一个服务类
在该服务类的方法中使用刚刚创建的自定义注解
package cn.test.demo.base_demo.service; import cn.test.demo.base_demo.annotations.NeedTest; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * @author 王杨帅 * @create 2018-04-29 21:31 * @desc 学生服务类 **/ @Service @Slf4j public class StudentService { private final String className = getClass().getName(); @NeedTest() public void insert() { log.info("===/" + className + "/insert===新增操作"); } @NeedTest(value = false) public void delete() { log.info("===/" + className + "/delete===删除操作"); } }
1.2.4 访问注解
从Java5开始包、类、构造器、方法、字段等都反射对象都开始支持访问注解信息的方法
/** * {@inheritDoc} * @throws NullPointerException {@inheritDoc} * @since 1.5 */ public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { return super.getAnnotation(annotationClass); }
package cn.test.demo.base_demo.service; import cn.test.demo.base_demo.annotations.NeedTest; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.lang.reflect.Method; import static org.junit.Assert.*; @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class StudentServiceTest { private final String className = getClass().getName(); @Test public void test01() { Class cla = StudentService.class; Method[] methods = cla.getDeclaredMethods(); log.info("===/" + className + "/test01===方法数量为:{}", methods.length); for (Method method : methods) { NeedTest nt = method.getAnnotation(NeedTest.class); if (nt.value()) { log.info("===" + method.getName() + "()需要测试"); } else { log.info("===" + method.getName() + "()不需要进行测试"); } } } @Test public void insert() throws Exception { } @Test public void delete() throws Exception { } }