1. AOP是什么?
AOP中文叫做面向切面编程,为Aspect Oriented Programming的首字符缩写。意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。通过名字可以看出,它与面向对象编程OOP名称相似。其实AOP就是OOP的进一步延伸。是Spring框架中的重要内容之一。
2. AOP有什么用?
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
简单来说:使用AOP可以在不修改源码的基础上,增加新的功能。(显而易见,这对于大型项目开发来说是极具诱惑的。减少了版本更迭时的工作量)
3. AOP是什么原理?
AOP代表的是一个横向的关系,相比于纵向的继承关系更加简便。将“对象”比作一个空心的圆柱体,其中封装的是对象的属性和行为;则面向方面编程的方法,就是将这个圆柱体以切面形式剖开,可以选择性的提供业务逻辑。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹,但完成了效果。
我的理解是 切面可以看作一个载体,在具体实现中的表现是一个切面类,类中包括切点和通知以及织入的动作等
ponitcut (切点):每个类拥有多个连接点,可以看作连接点的集合
joinpoint(连接点):程序执行的某个特定位置,如某个方法调用前后
我的理解:需要增强的目标类中的某个位置,及增强在这个位置调用
通过JDK动态代理和CGLib代理在运行时期在对象初始化阶段织入代码的
即声明的类, 增加@Component @Aspect 两个注解,Springboot中要引入spring-boot-stater-aop依赖包
@Component :告诉Spring 容器要扫描这个类
@Aspect:告诉Spring 这个类是个切面类
定义切点,并定义切点在那些地方执行,采用@Pointcut注解
@Pointcut ( public * com.xxx.xxxx.*.(…) )
规则:括号内,修饰符(可以不写但不能用 *) + 返回类型 + 那些包下的类 + 那些方法 + 方法参数
“” 表示不限,“…” 两个点表示参数不限
利用通知的五种注解:@Before,@After,@AfterReturning,@AfterThrowing,@Around 来实现某些切点的增强动作,如@Before(“myPointcut()”),myPointcut为定义的切点
这里创建的Springboot 项目,创建切面类,并在切面类中定义切点以及通知,利用输出日志信息来显示五种通知的不同执行时机。
首先
在Springboot项目中创建一个controller包,并再其下创建一个hellocontroller.java
在这个类的具体代码如下
package com.example.aopdeom.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
public class hellocontroller {
@GetMapping("/hello")
public String hello(@RequestParam("name") String name) {
return "Hello" + name;
}
@GetMapping("/bye")
public String bye(@RequestParam("name") String name,@RequestParam("age") int age) throws IOException {
if(age < 0){
throw new IOException();
}
return "Bye" + name + " age = " + age;
}
}
其次
定义切面类myAdvice
其代码如下,定义了两个切点,并使用五种通知实现了五个通知,切点
myPointcut拦截的是controller包下的所有类的方法,而myPointcut2只针对controller包下bye方法,并且不限参数,及如果有同名而不同参的bye也会拦截。
package com.example.aopdeom;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class myAdvice {
private Logger logger = LoggerFactory.getLogger(myAdvice.class);
//定义切面
@Pointcut(value = "execution( * com.example.aopdeom.controller.*.*(..))")
public void myPointcut(){
}
@Pointcut(value = "execution( * com.example.aopdeom.controller.hellocontroller.bye(..))")
public void myPointcut2(){
}
@Around("myPointcut()")
public Object myLogger(ProceedingJoinPoint pjp) throws Throwable {
String className = pjp.getTarget().getClass().toString();
String methodName = pjp.getSignature().getName();
Object[] array = pjp.getArgs();
ObjectMapper mapper = new ObjectMapper();
logger.info("调用前" + className + ":" + methodName + " 传递的参数:" + mapper.writeValueAsString(array));
Object obj = pjp.proceed();
logger.info("调用后:" + className + ":" + methodName + " 返回值:" + mapper.writeValueAsString(obj));
return obj;
}
@Before("myPointcut2()")
public void myLogger2(JoinPoint pjp) throws Throwable {
String className = pjp.getTarget().getClass().toString();
String methodName = pjp.getSignature().getName();
Object[] array = pjp.getArgs();
ObjectMapper mapper = new ObjectMapper();
logger.info("前置通知:" + className + ":" + methodName + " 传递的参数:" + mapper.writeValueAsString(array));
}
@After("myPointcut2()")
public void myLogger3(JoinPoint pjp) throws Throwable {
String className = pjp.getTarget().getClass().toString();
String methodName = pjp.getSignature().getName();
Object[] array = pjp.getArgs();
ObjectMapper mapper = new ObjectMapper();
logger.info("最终通知" + className + ":" + methodName + " 传递的参数:" + mapper.writeValueAsString(array));
}
@AfterReturning("myPointcut2()")
public void myLogger4(JoinPoint pjp) throws Throwable {
String className = pjp.getTarget().getClass().toString();
String methodName = pjp.getSignature().getName();
Object[] array = pjp.getArgs();
ObjectMapper mapper = new ObjectMapper();
logger.info("返回后通知" + className + ":" + methodName + " 传递的参数:" + mapper.writeValueAsString(array));
}
@AfterThrowing("myPointcut2()")
public void myLogger5(JoinPoint pjp) throws Throwable {
String className = pjp.getTarget().getClass().toString();
String methodName = pjp.getSignature().getName();
Object[] array = pjp.getArgs();
ObjectMapper mapper = new ObjectMapper();
logger.info("异常通知" + className + ":" + methodName + " 传递的参数:" + mapper.writeValueAsString(array));
}
}
测试
对于hello和bye调用时的情况。
输入的参数:
hello
bye
输出的日志:
hello
bye
当产生异常时,各个通知的情况
参数:
日志:
可以看到,产生日常后环绕通知中再方法执行后的日志不再输出,而且返回后通知也不再执行。
以上就是关于AOP的详细知识,以及demo的实现,感谢你的阅读,希望对你有所帮助,有什么建议可以在评论区留言