springboot+自定义注解

简介】

项目中经常使用自定义注解和切面,实现操作日志、权限、统计执行时间等功能,本文以操作日志为例

自定义注解类编写的一些规则:
1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
2. 参数成员只能用public或默认(default)这两个访问权修饰
3. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,因为你除此之外没有别的获取注解对象的方法
5. 注解也可以没有定义成员, 不过这样注解就没啥用了

 

 

1.首先自己创建一个maven的springboot project 在线创建可参考在线编辑swagger编辑器创建

2.创建工程后,编辑pom文件,文件中添加对aop的依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.定义注解:

   其中注解定以前,先了解定义注解的几种解释:

/**
 * java.lang.annotation提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解):
 * @Documented:注解是否将包含在JavaDoc中
 * @Retention:什么时候使用该注解 指明修饰的注解的生存周期,即会保留到哪个阶段。
 * 	SOURCE:源码级别保留,编译后即丢弃。
	CLASS:编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。
	RUNTIME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。
 * @Target:注解用于什么地方 指明了修饰的这个注解的使用范围,即被描述的注解可以用在哪里。
 *   ElementType的取值包含以下几种:
		TYPE:类,接口或者枚举
		FIELD:域,包含枚举常量
		METHOD:方法
		PARAMETER:参数
		CONSTRUCTOR:构造方法
		LOCAL_VARIABLE:局部变量
		ANNOTATION_TYPE:注解类型
		PACKAGE:包
 * @Inherited:是否允许子类继承该注解

  依据以上解释。,编写自己的注解MyAnnotation,代码如下:

package io.swagger.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * java.lang.annotation提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解):
 * @Documented:注解是否将包含在JavaDoc中
 * @Retention:什么时候使用该注解 指明修饰的注解的生存周期,即会保留到哪个阶段。
 * 	SOURCE:源码级别保留,编译后即丢弃。
	CLASS:编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。
	RUNTIME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。
 * @Target:注解用于什么地方 指明了修饰的这个注解的使用范围,即被描述的注解可以用在哪里。
 *   ElementType的取值包含以下几种:
		TYPE:类,接口或者枚举
		FIELD:域,包含枚举常量
		METHOD:方法
		PARAMETER:参数
		CONSTRUCTOR:构造方法
		LOCAL_VARIABLE:局部变量
		ANNOTATION_TYPE:注解类型
		PACKAGE:包
 * @Inherited:是否允许子类继承该注解
 * @author Jia.Huang
 * 
 * 
 * 几个注解的解释
	@Around 环绕切点,在进入切点前,跟切点后执行

	@After 在切点后,return前执行,

	@Before 在切点前执行方法,内容为指定的切点
	@Aspect 将一个类定义为一个切面类
 *
 */
@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface MyAnnotation {
	String value() default  "first one";
}

4.编写需要注入的方法:

  

package io.swagger.AscpetTest;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
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.springframework.stereotype.Component;

import io.swagger.annotation.MyAnnotation;

@Component
@Aspect
public class TestAspect {
	@Pointcut("@annotation(io.swagger.annotation.MyAnnotation)")
	public void addAdvice() {}
	         
	@Around("addAdvice()")
	public Object Interceptor(ProceedingJoinPoint joinPoint) {
		System.out.println("====Interceptor====");
		System.out.println("通知之开始");
		Object retmsg = null;
		try {
			retmsg = joinPoint.proceed();
			System.err.println("++++++++ 最后执行,执行完后结束 -->" + retmsg);
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("通知之结束 +retmsg -->" + retmsg);

		Object result = null;
		Object[] args = joinPoint.getArgs();
		if (args != null && args.length > 0) {
			String deviceId = (String) args[0];
			if (!"03".equals(deviceId)) {
				return "no anthorization";
			}
		}
		try {
			result = joinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return result;

	}

	@Before("addAdvice()")
	public void before(JoinPoint joinPoint) {
		MethodSignature sign = (MethodSignature) joinPoint.getSignature();
		Method method = sign.getMethod();
		MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
		System.out.println("打印:" + annotation.value() + " 开始前");
		// System.out.println("===开始前===");
	}

	@After("addAdvice()")
	public void after() {
		System.out.println("after方法执行后");
	}

}

5.编写controller类:

  

package io.swagger.api;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotation.MyAnnotation;

@RestController
public class TestController {
	@MyAnnotation("test11")
    @RequestMapping("/add1")
    public String addData1(String deviceId) {
        System.out.println("=====addData1=====");
        return "success";
    }
    @MyAnnotation()
    @RequestMapping("/add2")
    public String addData2(String deviceId) {
        System.out.println("=====addData2=====");
        return "success";
    }
    @MyAnnotation()
    @RequestMapping("/add3")
    public String addData3(String deviceId) {
        System.out.println("=====addData3=====");
        return "success";
    }

    @MyAnnotation()
    @RequestMapping("/update1")
    public String updateData1(String deviceId) {
        System.out.println("=====updateData1=====");
        return "success";
    }

	

}

6.修改springboot的启动项:

   在启动项上加入@EnableAspectJAutoProxy注解 让springboot project能开启自动注解功能

7.启动服务,测试:

  依据以上结果 则实现了自定义注解方法:

  其中 执行过程为:前端Api发送请求到后端,

   1.会先执行方法中joinPoint.proceed();之前的逻辑

  2运行到joinPoint.proceed();时,先执行@Befor注解的逻辑

 3.执行joinPoint.proceed();的逻辑

4.执行@After注解的逻辑

5.执行joinPoint.proceed();剩下的逻辑

至此执行完毕

 

 

附:

实现Log的监控

1.编写Log的annotation

package io.swagger.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
	/**
	 * 记录操作描述
	 */
	String description() default "";
	
	/**
	 * 增删改的数据的类型
	 */
	Class<?> clazz();
	

}

2.编写Log的切入点及逻辑

package io.swagger.AscpetTest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
@Component
public class LogAspect {
	//切入点签名:
	@Pointcut("@annotation(io.swagger.annotation.LogAnnotation)")
	public void cut() {}
	//前置通知
	@Before("cut()")
	public void beforCall() {
		log.info("====前置通知start");
		
		log.info("====前置通知end");
	}
	
	//环绕通知
	@Around(value="cut()")
	public Object AroundCall(ProceedingJoinPoint joinPoint) throws Throwable{
		log.info("====环绕通知start");
		
		//注解所切的方法所在类的全类名
		String typeName = joinPoint.getTarget().getClass().getName();
		log.info("====目标对象:[{}]",typeName);
		
		//注解所切的方法名
		String methodName = joinPoint.getSignature().getName();
		log.info("所切的方法名:[{}]",methodName);
		
		StringBuilder sb = new StringBuilder();
		//获取参数
		Object[] arguments = joinPoint.getArgs();
		for(Object argument:arguments) {
			sb.append(argument.toString());
		}
		
		log.info("所切方法入参:{}",sb.toString());
		
		//统计方法执行时间
		long start = System.currentTimeMillis();
		
		//执行目标方法。并获得对应方法的返回值
		Object result = joinPoint.proceed();
		log.info("返回结果:[{}]",result);
		
		long end = System.currentTimeMillis();
		log.info("====执行方法共用时间:[{}]",(end-start));
		
		log.info("====环绕通知之结束");
		return result;
	}
	
	//后置通知
	@After("cut()")
	public void AfterCall() {
		log.info("=====后置通知start");
		
		log.info("=====后置通知end");
	}
	
	//最终通知
	@AfterReturning("cut()")
	public void AfterReturningCall() {
		log.info("=====最终通知start");
		
		log.info("=====最终通知end");
	}
	
	//异常通知
	@AfterThrowing(value = "cut()",throwing="ex")
	public void afterThtrowing(Throwable ex) {
		throw new RuntimeException(ex);
	}

}

3.在controller层切入log

package io.swagger.api;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotation.LogAnnotation;
import io.swagger.annotation.MyAnnotation;

@RestController
public class TestController {
	
	//@MyAnnotation("test11")
	@LogAnnotation(description="获取所有用户信息",clazz = TestController.class)
    @RequestMapping("/add1")
    public String addData1(String deviceId) {
        System.out.println("=====addData1=====");
        return "success";
    }
    @MyAnnotation()
    @RequestMapping("/add2")
    public String addData2(String deviceId) {
        System.out.println("=====addData2=====");
        return "success";
    }
    @MyAnnotation()
    @RequestMapping("/add3")
    public String addData3(String deviceId) {
        System.out.println("=====addData3=====");
        return "success";
    }

    @MyAnnotation()
    @RequestMapping("/update1")
    public String updateData1(String deviceId) {
        System.out.println("=====updateData1=====");
        return "success";
    }

	

}

4.执行:结果显示:

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值