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("记录日志...");
    }
}
相关推荐
Abladol-aj23 分钟前
并发和并行的基础知识
java·linux·windows
清水白石00824 分钟前
从一个“支付状态不一致“的bug,看大型分布式系统的“隐藏杀机“
java·数据库·bug
Elihuss1 小时前
ONVIF协议操作摄像头方法
开发语言·php
Swift社区5 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht5 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht5 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20245 小时前
Swift 数组
开发语言
吾日三省吾码6 小时前
JVM 性能调优
java
stm 学习ing6 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc7 小时前
《Python基础》之字符串格式化输出
开发语言·python