基于注解SpringAOP,AfterReturning,Before,Around
AOP(Aspect Oriented Programming),即面向切面编程(也叫面向方面编程,面向方法编程)。其主要作用是,在不修改源代码的情况下给某个操作添加额外的功能。像日志记录,事务处理,权限控制,都可以用AOP来“优雅”地实现,使这些额外功能和真正的业务逻辑分离开来,软件的结构将更加清晰。
Spring AOP是基于代理机制的,通过JDK Proxy和CGLIB Proxy两种方法实现代理。
1)如果target object没有实现任何接口,那么Spring将使用CGLIB来实现代理。CGLIB是一个开源项目,它是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
2)如果target object实现了一个以上的接口,那么Spring将使用JDK Proxy来实现代理,因为Spring默认使用的就是JDK Proxy,并且JDK Proxy是基于接口的。这也是Spring提倡的面向接口编程。
依赖jar
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
下面是一个简单的示例:
1.定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {String type();
}
2.AOP配置
@Aspect
@Component
public class AOPConfig {@Around(value = "@annotation(OperationLog)")public Object around(ProceedingJoinPoint proceedingJoinPoint){System.out.println("方法环绕begin...参数:"+Arrays.toString(proceedingJoinPoint.getArgs()));try {Object ret= proceedingJoinPoint.proceed();System.out.println("方法环绕end...结果:"+ret);return ret;} catch (Throwable throwable) {throwable.printStackTrace();}return null;}@Before(value = "@annotation(OperationLog)")public void doBefore(JoinPoint joinPoint){ServletRequestAttributes requestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest req= requestAttributes.getRequest();System.out.println("uri:"+req.getRequestURI());System.out.println("执行方法前 : " + Arrays.toString(joinPoint.getArgs()));}@After(value = "@annotation(OperationLog)")public void after(JoinPoint joinPoint){System.out.println("执行方法后:"+ Arrays.toString(joinPoint.getArgs()));}@AfterReturning(pointcut = "@annotation(OperationLog)",returning = "ret")public void doAfterReturning(Object ret){System.out.println("方法的返回值 : " + ret);}@AfterThrowing(pointcut = "@annotation(OperationLog)",throwing = "ex")public void AfterThrowing(JoinPoint joinPoint,Throwable ex){System.out.println("方法执行异常 : " + ex);}
}
3.服务类
@RequestMapping("/aop")
@RestController
public class AOPCtrl {@Autowiredprivate MessageService messageService;@RequestMapping(value = "/test",method = RequestMethod.GET)public String test(){return messageService.sendMessage("xiaoming",29);}
}@Service
public class MessageService {@OperationLog(type = "sendMessage")public String sendMessage(String name,int age){return "this person: "+name+" age: "+age;}
}运行结果:
方法环绕begin...参数:[xiaoming, 29]
uri:/aop/test
执行方法前 : [xiaoming, 29]
方法环绕end...结果:this person: xiaoming age: 29
执行方法后:[xiaoming, 29]
方法的返回值 : this person: xiaoming age: 29
需要详细了解可以参照官方文档:https://docs.spring.io/spring/docs/2.5.x/reference/aop.html
springboot工程 @Around 简单的使用
一 .定义自定义注解
@Documented //生成文档API
@Target(ElementType.METHOD)//这个注解能写的位置,如method ,class
@Retention(RetentionPolicy.RUNTIME) //注解的生存时间
public @interface LoginUser{
}
二.导入的依赖包
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.8.RELEASE</version><!-- 根据自己spring工程版本选择--></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version></dependency>
三.实现环绕注解
package com.qf.annotation.impl;import com.qf.annotation.LoginUser;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoginUserImpl {@Around("@annotation(loginUser)")public void parseToken( ProceedingJoinPoint point,LoginUser loginUser) {System.out.println("loginUser = [" + loginUser + "]");try {//放行调用目标方法Object proceed = point.proceed();//返回值就是controller的返回值System.out.println("proceed = " + proceed);} catch (Throwable throwable) {throwable.printStackTrace();}}
}
需要注意点
- 使用该的注解时,必须在启动类进行包扫描,如果同一个模块则无需
- LoginUser 参数一定要写在最后面否则会报非法参数异常
SpringBoot:AOP 自定义注解实现日志管理
1.引入AOP依赖
<!-- aop依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.定义自定义注解@Log
package com.ohes.missclass.common.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {String value() default "";
}
3.定义AOP 。类名上使用@Aspect,@Component注解(说明是一个切面,并让spring扫描)
@Around 是可以同时在所拦截方法的前后执行一段逻辑
@Before 是在所拦截方法执行之前执行一段逻辑
@After 是在所拦截方法执行之后执行一段逻辑
@Pointcut 切入点
@annotation 表明只有在方法上引用@Log注解才会被AOP拦截执行
/*** AOP 记录用户操作日志**/
@Slf4j
@Aspect
@Component
public class LogAspect {@Pointcut("@annotation(com.ohes.missclass.common.annotation.Log)")public void pointcut() {// do nothing}@Around("pointcut()")public Object around(ProceedingJoinPoint point) throws Throwable {Object result = null;long beginTime = System.currentTimeMillis();// 执行方法result = point.proceed();// 获取 requestHttpServletRequest request = HttpContextUtil.getHttpServletRequest();// 设置 IP 地址String ip = IPUtil.getIpAddr(request);// 执行时长(毫秒)long time = System.currentTimeMillis() - beginTime;// 执行保存日志的数据库操作,把需要的信息插入到日志表中,根据实际业务逻辑编写return result;}
}
4.在保存日志的service方法上使用@Async注解,表明异步执行
@Async
void saveLog(ProceedingJoinPoint point, SysLog log) throws JsonProcessingException;
5.在controller层使用自定义@Log(利用AOP实现日志管理完成)
@Log("班级信息删除")
@DeleteMapping("/delClass/{ids}")
public Result delClass(@PathVariable(value = "ids", required = true) String ids) throws Exception {return iBizClassService.delClass(ids);
}