自定义注解
定义一个AOP自定义注解,一般格式如下:
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyLogger {
}
@Traget
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
ElementType 有:
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
}
https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/ElementType.html
@Retention
用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
public enum RetentionPolicy {
SOURCE, //该类型注解只在源代码级别保留,编译时就会被忽略
CLASS, //该类型的注解仅保存在class文件中,它们不会被加载进JVM;
RUNTIME; //该类型的注解会被加载进JVM,并且在运行期可以被程序读取。
private RetentionPolicy() {
}
}
SOURCE
类型的注解主要由编译器使用,因此我们一般只使用,不编写。CLASS
类型的注解主要由底层工具库使用,涉及到class的加载,一般我们很少用到。只有RUNTIME
类型的注解不但要使用,还经常需要编写。
@Inherited
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是可以被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
@interface
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
AOP 注解
AOP 要实现的是在我们原来写的代码的基础上,进行一定的包装,如在方法执行前、方法返回后、方法抛出异常后等地方进行一定的拦截处理或者叫增强处理。
AOP的常用注解有:
注解 | 说明 |
---|---|
@Aspect | 切面。表示一个横切进业务的一个对象。它里面包含切入点(Pointcut)和Advice(通知)。 |
@Pointcut | 切入点。表示需要切入的位置,比如某些类或者某些方法,也就是先定一个范围。 |
@Before | Advice(通知)的一种,切入点的方法体执行之前执行。 |
@Around | Advice(通知)的一种,环绕切入点执行也就是把切入点包裹起来执行。 |
@After | Advice(通知)的一种,在切入点正常运行结束后执行。 |
@AfterReturning | Advice(通知)的一种,在切入点正常运行结束后执行,异常则不执行 |
@AfterThrowing | Advice(通知)的一种,在切入点运行异常时执行。 |
方法入参和返参打印注解
通过注解实现打印一个方法的入参和返参日志。
-
pom.xml依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <version>2.1.11.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency>
-
定义注解接口
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface MyLogger { }
-
定义注解处理类
@Aspect @Component public class MyLoggerAopHandler { Logger logger = LoggerFactory.getLogger(this.getClass()); @Pointcut("@annotation(com.betago.transaction.MyLogger)") public void logger(){ } @Around("logger()") public Object doAround(ProceedingJoinPoint joinPoint)throws Throwable { Object result = null; MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); //获取方法名 String methodName = method.getName(); //获取方法参数名 Parameter[] argsName = method.getParameters(); //获取传入参数 Object[] args = joinPoint.getArgs(); //打印入参日志 logger.info( methodName + getParamStr(argsName, args)); //执行被代理方法 result = joinPoint.proceed(); //打印出参日志 logger.info(methodName + ", result: " + JSON.toJSONString(result)); return result; } private String getParamStr( Parameter[] paramNames, Object[] paramValues){ if((paramNames == null || paramNames.length < 1) || (paramValues == null || paramValues.length < 1)){ return ""; } StringBuilder sb = new StringBuilder(); int length = paramNames.length; for(int i = 0; i < length; i++){ String name = paramNames[i].getName(); Object value = paramValues[i]; String valStr = ""; if(value instanceof String){ valStr = (String)value; }else { valStr = JSON.toJSONString(value); } sb.append(", " + name + ": " + valStr); } return sb.toString(); } }
-
定义一个service
@Service public class UserService { @MyLogger public String userInfo(String name, int age){ System.out.println("name :" + name + ", age: " + age ); return name + age; } }
-
单元测试
@RunWith(SpringRunner.class) @SpringBootTest @WebAppConfiguration class UserServiceTest { @Autowired UserService userService; @Test public void userInfoTest() { userService.userInfo("aaa", 12); } }
输出结果:
11:52:21.498 logback [main] INFO c.b.transaction.MyLoggerAopHandler - userInfo, name: aaa, age: 12 name :aaa, age: 12 11:52:21.513 logback [main] INFO c.b.transaction.MyLoggerAopHandler - userInfo, result: "aaa12"
学习资料
- https://www.liaoxuefeng.com/wiki/1252599548343744/1265102026065728
- https://juejin.im/post/6844903858393595917
- https://segmentfault.com/a/1190000013258647