java自定义注解

2019-12-31 15:47:43 浏览数 (1)

文章目录

1. 自定义注解【Annotation】

1.1. 元注解

1.1.1. @Retention

1.1.2. @Target

1.1.3. @Document

1.1.4. @Inherited

1.2. 关于注解的反射方法

1.3. 五种通知

1.4. JoinPoint 对象

1.5. 实战

1.5.1. 日志

1.5.2. 性能监控

1.5.3. 输出错误日志到文件中

1.6. 参考文章

自定义注解【Annotation】

元注解

@Retention

  • 表示需要在什么级别保存该注解信息 。分为如下三类:
    • @Retention(RetentionPolicy.SOURCE):注解仅存在于源码中,在class字节码文件中不包含
    • @Retention(RetentionPolicy.CLASS):默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
    • @Retention(RetentionPolicy.RUNTIME): 注解会在class字节码文件中存在,在运行时可以通过反射获取到

@Target

  • 注解的作用目标

取值

作用域

@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)

@Document

  • 注解包含在javadoc中

@Inherited

  • 注解可以被继承

关于注解的反射方法

代码语言:javascript复制
// 获取某个类型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass);

// 获取所有注解(包括父类中被Inherited修饰的注解)
public Annotation[] getAnnotations(); 

// 获取声明的注解(但是不包括父类中被Inherited修饰的注解)
public Annotation[] getDeclaredAnnotations();

// 判断某个对象上是否被某个注解进行标注
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

// 获取某个类声明的所有字段
public Field[] getDeclaredFields() throws SecurityException;

// 获取某个方法
public Method getMethod(String name, Class<?>... parameterTypes);

五种通知

  • @Before:前置通知,在调用目标方法之前执行通知定义的任务
  • @After:后置通知,在目标方法执行结束后,无论执行结果如何(异常或者正常执行)都执行通知定义的任务
  • @After-returning:后置通知,在目标方法执行结束后,如果执行成功(没有异常),则执行通知定义的任务
  • @After-throwing:异常通知,如果目标方法执行过程中抛出异常,则执行通知定义的任务
  • @Around:环绕通知,在目标方法执行前和执行后,都需要执行通知定义的任务

JoinPoint 对象

方法名

功能

Signature getSignature();

获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息

Object[] getArgs();

获取传入目标方法的参数对象

Object getTarget();

获取被代理的对象

Object getThis();

获取代理对象

实战

  • 使用springBoot,需要 添加aop相关的依赖,如下:
代码语言:javascript复制
<!-- springBoot的aop功能启动器 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-aop</artifactId>
	</dependency>

日志

  • 实际的生产环境中会对每个都记录日志,比如xxx用户执行了xxx操作,这些日志如果都使用一个方法的话,未免太不雅,此时我们可以定义一个注解,使用spring中的aop,在方法执行成功之后记录日志信息,步骤如下:
  • 定义一个注解,使用@Interface
代码语言:javascript复制
import java.lang.annotation.*;
/**
 * 定义日志的注解,作用在方法上
 * @author 陈加兵 	
 * @since 2018年11月3日 上午11:53:59
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})  //作用在方法上面
@Retention(RetentionPolicy.RUNTIME)   //程序运行  
@Documented  
public @interface InsertLog {
	String name() default "";   //用户名
	String operation() default "";  //操作
}
  • 定义一个注解的实现类,使用aop实现该注解
代码语言:javascript复制
@Component   //注入
@Aspect   //切面
public class InsertLogAnnotation {
	
	@Resource
	private LogService logService;   //日志的service,用来记录日志
	
	/**
	 * 1、@pointCut:定义切入点,其中支持多种表达式来匹配切入点,这里的annotation是用来匹配注解的
	 * 2、@annotation的参数必须和这个方法的参数字段相同,因为这里表示的扫描的哪个注解
	 * 3、这个切入点的 意思:只要被`@InsertLog`这个注解标注的都会被扫描到成为切入点
	 * @param log  注解的对象
	 */
	@Pointcut("@annotation(log)")
	public void insertLog(InsertLog log){}
	
	
	/**
	 * `@AfterReturning`:定义后置通知,只有程序执行成功才会调用该注解,用来添加操作的日志
	 * `insertLog(log)`: 这里的log一定要和上面定义切入点(@Pointcut)中的参数字段一样
	 * @param point JoinPoint对象,可以获取一些切面信息,比如调用的类,调用的方法名称
	 * @param log  该注解的对象,可以获取注解中参数的内容
	 * @throws Exception 
	 */
	@AfterReturning("insertLog(log)")
	public void SystemLog(JoinPoint point,InsertLog log) throws Exception{
		//获取注解中的参数内容
		String name=log.name();  //姓名
		String operation=log.operation();  //操作
		
		Log log2=new Log();
		log2.setName(name);
		log2.setOperation(operation);
		logService.addLog(log2);  //添加到日志中
		
	}
  • 此时就已经完成了,我们只需要将该注解添加到需要记录日志的方法上即可,如下:
代码语言:javascript复制
@InsertLog(name="陈加兵",operation="删除用户")  //使用日志注解,在程序执行成功之后记录日志
public Object deleteUserById(Integer userId) {
	System.out.println("删除成功");
	return null;
}
  • 完整的项目截图如下:

性能监控

  • 可以定义一个注解实现性能监控,设置一个环绕通知即可,在程序执行开始和结束之后统计时间即可
  • 定义一个注解,如下:
代码语言:javascript复制
import java.lang.annotation.*;

/**
 * 性能监控的注解
 * @author 陈加兵 	
 * @since 2018年11月3日 下午1:08:30
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})  //作用在方法上面
@Retention(RetentionPolicy.RUNTIME)   //程序运行  
@Documented
public @interface CapabilityMonitor {
}
  • 定义注解的实现类,使用切面的环绕通知
代码语言:javascript复制
/**
 * 性能监控注解的实现类
 * @author 陈加兵 	
 * @since 2018年11月3日 下午1:09:40
 */
@Aspect    //切面
@Component   //注入
public class CapabilityMonitorAnnotationImpl {
	private Logger logger=LoggerFactory.getLogger(CapabilityMonitorAnnotationImpl.class);  //selfj的日志信息
	
	/**
	 * 定义切入点,只要方法体上有这个注解
	 * @param capabilityMonitor  注解的对象
	 */
	@Pointcut("@annotation(capabilityMonitor)")
	public void capabilityMonitor(CapabilityMonitor capabilityMonitor){}
	
	/**
	 * 环绕通知,在方法执行之前和之后都执行
	 * capabilityMonitor(capabilityMonitor):这里的参数一定要和切入点(`@Pointcut("@annotation(capabilityMonitor)")`)的参数相同
	 * @param point
	 * @param capabilityMonitor  注解的对象
	 * @throws Throwable 
	 */
	@Around("capabilityMonitor(capabilityMonitor)")
	public void execute(ProceedingJoinPoint point,CapabilityMonitor capabilityMonitor) throws Throwable{
		Long startTime=System.currentTimeMillis();  //开始时间
		Object[] args=point.getArgs(); //获取目标方法执行的参数数组
		Object returnValues=point.proceed(args);   //执行目标方法
		Long endTime=System.currentTimeMillis();  //结束时间
		logger.info("程序执行的时间:" ((endTime-startTime)/1000.0));   //输出程序执行的时间,秒位单位
	}
}

输出错误日志到文件中

  • 定义日志注解,如下:
代码语言:javascript复制
/**
 * 输出日志信息到日志文件的注解
 * @author 陈加兵 	
 * @since 2018年11月7日 下午5:52:55
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})  //作用在方法上面
@Retention(RetentionPolicy.RUNTIME)   //程序运行  
@Documented
public @interface PrintLog {
}
  • 注解的实现类,结合aop实现
代码语言:javascript复制
@Component
@Aspect
public class PrintLogAnnotationImpl {
	
	/**
	 * 定义切入点,凡是方法体上标注@PrintLog这个注解都会被增强
	 * @param printLog
	 */
	@Pointcut("@annotation(printLog)")
	public void printLog(PrintLog printLog){}
	
	/**
	 * 在程序之后执行并且在出现异常的时候才会执行
	 * @param point  JoinPoint对象,用于获取切入点的信息,比如被增加的类,被增强的方法名称、方法传入的参数等信息
	 * @param printLog  注解的接口信息,可以获取接口的信息
	 * @param message   异常信息
	 */
	@AfterThrowing(value="printLog(printLog)",throwing="message")
	public void excute(JoinPoint point,PrintLog printLog,Throwable message){
		Class targetCls=point.getTarget().getClass();  //获取目标类
		Logger logger = LoggerFactory.getLogger(targetCls);
		logger.error("异常信息:",message);
	}
}
  • 只需要在方法上标注这个注解,只要是遇到异常信息,就会自动写入日志文件中

参考文章

  • aop的相关知识点
  • spring aop

0 人点赞