• SpringMVC系列(七)之自定义注解


    目录

    一. Java注解简介

    1.1 Java注解分类

    1.2 JDK基本注解

    @Override

    @Deprecated

    @SuppressWarnings

    1.3 JDK元注解

    从 Java 7 开始,额外添加了 3 个注解:

    1.4 自定义注解

    如何自定义注解?

    二. 自定义注解示例

    枚举类:

    示例一:获取类与方法上的注解值

    示例二:获取到类属性上的注解属性值

    示例三:获取参数修饰注解对应的属性值

     三. aop自定义注解的应用

    MyLog自定义注解类

    MyLogAspect切面类

    controller层


    一. Java注解简介

    注解是JDK1.5版本开始引入的一个特性,是附加在代码中的一些元信息,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。注解相关类都包含在java.lang.annotation包中。它主要的作用有以下四方面:

    •  生成文档,通过代码里标识的元数据生成javadoc文档。
    •  编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
    •  编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
    •  运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例。

    1.1 Java注解分类

    • JDK基本注解
    • JDK元注解
    • 自定义注解

    1.2 JDK基本注解

    @Override

    检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。

    @Deprecated

    标记过时方法。如果使用该方法,会报编译警告。 这个不是报错,只是警告,提醒我们这个方法可能会有问题,可能有更好的方法来实现!

    @SuppressWarnings

    指示编译器去忽略注解中声明的警告,其参数有:

    deprecation,使用了过时的类或方法时的警告

    unchecked,执行了未检查的转换时的警告

    fallthrough,当 switch 程序块直接通往下一种情况而没有 break 时的警告

    path,在类路径、源文件路径等中有不存在的路径时的警告

    serial,当在可序列化的类上缺少serialVersionUID 定义时的警告

    finally ,任何 finally 子句不能正常完成时的警告

    all,关于以上所有情况的警告

    1.3 JDK元注解

    用于修饰注解的注解,称为元注解,需要注意,元注解只能修饰注解,不能修饰成员变量、方法、类。

    • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
    • @Documented - 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档
    • @Target - 标记这个注解应该是哪种 Java 成员。
    • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
    1. @Retention:定义注解的保留策略
    2. @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
    3. @Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
    4. @Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
    5. @Target:指定被修饰的Annotation可以放置的位置(被修饰的目标)
    6. @Target(ElementType.TYPE) //接口、类
    7. @Target(ElementType.FIELD) //属性
    8. @Target(ElementType.METHOD) //方法
    9. @Target(ElementType.PARAMETER) //方法参数
    10. @Target(ElementType.CONSTRUCTOR) //构造函数
    11. @Target(ElementType.LOCAL_VARIABLE) //局部变量
    12. @Target(ElementType.ANNOTATION_TYPE) //注解
    13. @Target(ElementType.PACKAGE) //包
    14. 注:可以指定多个位置,例如:
    15. @Target({ElementType.METHOD, ElementType.TYPE}),也就是此注解可以在方法和类上面使用
    16. @Inherited:指定被修饰的Annotation将具有继承性
    17. @Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.

    从 Java 7 开始,额外添加了 3 个注解:

    • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
    • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
    • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

    1.4 自定义注解

    注解分类(根据Annotation是否包含成员变量,可以把Annotation分为两类):

    标记Annotation:
    没有成员变量的Annotation; 这种Annotation仅利用自身的存在与否来提供信息

    元数据Annotation:
    包含成员变量的Annotation; 它们可以接受(和提供)更多的元数据;

    如何自定义注解?

    使用@interface关键字, 其定义过程与定义接口非常类似, 需要注意的是:
       Annotation的成员变量在Annotation定义中是以无参的方法形式来声明的, 其方法名和返回值类型定义了该成员变量的名字和类型,
       而且我们还可以使用default关键字为这个成员变量设定默认值;

    二. 自定义注解示例

    枚举类:

    1. public enum TranscationModel {
    2. Read, Write, ReadWrite
    3. }

    枚举是Java1.5引入的新特性,通过关键字enum来定义枚举类。枚举类是一种特殊类,它和普通类一样可以使用构造器、定义成员变量和方法,也能实现一个或多个接口,但枚举类不能继承其他类。 

    示例一:获取类与方法上的注解值

    MyAnnotation1自定义注解类

    1. package com.xissl.annotation.demo1;
    2. import java.lang.annotation.*;
    3. /**
    4. * MyAnnotation1注解可以用在类、接口、属性、方法上
    5. * 注解运行期也保留
    6. * 不可继承
    7. */
    8. @Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
    9. @Retention(RetentionPolicy.RUNTIME)
    10. @Documented
    11. public @interface MyAnnotation1 {
    12. String name();
    13. }

    MyAnnotation2自定义注解类 

    1. package com.xissl.annotation.demo1;
    2. import java.lang.annotation.*;
    3. /**
    4. * MyAnnotation2注解可以用在方法上
    5. * 注解运行期也保留
    6. * 不可继承
    7. */
    8. @Target(ElementType.METHOD)
    9. @Retention(RetentionPolicy.RUNTIME)
    10. @Documented
    11. public @interface MyAnnotation2 {
    12. TranscationModel model() default TranscationModel.ReadWrite;
    13. }

    MyAnnotation3自定义注解类  

    1. package com.xissl.annotation.demo1;
    2. import java.lang.annotation.*;
    3. /**
    4. * MyAnnotation3注解可以用在方法上
    5. * 注解运行期也保留
    6. * 可继承
    7. */
    8. @Target(ElementType.METHOD)
    9. @Retention(RetentionPolicy.RUNTIME)
    10. @Inherited
    11. @Documented
    12. public @interface MyAnnotation3 {
    13. TranscationModel[] models() default TranscationModel.ReadWrite;
    14. }

    测试代码:

    1. package com.xissl.annotation.demo1;
    2. /**
    3. * 获取类与方法上的注解值
    4. */
    5. @MyAnnotation1(name = "abc")
    6. public class Demo1 {
    7. @MyAnnotation1(name = "xyz")
    8. private Integer age;
    9. @MyAnnotation2(model = TranscationModel.Read)
    10. public void list() {
    11. System.out.println("list");
    12. }
    13. @MyAnnotation3(models = {TranscationModel.Read, TranscationModel.Write})
    14. public void edit() {
    15. System.out.println("edit");
    16. }
    17. }
    1. package com.xissl.annotation.demo1;
    2. import org.junit.Test;
    3. public class Demo1Test {
    4. @Test
    5. public void list() throws Exception {
    6. // 获取类上的注解
    7. MyAnnotation1 annotation1 = Demo1.class.getAnnotation(MyAnnotation1.class);
    8. System.out.println(annotation1.name());//abc
    9. // 获取方法上的注解
    10. MyAnnotation2 myAnnotation2 = Demo1.class.getMethod("list").getAnnotation(MyAnnotation2.class);
    11. System.out.println(myAnnotation2.model());//Read
    12. // 获取属性上的注解
    13. MyAnnotation1 myAnnotation1 = Demo1.class.getDeclaredField("age").getAnnotation(MyAnnotation1.class);
    14. System.out.println(myAnnotation1.name());// xyz
    15. }
    16. @Test
    17. public void edit() throws Exception {
    18. MyAnnotation3 myAnnotation3 = Demo1.class.getMethod("edit").getAnnotation(MyAnnotation3.class);
    19. for (TranscationModel model : myAnnotation3.models()) {
    20. System.out.println(model);//Read,Write
    21. }
    22. }
    23. }

    list()方法测试效果:
     

    edit()方法测试效果:

     

     

    示例二:获取到类属性上的注解属性值

    TestAnnotation自定义注解类

    1. package com.xissl.annotation.demo2;
    2. import java.lang.annotation.ElementType;
    3. import java.lang.annotation.Retention;
    4. import java.lang.annotation.RetentionPolicy;
    5. import java.lang.annotation.Target;
    6. //@Retention(RetentionPolicy.SOURCE)
    7. @Retention(RetentionPolicy.RUNTIME)
    8. @Target(ElementType.FIELD)
    9. public @interface TestAnnotation {
    10. String value() default "默认value值";
    11. String what() default "这里是默认的what属性对应的值";
    12. }

    测试代码:

    1. package com.xissl.annotation.demo2;
    2. /**
    3. * 获取类属性上的注解属性值
    4. */
    5. public class Demo2 {
    6. @TestAnnotation(value = "这就是value对应的值_msg1", what = "这就是what对应的值_msg1")
    7. private static String msg1;
    8. @TestAnnotation("这就是value对应的值1")
    9. private static String msg2;
    10. @TestAnnotation(value = "这就是value对应的值2")
    11. private static String msg3;
    12. @TestAnnotation(what = "这就是what对应的值")
    13. private static String msg4;
    14. }
    1. package com.xissl.annotation.demo2;
    2. import org.junit.Test;
    3. public class Demo2Test {
    4. @Test
    5. public void test1() throws Exception {
    6. TestAnnotation msg1 = Demo2.class.getDeclaredField("msg1").getAnnotation(TestAnnotation.class);
    7. System.out.println(msg1.value());
    8. System.out.println(msg1.what());
    9. }
    10. @Test
    11. public void test2() throws Exception{
    12. TestAnnotation msg2 = Demo2.class.getDeclaredField("msg2").getAnnotation(TestAnnotation.class);
    13. System.out.println(msg2.value());
    14. System.out.println(msg2.what());
    15. }
    16. @Test
    17. public void test3() throws Exception{
    18. TestAnnotation msg3 = Demo2.class.getDeclaredField("msg3").getAnnotation(TestAnnotation.class);
    19. System.out.println(msg3.value());
    20. System.out.println(msg3.what());
    21. }
    22. @Test
    23. public void test4() throws Exception{
    24. TestAnnotation msg4 = Demo2.class.getDeclaredField("msg4").getAnnotation(TestAnnotation.class);
    25. System.out.println(msg4.value());
    26. System.out.println(msg4.what());
    27. }
    28. }

     test1方法测试效果

     test2方法测试效果

     test3方法测试效果

     test4方法测试效果

    示例三:获取参数修饰注解对应的属性值

    IsNotNull自定义注解类

    1. package com.xissl.annotation.demo3;
    2. import java.lang.annotation.*;
    3. /**
    4. * 非空注解:使用在方法的参数上,false表示此参数可以为空,true不能为空
    5. */
    6. @Documented
    7. @Target({ElementType.PARAMETER})
    8. @Retention(RetentionPolicy.RUNTIME)
    9. public @interface IsNotNull {
    10. boolean value() default false;
    11. }

    测试代码:

    1. package com.xissl.annotation.demo3;
    2. /**
    3. * 获取参数修饰注解对应的属性值
    4. */
    5. public class Demo3 {
    6. public void hello1(@IsNotNull(true) String name) {
    7. System.out.println("hello:" + name);
    8. }
    9. public void hello2(@IsNotNull String name) {
    10. System.out.println("hello:" + name);
    11. }
    12. }
    1. package com.xissl.annotation.demo3;
    2. import org.junit.Test;
    3. import java.lang.reflect.Method;
    4. import java.lang.reflect.Parameter;
    5. /**
    6. * @author 小李飞刀
    7. * @site www.javaxl.com
    8. */
    9. public class Demo3Test {
    10. @Test
    11. public void hello1() throws Exception {
    12. Demo3 demo3 = new Demo3();
    13. for (Parameter parameter : demo3.getClass().getMethod("hello1", String.class).getParameters()) {
    14. IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
    15. if(annotation != null){
    16. System.out.println(annotation.value());//true
    17. }
    18. }
    19. }
    20. @Test
    21. public void hello2() throws Exception {
    22. Demo3 demo3 = new Demo3();
    23. for (Parameter parameter : demo3.getClass().getMethod("hello2", String.class).getParameters()) {
    24. IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
    25. if(annotation != null){
    26. System.out.println(annotation.value());//false
    27. }
    28. }
    29. }
    30. @Test
    31. public void hello3() throws Exception {
    32. // 模拟浏览器传递到后台的参数 解读@requestParam
    33. String name = "zs";
    34. Demo3 demo3 = new Demo3();
    35. Method method = demo3.getClass().getMethod("hello1", String.class);
    36. for (Parameter parameter : method.getParameters()) {
    37. IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
    38. if(annotation != null){
    39. System.out.println(annotation.value());//true
    40. if (annotation.value() && !"".equals(name)){
    41. method.invoke(demo3,name);
    42. }
    43. }
    44. }
    45. }
    46. }

    hello1方法测试效果:

    hello2方法测试效果:

    hello3方法测试效果:

     三. aop自定义注解的应用

    MyLog自定义注解类

    1. package com.xissl.annotation.aop;
    2. import java.lang.annotation.ElementType;
    3. import java.lang.annotation.Retention;
    4. import java.lang.annotation.RetentionPolicy;
    5. import java.lang.annotation.Target;
    6. @Target(ElementType.METHOD)
    7. @Retention(RetentionPolicy.RUNTIME)
    8. public @interface MyLog {
    9. String desc();
    10. }

    MyLogAspect切面类

     

    1. package com.xissl.aspect;
    2. import com.xissl.annotation.aop.MyLog;
    3. import org.aspectj.lang.JoinPoint;
    4. import org.aspectj.lang.annotation.Aspect;
    5. import org.aspectj.lang.annotation.Before;
    6. import org.aspectj.lang.annotation.Pointcut;
    7. import org.aspectj.lang.reflect.MethodSignature;
    8. import org.slf4j.Logger;
    9. import org.slf4j.LoggerFactory;
    10. import org.springframework.stereotype.Component;
    11. @Component
    12. @Aspect
    13. public class MyLogAspect {
    14. private static final Logger logger = LoggerFactory.getLogger(MyLogAspect.class);
    15. /**
    16. * 只要用到了com.javaxl.p2.annotation.springAop.MyLog这个注解的,就是目标类
    17. */
    18. @Pointcut("@annotation(com.xissl.annotation.aop.MyLog)")
    19. private void MyValid() {
    20. }
    21. @Before("MyValid()")
    22. public void before(JoinPoint joinPoint) {
    23. MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    24. logger.debug("[" + signature.getName() + " : start.....]");
    25. System.out.println("[" + signature.getName() + " : start.....]");
    26. MyLog myLog = signature.getMethod().getAnnotation(MyLog.class);
    27. logger.debug("【目标对象方法被调用时候产生的日志,记录到日志表中】:"+myLog.desc());
    28. System.out.println("【目标对象方法被调用时候产生的日志,记录到日志表中】:" + myLog.desc());
    29. }
    30. }

    controller层

    1. package com.xissl.web;
    2. import com.xissl.annotation.aop.MyLog;
    3. import org.springframework.stereotype.Component;
    4. import org.springframework.stereotype.Controller;
    5. import org.springframework.web.bind.annotation.RequestMapping;
    6. @Controller
    7. public class LogController {
    8. @RequestMapping("/mylog")
    9. @MyLog(desc = "这是结合spring aop知识,讲解自定义注解应用的一个案例")
    10. public void testLogAspect(){
    11. System.out.println("这里随便来点啥");
    12. }
    13. }

    测试结果:

  • 相关阅读:
    封装Detours用于Python中x64函数hook
    请问一下出现这种错误怎么解决呢(标签-xml)
    最大公约数与最小公倍数
    opengl 学习着色器
    从 Language Model 到 Chat Application:对话接口的设计与实现
    WebSocket
    Vue课程61-判断用户添加的数据是否为空
    FinalShell 远程连接 Linux(Ubuntu)系统
    Docker启动mysql服务
    vue2 render属性调用createElement()方法插入多级元素举例
  • 原文地址:https://blog.csdn.net/lijie1025/article/details/132887974