一、什么是AOP:
AOP又称动态代理,指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程模式
二、AOP示例代码
第一步、导入AOP相应的依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
第二步、创建业务逻辑类
package com.example.demo.service;
public class BookService {
public int save(String name)
{
System.out.println(name+":业务存储逻辑");
return 1;
}
}
第三步、创建切面类
定义一个日志切面类(LogAspect),切面类里面的方法需要动态感知BookService的方法运行到哪里,然后执行通知方法: 前置通知(@Before):logStart:在目标方法运行之前运行 参数列表传入joinPoint可获取到方法的相关属性, 且该参数必须放在第一个参数,否则无法识别 后置通知(@After):logEnd:在目标方法运行之后运行,无论方法正常结束还是异常结束 返回通知(@AfterReturning(returning可以指定封装返回值的参数)) 异常通知(@AfterThrowing(logException可以获取异常信息)) 环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.proceed())
JoinPoint:在切面方法中添加JoinPoint参数,就可以获取到连接点的信息
常用api:
方法名 功能 Signature getSignature(); 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息 Object[] getArgs(); 获取传入目标方法的参数对象 Object getTarget(); 获取被代理的对象 Object getThis(); 获取代理对象
package com.example.demo.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Modifier;
import java.util.Arrays;
/**
* 切面类
*/
@Aspect
public class LogAspect {
//抽取公共的切入点表达式
//1、本类引用
@Pointcut("execution(public int com.example.demo.service.BookService.*(..))")
public void pointCut(){}
//在目标方法之前切入:切入点表达式(指定在哪个方法切入)
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
System.out.println("目标方法名为:" + joinPoint.getSignature().getName());
System.out.println("目标方法所属类的简单类名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
System.out.println("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
System.out.println("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
//获取传入目标方法的参数
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
System.out.println("第" + (i+1) + "个参数为:" + args[i]);
}
System.out.println("被代理的对象:" + joinPoint.getTarget());
System.out.println("代理对象自己:" + joinPoint.getThis());
System.out.println("aop切面before:"+
joinPoint.getSignature().getName()+
"方法运行。。。参数列表是:{"+ Arrays.asList(joinPoint.getArgs())+"}");
}
//在目标方法之后切入
@After("pointCut()")
public void logEnd(){
System.out.println("aop切面,after");
}
@AfterReturning(value = "pointCut()",returning ="result" )
public void logReturn(Object result){
System.out.println("除法正常返回。。运行结果:{"+result+"}");
}
@AfterThrowing(value = "pointCut()",throwing ="exception")
public void LogException(Exception exception){
System.out.println("除法异常。。异常信息:{"+exception+"}");
}
/**
* 环绕方法,可自定义目标方法执行的时机
* @param pjd JoinPoint的子接口,添加了
* Object proceed() throws Throwable 执行目标方法
* Object proceed(Object[] var1) throws Throwable 传入的新的参数去执行目标方法
* 两个方法
* @return 此方法需要返回值,返回值视为目标方法的返回值
*/
// @Around("pointCut()")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
try {
//前置通知
System.out.println("目标方法执行前...");
//执行目标方法
//result = pjd.proeed();
//用新的参数值执行目标方法
result = pjd.proceed(new Object[]{"小米"});
//返回通知
System.out.println("目标方法返回结果后...");
} catch (Throwable e) {
//异常通知
System.out.println("执行目标方法异常后...");
throw new RuntimeException(e);
}
//后置通知
System.out.println("目标方法执行后...");
return result;
}
}
四、创建配置类
我们将上面的切面类和业务逻辑类注册到spring容器中,让spring进行管理
需要给配置类加一个@EnableAspectJAutoProxy【开启基于注解的aop模式】
//需要给配置类加一个@EnableAspectJAutoProxy【开启基于注解的aop模式】
@EnableAspectJAutoProxy
@Configuration
public class MainConfig {
//业务逻辑类加入容器中
@Bean
public BookService bookService()
{
return new BookService();
}
//切面类加入容器中
@Bean
public LogAspect logAspect(){
return new LogAspect();
}
}
五、编写测试类
@SpringBootTest
class DemoApplicationTests {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
@Test
void contextLoads() {
BookService bookService = applicationContext.getBean(BookService.class);
bookService.save("小明");
}
}
测试结果
目标方法名为:save
目标方法所属类的简单类名:BookService
目标方法所属类的类名:com.example.demo.service.BookService
目标方法声明类型:public
第1个参数为:小明
被代理的对象:com.example.demo.service.BookService@1aabf50d
代理对象自己:com.example.demo.service.BookService@1aabf50d
aop切面before:save方法运行。。。参数列表是:{[小明]}
小明:业务存储逻辑
aop切面,after
除法正常返回。。运行结果:{1}
有方法有异常,则结果如下
目标方法名为:save
目标方法所属类的简单类名:BookService
目标方法所属类的类名:com.example.demo.service.BookService
目标方法声明类型:public
第1个参数为:小明
被代理的对象:com.example.demo.service.BookService@1aabf50d
代理对象自己:com.example.demo.service.BookService@1aabf50daop切面before:save方法运行。。。参数列表是:{[小明]}
小明:业务存储逻辑
aop切面,after
除法异常。。异常信息:{java.lang.ArithmeticException: / by zero}
注释掉其他通知,并开启around环绕通知,测试结果
目标方法执行前...
小米:业务存储逻辑
目标方法返回结果后...
目标方法执行后...
总结:
AOP功能分三步:
1、创建切面类、并告诉spring哪个是切面类(@Aspect)
2、在切面类上的每一个通知方法标注通知注解,告诉spring何时何地运行(切入点表达式)
3、将业务逻辑组件和切面类都加入到容器中,开启基于注解的aop模式@EnableAspectJAutoProxy