目录
前言
1.引入依赖
2.SpringAop的用法举例
3. 自定义注解+AOP的用法举例
3.1 关于Target注解补充
3.2 关于Retention注解补充
3.3 举例
前言
如果你不太理解aop的知识,请看我写的这篇文章,非常详细:
Spring AOP(定义、使用场景、用法、3种事务、事务失效场景及解决办法、面试题)_spring aop的事件-CSDN博客https://blog.csdn.net/m0_64422133/article/details/143376059?spm=1001.2014.3001.5501
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.SpringAop的用法举例
service类(获取名字接口)
package com.example.aop_annotation.service;
import org.springframework.stereotype.Service;/*** @author xcw*/
@Service
public class NameService {public String getName(String name){System.out.println("getName方法执行了");return "NameService:"+name;}
}
TestService
这个类实现了 CommandLineRunner类并且重写run(),run()中的操作会在springboot启动后调用
package com.example.aop_annotation.service;import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;import javax.annotation.Resource;/*** @author xcw*/
@Component
public class TestService implements CommandLineRunner {@ResourceNameService nameService;@Overridepublic void run(String... args) throws Exception {nameService.getName("xcw");}
}
切面类:
package com.example.aop_annotation.aop;import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;import java.util.Arrays;/*** @author xcw*/
@Aspect
@Component
public class LogAspect {//定义切点拦截NameService类中的所有方法@Pointcut("execution(* com.example.aop_annotation.service.NameService.*(..))")public void pointcut(){}@Before("pointcut()")public void before(JoinPoint joinPoint){Object[] args = joinPoint.getArgs();System.out.println("拦截的类: " + joinPoint.getTarget().getClass().getName());System.out.println("拦截的方法: " + joinPoint.getSignature().getName());System.out.println("方法参数: " + Arrays.toString(args));//方法返回值System.out.println("before前置通知执行了");System.out.println("----------------------------------------------");}@AfterReturning(pointcut = "pointcut()", returning = "result")public void afterReturning(JoinPoint joinPoint, Object result) {System.out.println("拦截的类: " + joinPoint.getTarget().getClass().getName());System.out.println("拦截的方法: " + joinPoint.getSignature().getName());System.out.println("返回值: " + result);System.out.println("afterReturning后置通知执行了");System.out.println("----------------------------------------------");}
}
执行结果如下:
我们把上面代码改为使用自定义注解+AOP的方式
3. 自定义注解+AOP的用法举例
首先我们先创建一个自定义注解类
package com.example.aop_annotation.annotation;import java.lang.annotation.*;/*** @author xcw*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {String value() default "";
}
3.1 关于Target注解补充
@Target:常常配合枚举类ElementType来指定注解的作用位置,即你定义了一个注解,这个注解是类注解还是方法注解还是属性字段注解等具体作用的范围
ElementType的枚举值有
枚举值 | 含义 |
---|---|
TYPE | 类、接口(包括注解类型)、或枚举声明 |
FIELD | 字段,包括枚解常量 |
METHOD | 方法声明 |
PARAMETER | 正式的参数声明 |
CONSTRUCTOR | 构造函数的声明 |
LOCAL_VARIABLE | 局部变量的声明 |
ANNOTATION_TYPE | 注解类型的声明 |
PACKAGE | 包声明 |
3.2 关于Retention注解补充
@Retention:常常配合枚举类RetentionPolicy来指定注解的各种策略,注解的保留时间(生命周期),也就是何时生效,即你定义了一个注解,这个注解是编译时生效还是仅仅只是在代码中标记等等,具体作用的范围,取决于@Retention({RetentionPolic.TYPE})中,RetentionPolic的枚举值,在进行自定义枚举时,大多数都是使用RUNTIME(编译时生效)
RetentionPolic的枚举值
枚举值 | 含义 |
---|---|
SOURCE | 解只在源代码级别保留,编译时被忽略。 |
CLASS | 注解将被编译器在类文件中记录,但在运行时不需要JVM保留。这是默认的。 |
RUNTIME | 注解将被编译器记录在类文件中,在运行时保留,VM可以读取。一般自定义注解均使用这个。 |
3.3 举例
我们把上面 2 中的例子改成使用Aop+自定义注解的方式实现
1. 首先我们先创建一个自定义注解类
package com.example.aop_annotation.annotation;import java.lang.annotation.*;/*** @author xcw*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {String value() default "";
}
2. 在方法上使用自定义注解
package com.example.aop_annotation.service;
import com.example.aop_annotation.annotation.MyAnnotation;
import org.springframework.stereotype.Service;/*** @author xcw*/
@Service
public class NameService2 {@MyAnnotationpublic String getName2(String name){System.out.println("getName2方法执行了");return "NameService2:"+name;}
}
3. 把aop切点 指向改为 注解类的包路径
package com.example.aop_annotation.aop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;import java.util.Arrays;/*** @author xcw*/
@Aspect
@Component
public class LogAspect2 {//定义切点拦截使用了自定义注解@MyAnnotation的方法@Pointcut(value = "@annotation(com.example.aop_annotation.annotation.MyAnnotation)")public void pointcut(){}@Before("pointcut()")public void before(JoinPoint joinPoint){Object[] args = joinPoint.getArgs();System.out.println("拦截的类: " + joinPoint.getTarget().getClass().getName());System.out.println("拦截的方法: " + joinPoint.getSignature().getName());System.out.println("方法参数: " + Arrays.toString(args));//方法返回值System.out.println("before前置通知执行了");System.out.println("----------------------------------------------");}@AfterReturning(pointcut = "pointcut()", returning = "result")public void afterReturning(JoinPoint joinPoint, Object result) {System.out.println("拦截的类: " + joinPoint.getTarget().getClass().getName());System.out.println("拦截的方法: " + joinPoint.getSignature().getName());System.out.println("返回值: " + result);System.out.println("afterReturning后置通知执行了");System.out.println("----------------------------------------------");}
}
项目启动后执行的类
package com.example.aop_annotation.service;import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;import javax.annotation.Resource;/*** @author xcw*/
@Component
public class TestService implements CommandLineRunner {@ResourceNameService nameService;@ResourceNameService2 nameService2;@Overridepublic void run(String... args) throws Exception {nameService2.getName2("xcw");}
}
执行结果如下:
后续待完善此文章(AOP+自定义注解+枚举类 实现日志写入打印功能)
如果你也感兴趣可以参考:
springboot aop 自定义注解方式实现完善日志记录(完整源码)-腾讯云开发者社区-腾讯云 (tencent.com)