AOP
本质:Aspect Oriented Programming,面向切面编程;是OOP的一种延伸, 两者互补,降低系统耦合性,提高了代码的利用率;底层基于动态代理(JDK 动态代理和 CGLib 动态代理)和动态字节码技术来实现;
作用:在不修改原有业务代码的情况下添加额外的功能,从而达到将功能性需求与非功能性需求分离的效果;
示例:日志模块、事务模块等
基本概念
- Joint point: 连接点
- Pointcut: 经过筛选条件后的连接点集合
- Advice: 增强(通知),即具体要进行的操作
- Aspect: 切面,即Pointcut + Advice
- Weaving: 织入
- Target:目标对象
相关注解说明
-
(执行前)
-
后置通知(@After):在目标方法完成之后调用通知(完成后)
-
环绕通知(@Around):在被通知的方法调用之前和调用之后执行自定义的方法(before前和after后)
-
返回通知(@AfterReturning):在目标方法成功执行之后调用通知(退出时,该方法在after之前)
-
异常通知(@AfterThrowing):在目标方法抛出异常之后
简单Demo
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
自定义注解
便于后续找到切点,其中的参数可以传递给切面
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface MyDataSource {
String value() default "master";
}
定义切面
注意别忘了@Aspect、@Component
import com.example.datasource.annotation.MyDataSource;
import com.example.datasource.config.DynamicDatasourceHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
/**
* @Description : 切面:切换数据源
* @Author : Bruce Lee
* @CreateTime : 2024/4/16
*/
@Aspect
@Component
@Slf4j
public class DynamicDatasourceAspect {
/**
* 定位切点的第一种方式
* @param joinPoint
* @throws Throwable
*/
// @Pointcut("@annotation(com.example.datasource.annotation.MyDataSource)")
// public void dynamicDatasourcePointcut() {
// }
/**
* 定位切点的第二种方式
* @param joinPoint
* @throws Throwable
*/
@Around(value = "@annotation(com.example.datasource.annotation.MyDataSource)")
// @Around("dynamicDatasourcePointcut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
/**
* 获取注解相关属性值
*/
// // 获取类上的注解
// Class<?> targetClass = joinPoint.getTarget().getClass();
// MyDataSource annotation = targetClass.getAnnotation(MyDataSource.class);
// 获取方法上的注解
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
MyDataSource annotationMethod = methodSignature.getMethod().getAnnotation(MyDataSource.class);
if (annotationMethod != null){
String dataSource = annotationMethod.value();
// 切换数据源
DynamicDatasourceHolder.setDataSource(dataSource);
}
// 执行方法
joinPoint.proceed(joinPoint.getArgs());
// 切换回默认数据源
DynamicDatasourceHolder.removeDataSource();
}
}
定位目标方法
@Service
public class DataService {
@Autowired
private UserMapper userMapper;
// @MyDataSource("master")
@MyDataSource("slaver")
public void test()
{
userMapper.insertUser("aop", "aop");
System.out.println("动态数据源切换成功!");
}
}
注意事项
单元测试类中无法使用AOP
网上有种说法:在单元测试的类上加注解@EnableAspectJAutoProxy(true),但亲测无效
本质:Spring aop 是针对 Spring 的 bean 进行 AOP, 而 @Test 单元测试类不是Spring 的 Bean, 所以无法 aop.
解决方案
多封装一层,如下示例
@Service
public class DataService {
@Autowired
private UserMapper userMapper;
// @MyDataSource("master")
@MyDataSource("slaver")
public void test()
{
userMapper.insertUser("aop", "aop");
System.out.println("动态数据源切换成功!");
}
}
Ioc
是一种思想,而不是一种具体的实现.
控制反转,将对象的控制权交由spring框架进行管理 ,
作用:不用程序员手动new对象,而是直接注入@Autowired
参考文章
【1】AOP基本概念
【2】https://blog.csdn.net/qq_35757473/article/details/123818063
【3】代理模式Demo