AOP

本质:Aspect Oriented Programming,面向切面编程;是OOP的一种延伸, 两者互补,降低系统耦合性,提高了代码的利用率;底层基于动态代理(JDK 动态代理和 CGLib 动态代理)和动态字节码技术来实现;

作用:在不修改原有业务代码的情况下添加额外的功能,从而达到将功能性需求与非功能性需求分离的效果;

示例:日志模块、事务模块等

 

基本概念

  • Joint point: 连接点
  • Pointcut: 经过筛选条件后的连接点集合
  • Advice: 增强(通知),即具体要进行的操作
  • Aspect: 切面,即Pointcut + Advice
  • Weaving: 织入
  • Target:目标对象

 

相关注解说明

  • 前置通知(@Before):在目标方法调用之前调用通知(执行前)

  • 后置通知(@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

posted @ 2024-01-16 15:41  先娶国王后取经  阅读(3)  评论(0编辑  收藏  举报