AOP功能的基本使用,自定义注解的使用,aspect注解切面编程的使用等
注:AOP本质为动态代理,是对被代理对象、方法的增强,所以在无法进行动态代理情况下,AOP会失效。、
本文旨在记录注解、aop的一些写法、方法,以防使用时忘记,不对原理做深入探讨。
自定义注解
采用@interface构建注解,实例如下所示,其中注解@Target表明该自定义注解使用的位置,ElementType.METHOD表示使用在方法上,当然,我们也可以修改此参数让其使用类上、参数上等,@Retention(RetentionPolicy.RUNTIME)表明注解作用在什么时候,RetentionPolicy.RUNTIME表明运行时作用,一般此处均是此写法。
注解内定义成员,采用名字+()形式,注意此处并不是方法,而是代表变量,注意需要指定默认值,在注解中无法使用复杂类作为变量,可使用基本数据类型以及对应的数组。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperateLog {
String value() default "select";
}
@Aspect实现aop
@Aspect实现aop有两种形式,一种为使用注解,一种为使用ex....表达式,本处介绍使用注解,其主要区别在于注解的方式由注解来标定使用aop的地方,表达式方式可直接标定使用aop的地方
本处借助于上文的注解来实现aop
首先需要导入aop包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
即可使用aop,代码及其解释如下
//AOP注解,需要在aop类上标注此注解
@Aspect
@Component//将此类交给spring管理
public class OperateLogAspect {
// 该注解标定切点,即要切入的地方,参数为注解路径,表明在标注有该注解地方使用aop,operateLog()表示被切入点,自定义名字,方法内无需任何语句
@Pointcut("@annotation(com.yanzi.annotation.OperateLog)")
public void operateLog(){
System.out.println("切入点");
}
// @Before注解表示被切入前需要做什么,方法名自定义,参数可自选,JoinPoint joinPoint参数为连接点对象,从该对象我们可以获取被注解标记地方的一些信息
// 如被标记方法名,参数值,类名,包名等,详情见下面方法内示例和注释
@Before("operateLog()")
public void before(JoinPoint joinPoint){
// 获取签名,进而获得方法名,还可以获得包名等信息
Signature signature = joinPoint.getSignature();
String name = signature.getName();
// 获取request和response对象
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
String requestURI = request.getRequestURI();
// 获取参数
Object[] args = joinPoint.getArgs();
// 获取参数名
String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
System.out.println("方法前,方法名为:"+name+" 请求url:"+requestURI+" 请求参数a:"+args[0]);
}
// 环绕,即在被注解标定方法前和后需要做什么,需要手动执行 proceedingJoinPoint.proceed()来执行被标注方法,
//ProceedingJoinPoint参数为JoinPoint子类,即JoinPoint能使用的该参数亦可,
@Around("operateLog()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕前");
Object proceed = proceedingJoinPoint.proceed();
System.out.println("环绕后");
return proceed;
}
//在被标注方法后执行
@After("operateLog()")
public void after(JoinPoint joinPoint){
System.out.println("方法结束");
}
//在被标注方法后执行,returning = "result"表明该方法返回值信息,"result"需要与方法中一致,下面异常标注同理
@AfterReturning(pointcut = "operateLog()",returning = "result")
public void afterReturn(JoinPoint joinPoint, String result){
System.out.println("方法结束,截获返回值:"+result);
}
//在被标注方法后执行,throwing = "e"表明该方法异常值信息
@AfterThrowing(pointcut = "operateLog()",throwing = "e")
public void afterThrow(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常,异常消息:"+e.getMessage());
}
}
当然,我们也可以获取注解中的信息
- 示例1,通过方法签名来获取
OperateLog operateLog=((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(OperateLog.class);
String value = operateLog.value();
- 示例2,直接注入,@annotation(oplog)表明当前注解,并定义注解对象oplog,下面的方法将oplog自动注入
@Before("operateLog() && @annotation(oplog) ")
public void before(JoinPoint joinPoint,OperateLog oplog){
String value = oplog.value();
}