SpringMVC之自定义注解

一.什么是SpringMVC之自定义注解

二.Java注解简介

Java注解分类

JDK元注解

三.自定义注解简介

自定义注解的分类

四.自定义注解的基本案例

案例一(获取类与方法上的注解值)

案例二(获取类属性上的注解属性值)

案例三(获取参数修饰注解对应的属性值)

五.Aop自定义注解的应用

Mylog前置通知


一.什么是SpringMVC之自定义注解

在SpringMVC中,我们可以使用自定义注解来增强代码的可读性和可维护性。通过自定义注解,我们可以在控制器类、方法或参数上添加自己定义的元数据,来标记一些特殊的行为或属性。

使用自定义注解,我们可以为SpringMVC添加一些额外的功能或限制。例如,我们可以定义一个@LoginRequired注解,用于标记需要登录才能访问的控制器方法。在SpringMVC的拦截器中,我们可以根据该注解判断用户是否已登录,从而决定是否允许继续执行该方法。

另外,自定义注解还可以用于参数校验、请求参数绑定、权限控制等方面。通过定义合适的注解,我们可以让SpringMVC自动进行参数校验,从而减少手动编写校验代码的工作量。

要自定义注解,我们需要使用Java的注解定义语法,结合SpringMVC的相关功能,实现自己所需的功能。首先,我们需要定义一个注解类型,使用@interface关键字声明。然后,在注解中定义需要的元数据,并设置相应的默认值。

接下来,我们可以在控制器类、方法或参数上使用自定义注解,来标记需要增强的行为或属性。我们还可以通过AOP等方式,根据自定义注解的类型,在程序运行时动态地对标记的代码进行处理。

总而言之,通过自定义注解,我们可以在SpringMVC中扩展自己的功能和特性,使得代码更加灵活可扩展。

二.Java注解简介

Java注解分类

JDK基本注解

JDK元注解

自定义注解

JDK元注解

@Retention:定义注解的保留策略

@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含

@Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,

@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到

@Target:指定被修饰的Annotation可以放置的位置(被修饰的目标)
@Target(ElementType.TYPE) //接口、类
@Target(ElementType.FIELD) //属性
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数

@Target(ElementType.CONSTRUCTOR) //构造函数

@Target(ElementType.LOCAL_VARIABLE) //局部变量

@Target(ElementType.ANNOTATION_TYPE) //注解

@Target(ElementType.PACKAGE) //包

注:可以指定多个位置,例如:

@Target({ElementType.METHOD, ElementType.TYPE}),也就是此注解可以在方法和类上面使用

@Inherited:指定被修饰的Annotation将具有继承性

@Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.

三.自定义注解简介

自定义注解的分类

自定义注解可分为三类:元注解、标记注解和单值注解。

  1. 元注解(Meta-Annotation):元注解用于注解其他注解,可以对注解本身进行描述和限制。Java提供了四种元注解,分别是:

    • @Retention:指定注解的生命周期,可以是源代码级别(SOURCE)、编译时(CLASS)或运行时(RUNTIME)。
    • @Target:指定注解的使用范围,可以是类、方法、字段等。
    • @Documented:指定注解是否包含在JavaDoc中。
    • @Inherited:指定注解是否可被继承。
  2. 标记注解(Marker Annotation):标记注解也称为无成员注解,它没有定义任何成员变量。标记注解的存在本身就代表一种信息,可以被用于标识目标对象的特殊性或属性。例如,JUnit中的@Test注解就是一个标记注解。

  3. 单值注解(Single Value Annotation):单值注解是最简单的一种注解,它只包含一个成员变量。我们可以为注解定义一个成员变量,并为其赋予默认值。在使用注解时,可以根据需要给成员变量赋值。例如,Spring中的@Autowired注解就是一个单值注解,它用于自动注入依赖对象。

需要注意的是,以上分类是一种常见的方式,实际上可以根据需求和设计的需要进行灵活扩展和组合。通过合理使用自定义注解,可以提高代码的可读性、可维护性和扩展性。

四.自定义注解的基本案例

案例一**(获取类与方法上的注解值)**

编写定义常量TranscationModel类

java 复制代码
package com.xy.annotation.demo1;

public enum  TranscationModel {
    //定义常量
    Read, Write, ReadWrite
}

编写MyAnnotation1

java 复制代码
package com.xy.annotation.demo1;

import java.lang.annotation.*;

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

编写MyAnnotation2

java 复制代码
package com.xy.annotation.demo1;

import java.lang.annotation.*;

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

编写MyAnnotation3

java 复制代码
package com.xy.annotation.demo1;

import java.lang.annotation.*;

/**
 * @author xy集团
 * @site www.javaxl.com
 *
 * MyAnnotation3注解可以用在方法上
 * 注解运行期也保留
 * 可继承
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation3 {
    TranscationModel[] models() default TranscationModel.ReadWrite;
}

编写测试类Demo1

java 复制代码
package com.xy.annotation.demo1;

/**
 * @author xy集团
 * @site www.javaxl.com
 *
 * 获取类与方法上的注解值
 */
@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

java 复制代码
package com.xy.annotation.demo1;

import org.junit.Test;

/**
 * @author xy集团
 * @site www.javaxl.com
 */
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
        }
    }
}

测试list结果

测试edit结果

案例二**(获取类属性上的注解属性值)**

编写TestAnnotation类

java 复制代码
package com.xy.annotation.demo2;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author xy集团
 * @site www.javaxl.com
 */
//@Retention(RetentionPolicy.SOURCE)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TestAnnotation {
    String value() default "默认value值";

    String what() default "这里是默认的what属性对应的值";
}

编写Demo2测试类

java 复制代码
package com.xy.annotation.demo2;

/**
 * @author xy集团
 * @site www.javaxl.com
 *
 * 获取类属性上的注解属性值
 */
public class Demo2 {
    @TestAnnotation(value = "这就是value对应的值_msg1", what = "这就是what对应的值_msg1")
    private static String msg1;

    //当没有在注解中指定属性名,那么就是value
    @TestAnnotation("这就是value对应的值1")
    private static String msg2;

    @TestAnnotation(value = "这就是value对应的值2")
    private static String msg3;

    @TestAnnotation(what = "这就是what对应的值")
    private static String msg4;
}

编写Demo2Tes测试类

java 复制代码
package com.xy.annotation.demo2;

import org.junit.Test;

/**
 * @author xy集团
 * @site www.javaxl.com
 */
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());
    }
}

测试test1结果

测试test2结果

测试test3结果

测试test4结果

案例三(获取参数修饰注解对应的属性值)

编写IsNotNull类

java 复制代码
package com.xy.annotation.Demo3;

import java.lang.annotation.*;

/**
 * @author xy集团
 * @site www.javaxl.com
 *
 * 非空注解:使用在方法的参数上,false表示此参数可以为空,true不能为空
 */
@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNotNull {
    boolean value() default false;
}

编写Demo3测试类

java 复制代码
package com.xy.annotation.Demo3;

/**
 * @author xy集团
 * @site www.javaxl.com
 *
 * 获取参数修饰注解对应的属性值
 */
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);
    }
}

编写Demo3Test测试类

java 复制代码
package com.xy.annotation.Demo3;

import org.junit.Test;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;


/**
 * @author xy集团
 * @site www.javaxl.com
 */
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);
                }
            }
        }
    }
}

测试hello1结果

测试hello2结果

测试hello3结果

五.Aop自定义注解的应用

Mylog前置通知

编写Mylog前置通知类

java 复制代码
package com.xy.annotation.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author xy集团
 * @site www.javaxl.com
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    String desc();
}

编写MyLogAspect切面类

java 复制代码
package com.xy.aspect;

import com.xy.annotation.aop.MyLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
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;

import java.util.Arrays;

/**
 * @author xy集团
 * @site www.javaxl.com
 */
@Component
@Aspect
public class MyLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(MyLogAspect.class);

    /**
     * 只要用到了com.javaxl.p2.annotation.springAop.MyLog这个注解的,就是目标类
     */
    @Pointcut("@annotation(com.xy.annotation.aop.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());
//    }


    @Around("MyValid()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        System.out.println(pjp.getTarget());//获取目标方法
        System.out.println(pjp.getThis());//
        Object[] args = pjp.getArgs();//获取参数
        System.out.println(Arrays.toString(args));//输出参数
        Object ob = pjp.proceed();//获取方法返回值
        System.out.println(ob);//输出返回值
        logger.info("耗时 : " + (System.currentTimeMillis() - startTime));
        return ob;
    }

}

编写LogController类

java 复制代码
package com.xy.web;

import com.xy.annotation.aop.MyLog;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author xy集团
 * @site www.javaxl.com
 */
@Controller
public class LogController {

    @RequestMapping("/mylog")
    @MyLog(desc = "这是结合spring aop知识,讲解自定义注解应用的一个案例")
    public void testLogAspect(){
        System.out.println("这里随便来点啥");
    }
}

测试结果(先走前置通知)

测试结果

相关推荐
GISer_Jing7 分钟前
前端面试通关:Cesium+Three+React优化+TypeScript实战+ECharts性能方案
前端·react.js·面试
落霞的思绪1 小时前
CSS复习
前端·css
咖啡の猫3 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲5 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5816 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路6 小时前
GeoTools 读取影像元数据
前端
ssshooter7 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Jerry7 小时前
Jetpack Compose 中的状态
前端
dae bal8 小时前
关于RSA和AES加密
前端·vue.js
柳杉8 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化