SpringBoot Aop概念
下面这段概念摘自武哥的《SpringBoot经典学习笔记》
AOP:Aspect Oriented Programming 的缩写,意为:面向切面编程。面向切面编程的目标就是分离关注点。什么是关注点呢?就是关注点,就是你要做的事情。假如你是一位公子哥,没啥人生目标,每天衣来伸手,饭来张口,整天只知道一件事:玩(这就是你的关注点,你只要做这一件事)!但是有个问题,你在玩之前,你还需要起床、穿衣服、穿鞋子、叠被子、做早饭等等等等,但是这些事情你不想关注,也不用关注,你只想想玩,那么怎么办呢?
对!这些事情通通交给下人去干。你有一个专门的仆人 A 帮你穿衣服,仆人 B 帮你穿鞋子,仆人 C 帮你叠好被子,仆人 D 帮你做饭,然后你就开始吃饭、去玩(这就是你一天的正事),你干完你的正事之后,回来,然后一系列仆人又开始帮你干这个干那个,然后一天就结束了!
这就是 AOP。AOP 的好处就是你只需要干你的正事,其它事情别人帮你干。也许有一天,你想裸奔,不想穿衣服,那么你把仆人 A 解雇就是了!也许有一天,出门之前你还想带点钱,那么你再雇一个仆人 E 专门帮你干取钱的活!这就是AOP。每个人各司其职,灵活组合,达到一种可配置的、可插拔的程序结构。
添加Maven依赖
先创建一个SpringBoot工程,添加下面依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
切面有哪些元素
-
@Aspect 表明是一个切面类
-
@Component 将当前类注入到Spring容器内
-
@Pointcut
切入点,其中execution用于使用切面的连接点。使用方法:execution(方法修饰符(可选) 返回类型 方法名 参数
异常模式(可选)) ,可以使用通配符匹配字符,*可以匹配任意字符。 -
@Before 在方法前执行
-
@After 在方法后执行
-
@AfterReturning 在方法执行后返回一个结果后执行 –
-
@AfterThrowing
在方法执行过程中抛出异常的时候执行 -
@Around
环绕通知,就是可以在执行前后都使用,这个方法参数必须为ProceedingJoinPoint,proceed()方法就是被切面的方法,上面四个方法可以使用JoinPoint,JoinPoint包含了类名,被切面的方法名,参数等信息。 -
JoinPoint
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象
普通方法实现切面
package com.example.aop.config;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* @author 朝花不迟暮
* @version 1.0
* @date 2020/8/31 14:58
*/
@Component
@Aspect
public class LogAspectHandler
{
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 定义一个切点,拦截com.itcodai.course09.controller包和子包下的所有方法
*/
@Pointcut("execution(* com.example.aop.controller..*.*(..))")
public void pointCut()
{
}
/**
* 在上面定义的切面方法之前执行该方法
*
* @param joinPoint jointPoint
*/
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint)
{
logger.info("====doBefore方法进入了====");
// 获取签名
Signature signature = joinPoint.getSignature();
// 获取切入的包名
String declaringTypeName = signature.getDeclaringTypeName();
// 获取即将执行的方法名
String funcName = signature.getName();
logger.info("即将执行方法为: {},属于{}包", funcName, declaringTypeName);
// 也可以用来记录一些信息,比如获取请求的url和ip
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 获取请求url
String url = request.getRequestURL().toString();
// 获取请求ip
String ip = request.getRemoteAddr();
logger.info("用户请求的url为:{},ip地址为:{}", url, ip);
}
/**
* 在上面定义的切面方法之后执行该方法
*
* @param joinPoint jointPoint
*/
@After("pointCut()")
public void doAfter(JoinPoint joinPoint)
{
logger.info("====doAfter方法进入了====");
Signature signature = joinPoint.getSignature();
String method = signature.getName();
logger.info("方法{}已经执行完", method);
}
/**
* 在上面定义的切面方法返回后执行该方法,可以捕获返回对象或者对返回对象进行增强
*
* @param joinPoint joinPoint
* @param result result
*/
@AfterReturning(pointcut = "pointCut()", returning = "result")
public void doAfterReturning(JoinPoint joinPoint, Object result)
{
Signature signature = joinPoint.getSignature();
String classMethod = signature.getName();
logger.info("方法{}执行完毕,返回参数为:{}", classMethod, result);
// 实际项目中可以根据业务做具体的返回值增强
logger.info("对返回参数进行业务上的增强:{}", result + "增强版");
}
/**
* 在上面定义的切面方法执行抛异常时,执行该方法
*
* @param joinPoint jointPoint
* @param ex ex
*/
@AfterThrowing(pointcut = "pointCut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex)
{
Signature signature = joinPoint.getSignature();
String method = signature.getName();
// 处理异常的逻辑
logger.info("执行方法{}出错,异常为:{}", method, ex);
}
@Around("pointCut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable
{
logger.info("=============进入doAround==================");
return proceedingJoinPoint.proceed();
}
}
写个测试的控制器
package com.example.aop.controller;
import com.example.aop.annotation.LogASP;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 朝花不迟暮
* @version 1.0
* @date 2020/8/31 14:59
*/
@RestController
public class TestController
{
@GetMapping("/{name}")
public String testAop(@PathVariable String name) {
return "Hello " + name;
}
}
浏览器访问8080,代参,进入控制台看打印
Aop实现自定义注解
自定义注解
package com.example.aop.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogASP
{
String param() default "";
}
定义切面
强调一下,自定义的注解要当做参数放在括号里,不然会报错
package com.example.aop.config;
import com.example.aop.annotation.LogASP;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @author 朝花不迟暮
* @version 1.0
* @date 2020/9/5 19:23
*/
@Aspect
@Component
public class LogAop
{
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Around("@annotation(logASP)")
public Object around(ProceedingJoinPoint joinPoint, LogASP logASP) throws Throwable
{
long a = System.currentTimeMillis();
System.out.println("方法开始时间是:"+a);
Object o = joinPoint.proceed();
long b = System.currentTimeMillis();
System.out.println("方法结束时间是:"+b) ;
log.info("方法总耗时{}秒",b-a);
return o;
}
}
控制器方法上加这个注解
package com.example.aop.controller;
import com.example.aop.annotation.LogASP;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 朝花不迟暮
* @version 1.0
* @date 2020/8/31 14:59
*/
@RestController
public class TestController
{
@GetMapping("/{name}")
@LogASP(param = "testAop")
public String testAop(@PathVariable String name) {
return "Hello " + name;
}
}
访问打印
方法开始时间是:1599307795370
2020-09-05 20:09:55.370 INFO 10624 --- [nio-8080-exec-1] com.example.aop.config.LogAspectHandler : =============进入doAround==================
2020-09-05 20:09:55.370 INFO 10624 --- [nio-8080-exec-1] com.example.aop.config.LogAspectHandler : ====doBefore方法进入了====
2020-09-05 20:09:55.371 INFO 10624 --- [nio-8080-exec-1] com.example.aop.config.LogAspectHandler : 即将执行方法为: testAop,属于com.example.aop.controller.TestController包
2020-09-05 20:09:55.372 INFO 10624 --- [nio-8080-exec-1] com.example.aop.config.LogAspectHandler : 用户请求的url为:http://localhost:8080/jcl/,ip地址为:0:0:0:0:0:0:0:1
2020-09-05 20:09:55.413 INFO 10624 --- [nio-8080-exec-1] com.example.aop.config.LogAspectHandler : 方法testAop执行完毕,返回参数为:Hello jcl
2020-09-05 20:09:55.414 INFO 10624 --- [nio-8080-exec-1] com.example.aop.config.LogAspectHandler : 对返回参数进行业务上的增强:Hello jcl增强版
2020-09-05 20:09:55.414 INFO 10624 --- [nio-8080-exec-1] com.example.aop.config.LogAspectHandler : ====doAfter方法进入了====
2020-09-05 20:09:55.414 INFO 10624 --- [nio-8080-exec-1] com.example.aop.config.LogAspectHandler : 方法testAop已经执行完
方法结束时间是:1599307795414
2020-09-05 20:09:55.414 INFO 10624 --- [nio-8080-exec-1] com.example.aop.config.LogAop : 方法总耗时44秒
方法开始时间是:1599307795483
2020-09-05 20:09:55.483 INFO 10624 --- [nio-8080-exec-2] com.example.aop.config.LogAspectHandler : =============进入doAround==================
2020-09-05 20:09:55.484 INFO 10624 --- [nio-8080-exec-2] com.example.aop.config.LogAspectHandler : ====doBefore方法进入了====
2020-09-05 20:09:55.484 INFO 10624 --- [nio-8080-exec-2] com.example.aop.config.LogAspectHandler : 即将执行方法为: testAop,属于com.example.aop.controller.TestController包
2020-09-05 20:09:55.484 INFO 10624 --- [nio-8080-exec-2] com.example.aop.config.LogAspectHandler : 用户请求的url为:http://localhost:8080/favicon.ico,ip地址为:0:0:0:0:0:0:0:1
2020-09-05 20:09:55.484 INFO 10624 --- [nio-8080-exec-2] com.example.aop.config.LogAspectHandler : 方法testAop执行完毕,返回参数为:Hello favicon.ico
2020-09-05 20:09:55.484 INFO 10624 --- [nio-8080-exec-2] com.example.aop.config.LogAspectHandler : 对返回参数进行业务上的增强:Hello favicon.ico增强版
2020-09-05 20:09:55.484 INFO 10624 --- [nio-8080-exec-2] com.example.aop.config.LogAspectHandler : ====doAfter方法进入了====
2020-09-05 20:09:55.484 INFO 10624 --- [nio-8080-exec-2] com.example.aop.config.LogAspectHandler : 方法testAop已经执行完
方法结束时间是:1599307795484
2020-09-05 20:09:55.484 INFO 10624 --- [nio-8080-exec-2] com.example.aop.config.LogAop : 方法总耗时1秒
关于自定义注解,这里只是简单地使用一下,并没有进行深刻的研究,如果想了解更多的玩法,可以找下其他大佬的blog进行深入研究。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16432.html