SpringMVC之自定义注解

目录

前言

一、自定义注解

1.Java注解简介

[2. 注解的用处](#2. 注解的用处)

[3. 为什么要用注解](#3. 为什么要用注解)

4.自定义注解的应用场景

[5. 注解的分类](#5. 注解的分类)

6.如何定义并使用自定义注解

7.自定义注解三种使用案例

案例一:

案例二:

案例三:

二、Aop自定义注解的应用

[1 .自定义注解类](#1 .自定义注解类)

2.切面类

3.Controller层


前言

随着Web开发的发展,越来越多的企业开始使用SpringMVC框架来构建自己的Web应用程序。在开发过程中,为了提高代码的可读性和可维护性,我们经常需要自定义注解。本文将介绍如何编写一个自定义注解,并通过一个简单的例子来演示如何使用它。

一、自定义注解

1.Java注解简介

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。 注解相关类都包含在java.lang.annotation包中。

2. 注解的用处

  • 配置和元数据:注解可以用于配置和描述类、方法、字段等元素的特性和行为。例如,我们可以使用注解来指定数据库表的名称、字段的长度、方法的访问权限等。

  • 编译时检查:注解可以用于在编译时对代码进行静态检查,以确保代码的正确性和合规性。例如,我们可以使用注解来标记过时的方法或类,编译器会在编译时给出警告。

  • 运行时处理:注解可以在运行时通过反射机制获取到,并根据注解的定义来执行相应的逻辑。例如,我们可以使用注解来实现自定义的AOP(面向切面编程)逻辑,或者在某些条件下动态地修改方法的行为。

  • 文档生成:注解可以用于生成文档,以便开发者更好地理解代码的含义和使用方式。例如,我们可以使用注解来标记API的使用示例、参数的说明等。

3. 为什么要用注解

  • 减少样板代码:通过使用注解,我们可以将一些重复性的代码抽象为注解,从而减少冗余的代码量。

  • 提高代码可读性:注解可以用于描述代码的特性和行为,使代码更加易于理解和阅读。

  • 增强代码的可维护性:通过使用注解,我们可以将一些与业务逻辑无关的代码与业务逻辑分离,使代码更加模块化和可维护。

  • 提高开发效率:使用注解可以简化一些繁琐的操作,提高开发效率。

4.自定义注解的应用场景

应用场景十分广泛,举例说明几个:

  • 在SpringMVC中,我们可以使用自定义注解来标记Controller中的方法,以实现请求的映射和处理。
  • 在MyBatis中,我们可以使用自定义注解来标记Mapper接口中的方法,以实现SQL语句的映射和执行。
  • 在JUnit中,我们可以使用自定义注解来标记测试方法,以实现自动化测试的执行。

5. 注解的分类

  • JDK元注解 :元注解是用于注解其他注解的注解,它可以用于指定注解的作用范围、生命周期等。常见的元注解有@Target@Retention@Documented等。

  • JDK基本注解 :标准注解是Java提供的一些常用注解,例如@Override@Deprecated@SuppressWarnings等。

  • 自定义注解:自定义注解是开发者根据自己的需求定义的注解,用于实现特定的功能或逻辑。

  • 框架注解 :框架注解是一些常用框架提供的注解,用于实现框架的特定功能。例如,Spring框架提供的@Autowired@RequestMapping等注解。

6.如何定义并使用自定义注解

自定义注解的定义非常简单,只需要使用**@interface**关键字即可。

package com.ctb.utils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
    String value() default "";
}

我们定义了一个名为CustomAnnotation的注解。该注解可以应用于方法上,并且在运行时保留。注解中还定义了一个名为value的属性,默认为空字符串。

使用自定义注解非常简单,只需要在需要标记的地方使用注解即可。

@CustomAnnotation("这是一个自定义注解示例")
public void doSomething() {
    // 执行一些操作
    
}

7.自定义注解三种使用案例

案例一:

**1. 定义一个枚举类:**用来表示一组固定的值。

package com.ctb.model;
 
public enum  TranscationModel {
    Read, Write, ReadWrite
}

2. 定义三个不同注解方式的类:

MyAnnotation1 :

package com.ctb.annotation;
 
import java.lang.annotation.*;
 
/**
 * MyAnnotation1注解可以用在类、接口、属性、方法上
 * 注解运行期也保留
 * 不可继承
 */
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation1 {
    String name();
}

MyAnnotation2 :

package com.ctb.annotation;
 
import com.ctb.model.TranscationModel;
 
import java.lang.annotation.*;
 
/**
 *  MyAnnotation2注解可以用在方法上
 *  注解运行期也保留
 *  不可继承
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation2 {
    TranscationModel model() default TranscationModel.ReadWrite;
}

MyAnnotation3 :

package com.ctb.annotation;
 
import com.ctb.model.TranscationModel;
 
import java.lang.annotation.*;
 
 
/**
 * MyAnnotation3注解可以用在方法上
 * 注解运行期也保留
 * 可继承
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation3 {
    TranscationModel[] models() default TranscationModel.ReadWrite;
}

**Demo1测试类 :**获取类与方法上的注解值

package com.ctb.annotation.demo;
 
import com.ctb.annotation.MyAnnotation1;
import com.ctb.annotation.MyAnnotation2;
import com.ctb.annotation.MyAnnotation3;
import com.ctb.model.TranscationModel;
 
/**
 * 获取类与方法上的注解值
 */
@MyAnnotation1(name = "abc")
public class Demo1 {
 
    @MyAnnotation1(name = "xyz")
    private Integer age;
 
    @MyAnnotation2(model = TranscationModel.Read)
    public void list() {
        System.out.println("list");
    }
 
    @MyAnnotation3(models = {TranscationModel.Read, TranscationModel.Write})
    public void edit() {
        System.out.println("edit");
    }
}

Demo1Test测试类:

package com.ctb.annotation.demo;
 
import com.ctb.annotation.MyAnnotation1;
import com.ctb.annotation.MyAnnotation2;
import com.ctb.annotation.MyAnnotation3;
import com.ctb.model.TranscationModel;
import org.junit.Test;
 
public class Demo1Test {
    @Test
    public void list() throws Exception {
//        获取类上的注解
        MyAnnotation1 annotation1 = Demo1.class.getAnnotation(MyAnnotation1.class);
        System.out.println(annotation1.name());//abc
 
//        获取方法上的注解
        MyAnnotation2 myAnnotation2 = Demo1.class.getMethod("list").getAnnotation(MyAnnotation2.class);
        System.out.println(myAnnotation2.model());//Read
 
//        获取属性上的注解
        MyAnnotation1 myAnnotation1 = Demo1.class.getDeclaredField("age").getAnnotation(MyAnnotation1.class);
        System.out.println(myAnnotation1.name());// xyz
    }
 
    @Test
    public void edit() throws Exception {
        MyAnnotation3 myAnnotation3 = Demo1.class.getMethod("edit").getAnnotation(MyAnnotation3.class);
        for (TranscationModel model : myAnnotation3.models()) {
            System.out.println(model);//Read,Write
        }
    }
}

@Ingerited的使用

@Inherited注解是Java中的元注解之一,用于控制注解的继承性。当一个类或接口被@Inherited注解修饰时,它的子类将自动获得该注解。但需要注意的是,@Inherited只对类和接口有效,对方法、字段等无效。

普通的类继承是指子类可以继承父类的所有非private的成员变量和方法,包括构造器。子类可以使用父类中的方法和属性,也可以重写父类的方法,在其基础上进行扩展或修改。

执行list方法测试结果

执行edit方法测试结果

案例二:

1. 自定义注解类:

package com.ctb.annotation;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
//@Retention(RetentionPolicy.SOURCE)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TestAnnotation {
    String value() default "默认value值";
 
    String what() default "这里是默认的what属性对应的值";
}

2. 定义测试类:获取类属性上的注解属性值

package com.ctb.annotation.demo2;
 
import com.ctb.annotation.TestAnnotation;
 
/**
 * 获取类属性上的注解属性值
 */
public class Demo2 {
    @TestAnnotation(value = "这就是value对应的值_msg1", what = "这就是what对应的值_msg1")
    private static String msg1;
 
    @TestAnnotation("这就是value对应的值1")
    private static String msg2;
 
    @TestAnnotation(value = "这就是value对应的值2")
    private static String msg3;
 
    @TestAnnotation(what = "这就是what对应的值")
    private static String msg4;
}

3. 测试

package com.ctb.annotation.demo2;
 
import com.ctb.annotation.TestAnnotation;
import org.junit.Test;
 
public class Demo2Test {
    @Test
    public void test1() throws Exception {
        TestAnnotation msg1 = Demo2.class.getDeclaredField("msg1").getAnnotation(TestAnnotation.class);
        System.out.println(msg1.value());
        System.out.println(msg1.what());
    }
 
    @Test
    public void test2() throws Exception{
        TestAnnotation msg2 = Demo2.class.getDeclaredField("msg2").getAnnotation(TestAnnotation.class);
        System.out.println(msg2.value());
        System.out.println(msg2.what());
    }
 
    @Test
    public void test3() throws Exception{
        TestAnnotation msg3 = Demo2.class.getDeclaredField("msg3").getAnnotation(TestAnnotation.class);
        System.out.println(msg3.value());
        System.out.println(msg3.what());
    }
 
    @Test
    public void test4() throws Exception{
        TestAnnotation msg4 = Demo2.class.getDeclaredField("msg4").getAnnotation(TestAnnotation.class);
        System.out.println(msg4.value());
        System.out.println(msg4.what());
    }
}

案例三:

1.创建自定义注解:

package com.ctb.annotation;
 
import java.lang.annotation.*;
 
/**
 * 非空注解:使用在方法的参数上,false表示此参数可以为空,true不能为空
 */
@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNotNull {
    boolean value() default false;
}

2. 创建测试类:

package com.ctb.annotation.demo3;
 
import com.ctb.annotation.IsNotNull;
 
/**
 * 获取参数修饰注解对应的属性值
 */
public class Demo3 {
 
    public void hello1(@IsNotNull(true) String name) {
        System.out.println("hello:" + name);
    }
 
    public void hello2(@IsNotNull String name) {
        System.out.println("hello:" + name);
    }
}

3. 测试

package com.ctb.annotation.demo3;
 
import com.ctb.annotation.IsNotNull;
import org.junit.Test;
 
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
 
public class Demo3Test {
 
    @Test
    public void hello1() throws Exception {
        Demo3 demo3 = new Demo3();
        for (Parameter parameter : demo3.getClass().getMethod("hello1", String.class).getParameters()) {
            IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
            if(annotation != null){
                System.out.println(annotation.value());//true
            }
        }
    }
 
    @Test
    public void hello2() throws Exception {
        Demo3 demo3 = new Demo3();
        for (Parameter parameter : demo3.getClass().getMethod("hello2", String.class).getParameters()) {
            IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
            if(annotation != null){
                System.out.println(annotation.value());//false
            }
        }
    }
 
    @Test
    public void hello3() throws Exception {
//        模拟浏览器传递到后台的参数 解读@requestParam
        String name = "zs";
        Demo3 demo3 = new Demo3();
        Method method = demo3.getClass().getMethod("hello1", String.class);
        for (Parameter parameter : method.getParameters()) {
            IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
            if(annotation != null){
                System.out.println(annotation.value());//true
                if (annotation.value() && !"".equals(name)){
                    method.invoke(demo3,name);
                }
            }
        }
    }
}

二、Aop自定义注解的应用

1 .自定义注解类

package com.ctb.annotation;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    String desc();
}

2.切面类

package com.ctb.aspect;
 
import com.ctb.annotation.MyLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
 
@Component
@Aspect
public class MyLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(MyLogAspect.class);
 
    /**
     * 只要用到了com.javaxl.p2.annotation.springAop.MyLog这个注解的,就是目标类
     */
    @Pointcut("@annotation(com.ctb.annotation.MyLog)")
    private void MyValid() {
    }
 
    @Before("MyValid()")
    public void before(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        logger.debug("[" + signature.getName() + " : start.....]");
        System.out.println("[" + signature.getName() + " : start.....]");
 
        MyLog myLog = signature.getMethod().getAnnotation(MyLog.class);
        logger.debug("【目标对象方法被调用时候产生的日志,记录到日志表中】:"+myLog.desc());
        System.out.println("【目标对象方法被调用时候产生的日志,记录到日志表中】:" + myLog.desc());
    }
}

3.Controller层

package com.ctb.web;
 
import com.ctb.annotation.MyLog;
import org.springframework.stereotype.Component;
 
@Component
public class LogController {
    @MyLog(desc = "这是结合spring aop知识,讲解自定义注解应用的一个案例")
    public void testLogAspect(){
        System.out.println("记录日志...");
    }
}
相关推荐
资源补给站1 小时前
大恒相机开发(2)—Python软触发调用采集图像
开发语言·python·数码相机
豪宇刘1 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意1 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
m0_748247551 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
6.941 小时前
Scala学习记录 递归调用 练习
开发语言·学习·scala
FF在路上2 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人2 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
皓木.2 小时前
Mybatis-Plus
java·开发语言
不良人天码星2 小时前
lombok插件不生效
java·开发语言·intellij-idea