首页 > 其他分享 >AOP

AOP

时间:2024-10-18 13:11:50浏览次数:1  
标签:service 通知 example public AOP com AspectJ

Spring AOP

  • OOP:Object-Oriented Programming,面向对象编程;AOP:Aspect-Oriented Programming,面向切面编程
  • Advisor:spring 自己的 AOP 组件;AspectJ :三方实现的 AOP 组件
  • 底层对 bean 创建代理对象,达到增强目的。如果目标 bean 实现了接口,就用 JDK 动态代理;如果没有,就用 CGLIB
  • 声明式事务 @Transactional 注解底层就使用 AOP 实现事务提交回滚的

AspectJ VS Advisor

说人话 AspectJ 更强大、性能更好、可以不依赖 spring;总结就是:简单场景使用 Advisor,复杂场景使用 AspectJ。下面是两者详细对比

AspectJ Advisor
定义 AspectJ 是一个完整的 AOP 框架,支持面向切面编程的编译时和运行时特性。 Advisor 是 Spring AOP 的一个概念,表示一个切面中的通知和切入点的组合。
编程模型 提供了丰富的语言支持,包括注解、XML 配置和代码注入。 主要通过注解(如 @Before, @After)或 XML 配置来定义。
切面实现 可以在编译时、类加载时或运行时进行织入,支持复杂的切点表达式。 主要通过 Spring 容器在运行时使用动态代理实现,切点表达式相对简单。
织入方式 支持编译时织入、类加载时织入和运行时织入。 仅支持运行时织入,通常使用 Java 动态代理或 CGLIB 代理。
切点表达式 使用 AspectJ 表达式,支持更复杂的匹配规则。 使用 Spring AOP 的切点表达式,功能相对有限。
应用范围 可用于任何 Java 应用,包括独立 Java 程序和 Spring 应用。 主要用于 Spring 应用中,与 Spring 生态系统紧密集成。
支持的通知类型 支持多种通知类型,如 @Before, @After, @Around, @AfterReturning, @AfterThrowing 支持 Before, After, Around 和其他通知类型,通常由 Advisor 定义。
性能 AspectJ 的性能较高,因为它可以在编译时进行优化。 Spring AOP 的性能相对较低,因为它依赖于运行时代理。
易用性 对于初学者来说,学习曲线可能较陡峭。 更加简单直观,易于上手。

AOP 概念

概念 定义 说人话
切面 (Aspect) 定义了一个横切关注点的模块,包含通知和切入点。 就是我们定义的切面类
连接点 (Join Point) 程序执行过程中一个可以插入通知的点,例如方法调用、方法执行、对象创建等。 目标对象所有方法
切入点 (Pointcut) 定义在哪些连接点上应用通知的规则或表达式,决定通知的应用范围。 目标对象被增强的方法
通知 (Advice) 在连接点上执行的行为,分为前置通知、后置通知、返回通知、异常通知和环绕通知。
织入 (Weaving) 将切面与目标对象结合的过程,发生在编译时、类加载时或运行时。 将切点和通知组合的过程
目标对象 (Target Object) 被切面影响的对象,通常是包含业务逻辑的类。

通知

通知类型 描述
前置通知 (Before) 在连接点执行之前运行的通知。
后置通知 (After) 在连接点执行之后运行的通知,通常在连接点成功或失败后都会执行。
返回通知 (After Returning) 在连接点成功完成后执行的通知,可以访问连接点的返回值(目标方法执行不能发生异常)。
异常通知 (After Throwing) 在连接点抛出异常时执行的通知,可以访问异常信息。
环绕通知 (Around) 在连接点执行之前和之后都可以执行的通知,允许控制连接点的执行,能够选择不执行连接点的方法(目标方法执行不能发生异常)。

切点表达式

  1. 就是定义切点,也就是指明哪些方法要被增强
  2. Advisor 的切点表达式是 AspectJ 的子集

AspectJ 示例

  1. 常用切点表达式(可以 and or 取反 指定方法返回值 指定参数个数 指定参数类型等等)

    • 某个特殊的方法:com.example.service.UserService 类中所有以 find 开头的公共方法

      execution(public * com.example.service.UserService.find*(..))

    • 类中的所有方法:com.example.service 包下所有类的所有方法

      execution(* com.example.service.*.*(..))

    • 特定参数类型的方法:com.example.service.UserService 类中接受一个 User 对象作为参数的方法

      execution(String com.example.service.UserService.getName(..))

    • com.example.service 包及其子包下的所有类中的所有方法

      execution(* com.example.service..*.*(..))

    • com.example.service 包及其子包下的所有类中的所有方法

      within 不能指定某个方法,本身是类级别的;execution 可以指定某个方法

      如果exection 切某个包及其子包下所有方法就和 within 功能一样了

      within(com.example.service..*)

    • 带有特定注解的方法:被 @Loggable 注解标记的方法

      @annotation(com.example.annotation.Loggable)

    • 结合多个条件:匹配 com.example.service 包中所有被 @Loggable 注解标记的方法

      execution(* com.example.service.*.*(..)) && @annotation(com.example.annotation.Loggable)

    • 有异常抛出的方法:匹配 com.example.service 包下所有方法,并且这些方法抛出 Exception

      execution(* com.example.service.*.*(..)) throws Exception

  2. 两种连接点

    • JoinPoint 获取连接点信息,比如方法、参数、目标对象等,可用于 @Before@After@AfterReturning
    • ProceedingJoinPoint 是 JointPoint 的子接口,额外提供 proceed() 来执行目标方法,仅用于 @Around
// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecution {
}

// 切面类
@Aspect
@Component
public class LoggingAspect {

    // 定义切点
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    // 前通知
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Executing method...");
        System.out.println("Before method: " + joinPoint.getSignature());
    }

    // 后通知
    @After("serviceMethods()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("Method execution completed.");
    }

    // 返回通知
    @AfterReturning(pointcut = "serviceMethods()", JoinPoint joinPoint, returning = "result")
    public void logAfterReturning(Object result) {
        System.out.println("Method returned: " + result);
    }

    // 异常通知
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
    public void logAfterThrowing(Throwable ex) {
        System.out.println("Method threw an exception: " + ex);
    }

    // 环绕通知
    @Around("serviceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before method: " + joinPoint.getSignature());
        Object result = joinPoint.proceed();  // 执行目标方法
        System.out.println("After method: " + joinPoint.getSignature());
        return result;
    }

    // 使用自定义注解的通知
    @Before("@annotation(LogExecution)")
    public void logBeforeAnnotatedMethod() {
        System.out.println("Executing annotated method...");
    }
}

Advisor 示例

Advisor 配合 AspectJ 更优雅,也能不结合 AspectJ 使用

纯粹使用 Advisor 没 AspectJ 方便,通知、切点等都要使用 java 代码实现

结合 AspectJ 的话,既然已经用了 AspectJ 为什么不直接使用要再引入 Advisor 呢

不结合 AspectJ:

  1. 定义通知

    public class LoggingAdvice implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("Method " + invocation.getMethod().getName() + " is being called");
            return invocation.proceed(); // 继续执行目标方法
        }
    }
    
  2. 定义切点

    public class LoggingPointcut implements Pointcut {
        @Override
        public ClassFilter getClassFilter() {
            return ClassFilter.TRUE; // 适用于所有类
        }
    
        @Override
        public MethodMatcher getMethodMatcher() {
            return new NameMatchMethodMatcher() {
                @Override
                public boolean matches(String methodName, Class<?> targetClass) {
                    return methodName.startsWith("get"); // 适用于所有以 "get" 开头的方法
                }
            };
        }
    }
    
  3. 定义切面类

    public class LoggingAdvisor extends DefaultPointcutAdvisor {
        public LoggingAdvisor() {
            super(new LoggingPointcut(), new LoggingAdvice());
        }
    }
    
  4. 注册切面

    @Configuration
    public class AopConfig {
        @Bean
        public LoggingAdvisor loggingAdvisor() {
            return new LoggingAdvisor();
        }
    }
    

JDK动态代理增强 bean

AOP 的原理就是给目标对象创建代理对象,达到增强目标对象方法的目的

如果目标对象实现了接口就是用 JDK 动态代理,如果没实现接口就是用三方的 CGLIB 代理

如果不使用 AOP 想要增强一个 bean 可以这样做:

@Component
public class Test implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    // ApplicationContextAware 感知回调回传 IOC 容器
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    // bean 工厂后置处理器,拦截所有的 bean
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 只增强 User bean
        if (bean instanceof User) {
            User user = (User) applicationContext.getBean("user");
            return Proxy.newProxyInstance(
                    user.getClass().getClassLoader(),
                    user.getClass().getInterfaces(),
                    (Object proxy, Method method, Object[] args) -> {
                        // 如果不是 sayHello 方法不增强(只增强 sayHello 方法)
                        if (!method.getName().equals("sayHello")){
                            return method.invoke(user, args);
                        }
                        System.out.println("执行前.....");
                        Object invoke = method.invoke(user, args); // 执行 bean 本身的方法,这里就是 sayHello
                        System.out.println("执行后.....");
                        return invoke;
                    }
            );
        }
        // 如果 bean 不是 User 直接返回原来的
        return bean;
    }
}

标签:service,通知,example,public,AOP,com,AspectJ
From: https://www.cnblogs.com/cyrushuang/p/18474036

相关文章

  • SpringBoot Aop面向切面编程-快速入门-实战案例
    AOP部分笔记来自黑马程序员。一、AOP概述什么是AOP?AOP英文全称:AspectOrientedProgramming(面向切面编程、面向方面编程),其实说白了,面向切面编程就是面向特定方法编程。那什么又是面向方法编程呢,为什么又需要面向方法编程呢?来我们举个例子做一个说明:比如,我们这里有一个......
  • (系列七).net8 Aop切面编程
    说明  该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发)。   该系统文章,我会尽量说的非常详细,做到不管新手、老手都能看懂。   说明:OverallAuth2.0是一个简单、易懂、功能强大的权限+可视化流程管理系统。友情提醒:本篇文章是属于系......
  • spring上 -基于注解配置bean,动态代理,AOP笔记
     用的是jdk8,spring框架里jar包的下载可以自己搜到注解用到的jar包。  60,注解配置Bean快速入门 基本介绍 代码结构: UserDao.javapackagecom.hspedu.spring.component;importorg.springframework.stereotype.Repository;/**使用@Repository标识该......
  • SpringBootAOP
    ​ 概念1.AspectOrientedProgramming面向切面编程处理面向对象编程中业务需求重复的部分,作为横切面插入到面向对象当中,一般有固定的应用场景,例如日志记录,登录校验,数据验证,统计耗时,事务管理等(AOP是OOP的延伸,简单一句话就是对基于面向对象编程的某些业务方法进行增强......
  • Mybatis-plus 3.5.4 的AOP问题 java.lang.ClassCastException: class org.springfram
    报错,然后我把mapper上的@repository删掉就好了,为什么ChatGPT说:ChatGPT删除@Repository注解后问题解决,可能是与SpringAOP代理机制和MyBatisPlus结合时的一些细节有关。以下是原因分析:@Repository和SpringAOP代理的影响@Repository注解的主要作用是将类标记为持......
  • RocketMq详解:五、SpringBoot+Aop实现RocketMq的幂等
    上一章:《RocketMq详解:四、RocketMq消息的重试》文章目录1什么是幂等2需要进行消息幂等的场景3.如何才能实现消息幂等呢4.RocketMQ场景下如何处理消息幂等4.1消费端常见的幂等操作1.使用唯一标识符2.Redis处理标志位3.分布式锁3.1数据库乐观锁3.2数据库悲观锁3.3Re......
  • [笔记] 使用注解切面(AOP)实现操作日志和数据日志记录
    前言只是一个笔记,肯定有不足的地方,麻烦指出来一起进步.因为是多模块的内部项目,所以不会有高并发.所以是在一个线程内进行的一.枚举操作状态/***操作状态*/publicenumBusinessStatus{/***成功*/SUCCESS,/***失败*......
  • Spring学习——SpringAOP
    0.IOC思想(DI)1.关键注解 @Repository publicclassDeptDaoImpl1implementsDeptDao{} @Repository @Primary publicclassDeptDaoImpl2implementsDeptDao{} @Service publicclassDeptServiceImplimplementsDeptService{ @Autowired @Qulif......
  • Spring 过滤器 拦截器 监听器 Aop
    目录Spring过滤器拦截器监听器Aop1.过滤器2.拦截器3.监听器4.Aop5.参考文档Spring过滤器拦截器监听器Aop1.过滤器1.简介 过滤器Filter用于对数据进行过滤和预处理 过滤器只能在请求前后使用 依赖于servlet容器基于函数回调实现其生命周期由servlet容器管......
  • 利用AOP切面实现多数据源切换
    引言在现代企业应用中,使用多个数据库已成为一种常见的需求,尤其是大数据量、多系统集成的场景。多数据源的使用可以帮助企业更好地管理数据、提高系统的性能和扩展性。然而,随着多数据源应用的增多,如何在程序中动态切换数据源成为了一个挑战。传统的硬编码方式虽然可以实现数......