AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
白话(自我理解):就是对符合切入点表示的方法进行功能扩展
因为这里使用的SpringBoot项目所以导入SpringBoot中的依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
业务层,继承mybatis-plus
package com.sky.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sky.pojo.SysUser;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
public interface SysUserService extends IService<SysUser> {
void add();
void delete();
void updateUser();
SysUser queryById(Integer id);
}
实现层
package com.sky.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sky.mapper.SysUserMapper;
import com.sky.pojo.SysUser;
import com.sky.service.SysUserService;
import org.springframework.stereotype.Service;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
implements SysUserService {
@Override
public void add() {
System.out.println("add方法调用");
}
@Override
public void delete() {
System.out.println("delete方法调用");
}
@Override
public void updateUser() {
System.out.println("updateUser方法调用");
}
@Override
public SysUser queryById(Integer id) {
System.out.println("queryById方法调用");
return null;
}
}
动作关键字(访问修饰符 返回值 包名.类/接口.方法名(参数)异常名)
如果把包名写死,那么就需要写特别多的切入点表达式,所以一般我们都是使用通配符来描述切入点,可以快速大量匹配连接点方法
execution(public * com.sky.*.UserService.select*(*))
execution(public * com.sky..UserService.select*(..))
execution(public * com..*Service+.select*(..))
书写技巧
| 通知类型 | 连接点 |
|---|---|
| 前置通知 | 方法前 |
| 后置通知 | 方法后,发生异常不执行 |
| 环绕通知 | 方法前后 |
| 返回后通知 | 方法后,如果发生异常不会执行 |
| 抛出异常后通知 | 只有发生异常后会通知 |
package com.sky.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@Aspect
@Component
public class LogAspect {
/** 切入点表达式 */
@Pointcut("execution(* com.sky.service.*Service.*(..))")
public void logPointCut(){}
/** 前置通知 */
@Before("logPointCut()")
public void before(){
System.out.println("前置通知");
}
}
测试:
package com.sky;
import com.sky.service.SysUserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@SpringBootTest
public class ApplicationTest {
@Autowired
private SysUserService sysUserService;
@Test
public void testBefore(){
sysUserService.add();
}
}
结果:
前置通知
add方法调用
package com.sky.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@Aspect
@Component
public class LogAspect {
/** 切入点表达式 */
@Pointcut("execution(* com.sky.service.*Service.*(..))")
public void logPointCut(){}
/** 前置通知 */
@Before("logPointCut()")
public void before(){
System.out.println("前置通知");
}
/** 后置通知 */
@After("logPointCut()")
public void after(){
// int i = 1/0;
System.out.println("后置通知");
}
}
结果:
前置通知
add方法调用
后置通知
如果我们在后置通知中埋了一个异常,那么会怎么样呢?
package com.sky.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@Aspect
@Component
public class LogAspect {
/** 切入点表达式 */
@Pointcut("execution(* com.sky.service.*Service.*(..))")
public void logPointCut(){}
/** 前置通知 */
@Before("logPointCut()")
public void before(){
System.out.println("前置通知");
}
/** 后置通知 */
@After("logPointCut()")
public void after(){
int i = 1/0;
System.out.println("后置通知");
}
}
结果:发生异常不执行
前置通知
add方法调用
java.lang.ArithmeticException: / by zero
package com.sky.aspect;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@Aspect
@Component
public class LogAspect {
/** 切入点表达式 */
@Pointcut("execution(* com.sky.service.*Service.*(..))")
public void logPointCut(){}
/** 环绕通知 */
@Around("logPointCut()")
public Object around(){
System.out.println("环绕通知");
return null;
}
}
调用:
package com.sky;
import com.sky.service.SysUserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@SpringBootTest
public class ApplicationTest {
@Autowired
private SysUserService sysUserService;
@Test
public void testBefore(){
sysUserService.add();
}
}
结果:
环绕通知
为什么这里的结果只有环绕通知中的方法呢,而add方法没有执行呢?
因为环绕通知和其他的通知不同,需要自己调用
环绕通知的正确写法:
package com.sky.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@Aspect
@Component
public class LogAspect {
/** 切入点表达式 */
@Pointcut("execution(* com.sky.service.*Service.*(..))")
public void logPointCut(){}
/** 环绕通知 */
@Around("logPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知前置");
Object result = joinPoint.proceed();
System.out.println("环绕通知后置");
return result;
}
}
记住一定要将结果返回,不然是没有执行结果的
Object result = joinPoint.proceed();
return result;
介绍ProceedingJoinPoint:
getArgs:可以获取方法的参数
getTarget: 可以获取目标对象,通过反射可以获取其他信息
Object[] args = joinPoint.getArgs();
System.out.println(args);
Object target = joinPoint.getTarget();
System.out.println(target.getClass().getName());
结果:
环绕通知前置
add方法调用
[Ljava.lang.Object;@3af36922
com.sky.service.impl.SysUserServiceImpl
环绕通知后置
和后置通知类似,略
发生异常时,触发:
package com.sky.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sky.mapper.SysUserMapper;
import com.sky.pojo.SysUser;
import com.sky.service.SysUserService;
import org.springframework.stereotype.Service;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
implements SysUserService {
@Override
public void add() {
System.out.println("add方法调用");
int i = 1 /0;
}
@Override
public void delete() {
System.out.println("delete方法调用");
}
@Override
public void updateUser() {
System.out.println("updateUser方法调用");
}
@Override
public SysUser queryById(Integer id) {
System.out.println("queryById方法调用");
return null;
}
}
切面类
package com.sky.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@Aspect
@Component
public class LogAspect {
/** 切入点表达式 */
@Pointcut("execution(* com.sky.service.*Service.*(..))")
public void logPointCut(){}
/** 异常通知 */
@AfterThrowing(value = "logPointCut()",throwing = "t")
public void afterThrowing(Throwable t){
System.out.println("异常通知"+t.getMessage());
}
}
调用:
package com.sky;
import com.sky.service.SysUserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/9/6
*/
@SpringBootTest
public class ApplicationTest {
@Autowired
private SysUserService sysUserService;
@Test
public void testBefore(){
sysUserService.add();
}
}
结果:
add方法调用
异常通知/ by zero
java.lang.ArithmeticException: / by zero