Java注解开发

目录

一、JAVA注解

1、java注解的定义

        2、java注解的分类

                 2.1 JDK基本注解

                 2.2 JDK元注解

                 2.3 自定义注解

3、注解分类

        3.1标记Annotation: 

        3.2 元数据Annotation: 

二、自定义注解开发

1、自定义注解的定义

2、实例

三、用自定义注解完成切面日志操作

        一、传统的切面日志操作

                 1、先定义一个切面类:

                 2、定义一个测试类:

        二、自定义注解完成切面日志操作

                   1、定义一个专用于切面的注解类

                   2、在切面类中改变注解成专用于

                   3、在对应方法上注入注解

        三、自定义注解和传统切面日志操作对比:

四、自定义注解完成前端响应返回

           1、定义好四个自定义帮助类:

           2、测试实现


一、JAVA注解

1、java注解的定义

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

   注解相关类都包含在java.lang.annotation包中。

        2、java注解的分类

                 2.1 JDK基本注解

  2.1.1 @Override
      重写
  2.1.2 @Deprecated
      已过时 
  2.1.3 @SuppressWarnings(value = "unchecked") 
      压制编辑器警告

                 2.2 JDK元注解

   元注解用于修饰其他的注解(纪委:管干部的干部)
 2.2 .1 @Retention:定义注解的保留策略
      @Retention(RetentionPolicy.SOURCE)             //注解仅存在于源码中,在class字节码文件中不包含
      @Retention(RetentionPolicy.CLASS)              //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
      @Retention(RetentionPolicy.RUNTIME)            //注解会在class字节码文件中存在,在运行时可以通过反射获取到

 2.2 .2 @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}),也就是此注解可以在方法和类上面使用
  
 2.2 .3 @Inherited:指定被修饰的Annotation将具有继承性 

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

                 2.3 自定义注解

(第二个大点讲到 2.2 .4 @Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.

3、注解分类

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

        3.1标记Annotation: 

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

        3.2 元数据Annotation: 

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

二、自定义注解开发

1、自定义注解的定义

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

2、实例

以一个实例来讲:创建一个注解叫做MyAnnotation,在类的上面有四个源注解。

package com.yk.annotation;

import java.lang.annotation.*;

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "hello";

    String message() default "aaa";
}

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

2、@Target({ElementType.TYPE,ElementType.METHOD})是指这个注解可以用在方法上面

在对应枚举中有相关属性介绍:

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
}

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

4、@Retention注解的集中情况:写一个测试类进行测试

      4.1@Retention(RetentionPolicy.SOURCE) 这个只保存在源码中

MyAnnotation:

package com.yk.annotation;

import java.lang.annotation.*;

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Inherited
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
    String value() default "hello";

    String message() default "aaa";
}

 TestController:

package com.yk.controller;


import com.yk.annotation.MyAnnotation;
import org.aspectj.bridge.Message;
import org.springframework.stereotype.Controller;

@Controller
@MyAnnotation(message = "bbb")
public class TestController {
    public TestController(){
    }


    @MyAnnotation
    public void aa(){

    }



}
Test
package com.yk.controller;

import com.yk.annotation.MyAnnotation;
import org.springframework.test.context.TestContext;

import java.lang.annotation.Annotation;

public class Test {
    public static void main(String[] args) {
        for (Annotation a : TestController.class.getAnnotations()) {
            /*
            *
            * 判断里面的注解是否在类上面
            * */
//            System.out.println(a);

            /*
            * 判断遍历出来的注解是否属于某个注解
            * */
            if(a instanceof MyAnnotation){
                System.out.println(((MyAnnotation) a).message());
            }
        }


         MyAnnotation a = TestController.class.getAnnotation(MyAnnotation.class);
        if(a!=null){
            System.out.println(a.message());

        }
    }
}

测试结果:没有出现 MyAnnotation注解,原因是在@Retention(RetentionPolicy.SOURCE)当运行时,只存在源码中。

target中TestController没有出现MyAnnotation注解。

      4.2@Retention(RetentionPolicy.CLASS)     

运行结果中没有出现MyAnnotation注解,但是在target中出现了注解。      4.3@Retention(RetentionPolicy.RUNTIME)  

当运行结果出来时,会出现MyAnnotation注解:

三、用自定义注解完成切面日志操作

        一、传统的切面日志操作

                 1、先定义一个切面类:

package com.yk.aop;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Slf4j
public class LogAop {


    /*
    * 这是一个切入点
    * */
    @Pointcut("execution(* com.yk.controller.*Controller.*(..))")

//        @Pointcut("@annotation(com.yk.annotation.MyLog)")

    public void logger(){

    }


    @Around("logger()")
    public Object around(ProceedingJoinPoint point) {
        //获取方法名称
        Signature methodName = point.getSignature();
        //日志输出
        log.info(methodName+"进来了");
        Long l1=System.currentTimeMillis();
Object obj=null;
        try {
           obj= point.proceed(point.getArgs());
        } catch (Throwable e) {
            e.printStackTrace();
        }
        log.info(methodName+"bye"+"\t耗時 "+(System.currentTimeMillis()-l1));
//记录一个耗时时间,将证明日志通知
return obj;
    }

}

代码分析

1、

/*
* 这是一个切入点
* */
@Pointcut("execution(* com.yk.controller.*Controller.*(..))")

这是读取controller中的以controller结尾的类中的所有方法

2、导航方法

这是写了环绕通知后出现的导航方法

    @Around("logger()")
    public Object around(ProceedingJoinPoint point) {
        //获取方法名称
        Signature methodName = point.getSignature();
        //日志输出
        log.info(methodName+"进来了");
        Long l1=System.currentTimeMillis();
Object obj=null;
        try {
           obj= point.proceed(point.getArgs());
        } catch (Throwable e) {
            e.printStackTrace();
        }
        log.info(methodName+"bye"+"\t耗時 "+(System.currentTimeMillis()-l1));
return obj;

    }

                 2、定义一个测试类:

package com.yk.controller;


import com.yk.annotation.MyLog;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {

    @MyLog
    @RequestMapping("/add")
    public  String add(){
        return "addyes";
    }

    @RequestMapping("/del")
    public  String del(){


        return "delyes";
    }


    @RequestMapping("/upd")
    public  String upd(){

        return "updyes";
    }


    @RequestMapping("/list")
    public  String list(){
        return "listyes";
    }






}

 运行结果: 

        二、自定义注解完成切面日志操作

                   1、定义一个专用于切面的注解类

package com.yk.annotation;


import java.lang.annotation.*;

@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}

                   2、在切面类中改变注解成专用于

                  
  @Pointcut("execution(* com.yk.controller.*Controller.*(..))")
        
  改变一下这个@Pointcut("@annotation(com.yk.annotation.MyLog)")

                   3、在对应方法上注入注解


    @MyLog
    @RequestMapping("/add")
    public  String add(){
        return "addyes";
    }

运行结果:

        三、自定义注解和传统切面日志操作对比:

自定义注解和传统最大特点的对比是,自定义注解灵活性比传统方式要高

重点突出在一下两行代码:

          @Pointcut("execution(* com.yk.controller.*Controller.*(..))")
          @Pointcut("@annotation(com.yk.annotation.MyLog)")

以及自定义注解的方式:在方法上方注入注解即可

                  

            

四、自定义注解完成前端响应返回

           1、定义好四个自定义帮助类:

自定义注解类:ResponseResult

package com.yk.response;

import java.lang.annotation.*;

/**
 * 
 */
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD})
public @interface ResponseResult {

}
Result:响应对象封装类
package com.yk.response;

import lombok.Data;

import java.io.Serializable;

/**
 * 响应对象封装类
 *

 */
@Data
public class Result<T> implements Serializable {

    private final int code;
    private final String message;
    private final T data;

    /**
     * 私有构造, 只允许通过static调用构造
     *
     * @param resultCode 结果枚举
     * @param data       响应数据
     */
    private Result(ResultCode resultCode, T data) {
        this.code = resultCode.getCode();
        this.message = resultCode.getMessage();
        this.data = data;
    }

    /**
     * 成功调用返回的结果(无数据携带)
     *
     * @return Result
     */
    public static Result success() {
        return success(null);
    }

    /**
     * 成功调用返回的结果(数据携带)
     *
     * @return Result
     */
    public static <T> Result success(T data) {
        return new Result(ResultCode.SUCCESS, data);
    }

    /**
     * 失败调用返回的结果(数据携带)
     *
     * @param resultCode 状态枚举
     * @param data       携带的数据
     * @return Result
     */
    public static <T> Result failure(ResultCode resultCode, T data) {
        return new Result(resultCode, data);
    }

    /**
     * 失败调用返回的结果(无数据携带)
     *
     * @param resultCode 状态枚举
     * @return Result
     */
    public static Result failure(ResultCode resultCode) {
        return failure(resultCode, null);
    }

}
ResultCode:响应结果码枚举
package com.yk.response;

import java.io.Serializable;

/**
 * 响应结果码枚举
 *
 *
 */

public enum ResultCode implements Serializable {

    /* 正常状态 */
    SUCCESS(100, "成功"),
    FAILURE(101, "失败"),
    UNKNOWN(102, "未知响应"),
    /**
     * 用户code范围: 200~300;
     */
    USER_ACCOUNT_NOT_FIND(201, "用户名不存在"),
    USER_ACCOUNT_DISABLED(202, "该用户已被禁用"),
    USER_PASSWORD_NOT_MATCH(203, "该用户密码不一致"),
    USER_PERMISSION_ERROR(204, "该用户不具备访问权限"),
    USER_STATE_OFF_LINE(205, "该用户未登录");

    private final Integer code;
    private final String message;

    ResultCode(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public static ResultCode queryCode(Integer code) {
        for (ResultCode value : values()) {
            if (code.equals(value.code)) {
                return value;
            }
        }
        return UNKNOWN;
    }

}
ResponseParse

package com.yk.response;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@RestControllerAdvice
public class ResponseParse implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        //返回值决定他是否需要进入beforeBodyWrite
        return methodParameter.getMethod().isAnnotationPresent(ResponseResult.class);
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //更改返回值
        if (o == null) {
            return Result.success();
        }
        if (o instanceof Integer) {
            return Result.failure(ResultCode.queryCode((Integer) o));
        }
        if (o instanceof ResultCode) {
            return Result.failure((ResultCode) o);
        }
        if (o instanceof Result) {
            return o;
        }
        return null;
    }

}

           2、测试实现

以下四种情况分别对应着ResponseParse方法中的四种情况

package com.yk.controller;


import com.yk.annotation.MyLog;
import com.yk.response.ResponseResult;
import com.yk.response.Result;
import com.yk.response.ResultCode;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {

    @MyLog
    @RequestMapping("/add")
    @ResponseResult
    public Result add(){
        return Result.success();
    }

    @ResponseResult
    @RequestMapping("/del")
    public  Object del(){


        return 201;
    }

    @ResponseResult
    @RequestMapping("/upd")
    public  Object upd(){
        return ResultCode.USER_ACCOUNT_NOT_FIND;
        //用户名不存在
    }

    @ResponseResult
    @RequestMapping("/list")
    public  Object list(){
        return Result.success("yes");
    }





}

四种不同情况方法运行结果:

 add:

del:

 对应的编码翻译:

USER_ACCOUNT_NOT_FIND(201, "用户名不存在"),

upd:

list:

今天的知识就分享到这了,希望能够帮助到你 

  • 8
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值