目录
一、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注解是附加在代码中的一些元信息,用于一些工具在编译、
运行时进行解析和使用,起到说明、配置的功能。注解相关类都包含在java.lang.annotation包中。
2.1.1 @Override
重写
2.1.2 @Deprecated
已过时
2.1.3 @SuppressWarnings(value = "unchecked")
压制编辑器警告
元注解用于修饰其他的注解(纪委:管干部的干部)
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.2 .4 @Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.)
根据Annotation是否包含成员变量,可以把Annotation分为两类:
没有成员变量的Annotation; 这种Annotation仅利用自身的存在与否来提供信息
包含成员变量的Annotation;他们可以接受(或提供)更多的元数据。
使用@interface关键字, 其定义过程与定义接口非常类似, 需要注意的是:Annotation的成员变量在Annotation定义中是以无参的方法形式来声明的, 其方法名和返回值类型定义了该成员变量的名字和类型, 而且我们还可以使用default关键字为这个成员变量设定默认值
以一个实例来讲:创建一个注解叫做MyAnnotation,在类的上面有四个源注解。
package com.zxy.annotation;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.annotation.*;
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})//该注解只能写在类上
@Retention(RetentionPolicy.RUNTIME)//停留在源码阶段
@Inherited//继承
public @interface MyAnnotation {
String value() default "";
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.zxy.controller;
import com.zxy.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.zxy.controller;
import com.zxy.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注解:
package com.zxy.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;}
package com.zxy.controller;
import com.zxy.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";
}
}
运行结果:
package com.yk.annotation;
import java.lang.annotation.*;
@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}
@Pointcut("execution(* com.yk.controller.*Controller.*(..))")
改变一下这个@Pointcut("@annotation(com.yk.annotation.MyLog)")
@MyLog
@RequestMapping("/add")
public String add(){
return "addyes";
}
自定义注解和传统最大特点的对比是,自定义注解灵活性比传统方式要高
重点突出在一下两行代码:
@Pointcut("execution(* com.yk.controller.*Controller.*(..))")
@Pointcut("@annotation(com.yk.annotation.MyLog)")
以及自定义注解的方式:在方法上方注入注解即可
自定义注解类: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 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 Result success(T data) {
return new Result(ResultCode.SUCCESS, data);
}
/**
* 失败调用返回的结果(数据携带)
*
* @param resultCode 状态枚举
* @param data 携带的数据
* @return Result
*/
public static 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;
}
}
以下四种情况分别对应着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: