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("动态数据源切换成功!");
}
}
AOP只有在外部调用代理对象的方法时才会生效
/**
* 自调用导致AOP不生效,从而事务不生效
*/
@Service
public class OrderService {
@Transactional
public void methodA() {
System.out.println("方法A:事务开始");
methodB(); // 内部调用 methodB(),不会触发事务代理
System.out.println("方法A:事务提交");
}
@Transactional
public void methodB() {
System.out.println("方法B:事务生效?");
}
}
Ioc
是一种思想,而不是一种具体的实现.
控制反转,将对象的控制权交由spring框架进行管理 ,
作用:不用程序员手动new对象,而是直接注入@Autowired
参考文章
【1】AOP基本概念
【2】https://blog.csdn.net/qq_35757473/article/details/123818063
【3】代理模式Demo
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
2023-01-16 Nginx