系列十一、AOP

news2025/1/23 9:28:21

一、概述

1.1、官网       

        AOP的中文名称是面向切面编程或者面向方面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

1.2、通俗描述

        不通过修改源代码的方式,在主干功能里面添加新功能。

1.3、案例说明

二、底层原理 

        AOP底层使用动态代理技术。有两种情况的动态代理:

  • 有接口情况:JDK动态代理
  • 无接口情况:CGLIB动态代理,基于类的继承,使用代理子类的形式,对父类的方法进行重写,完成方法的增强

2.1、JDK动态代理案例代码

2.1.1、UserDao

package org.star.dao;

public interface UserDao {

    /**
     * 两数相加
     * @param num1
     * @param num2
     * @return
     */
    int add(int num1,int num2);

    /**
     * 根据id修改
     * @param id
     * @return
     */
    int update(long id);

}

2.1.2、UserDaoImpl

package org.star.dao.impl;

import org.star.dao.UserDao;

public class UserDaoImpl implements UserDao {

    @Override
    public int add(int num1, int num2) {
        return num1 + num2;
    }

    @Override
    public int update(long id) {
        // do your business...
        return 1;
    }
}

 2.1.3、MyInvocationHandler

package org.star.handler;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

@Slf4j
public class MyInvocationHandler implements InvocationHandler {

    private Object obj;

    public MyInvocationHandler(Object obj) {
        this.obj = obj;
    }

    /**
     * 增强逻辑
     *
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 目标方法执行之前
        log.info("目标方法执行之前执行,目标方法名称:{},入参:{}", method.getName(), Arrays.toString(args));
        // 目标方法执行
        Object result = method.invoke(obj, args);
        // 目标方法执行之后
        log.info("目标方法执行之后执行,result:{},obj:{}",result,obj.getClass());
        return result;
    }
}

2.1.4、测试类

package org.star;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.star.dao.UserDao;
import org.star.dao.impl.UserDaoImpl;
import org.star.handler.MyInvocationHandler;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * Unit test for simple App.
 */
@Slf4j
public class AppTest {

    @Test
    public void testJDKProxy1() {
        Class[] interfaces = {UserDao.class};
        UserDaoImpl userDaoImpl = new UserDaoImpl();
        UserDao userDao = (UserDao) Proxy.newProxyInstance(AppTest.class.getClassLoader(),interfaces,new MyInvocationHandler(userDaoImpl));
        int result = userDao.add(3,5);
        log.info("result:{}",result);
    }

    @Test
    public void testJDKProxy2() {
        Class[] interfaces = {UserDao.class};
        UserDao userDao = (UserDao) Proxy.newProxyInstance(AppTest.class.getClassLoader(), interfaces, new InvocationHandler() {
            UserDaoImpl userDaoImpl = new UserDaoImpl();
            @Override
            public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
                // 目标方法执行之前
                log.info("目标方法执行之前执行,目标方法名称:{},入参:{}", method.getName(), Arrays.toString(args));
                // 目标方法执行
                Object result = method.invoke(userDaoImpl, args);
                // 目标方法执行之后
                log.info("目标方法执行之后执行,obj:{}",userDaoImpl.getClass());
                return result;
            }
        });
        int result = userDao.add(5,5);
        log.info("result:{}",result);
    }

}

2.2、Cglib动态代理案例代码

2.2.1、pom

<dependencies>
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.1</version>
    </dependency>
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.19</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.16</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>


    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
    </dependency>
    <!-- 普通maven项目中使用Sl4j注解 -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.32</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.10</version>
    </dependency>

</dependencies>

2.2.2、Panda

package org.star.cglib;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author: 
 * @Date: 2023/8/28 14:32
 * @Description:
 */
@Slf4j
public class Panda {

    public void eat() {
        log.info("熊猫吃竹子");
    }

}

2.2.3、App

package org.star;

import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.star.cglib.Panda;

import java.lang.reflect.Method;

@Slf4j
public class App {
    public static void main( String[] args ) {
        Panda panda = new Panda();
        // 创建代理类
        Panda proxyPanda = (Panda) Enhancer.create(Panda.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                log.info("target method before...");
                Object result = method.invoke(panda, args);
                log.info("target method after...");
                return result;
            }
        });
        // 通过代理类调用目标方法实现增强
        proxyPanda.eat();
    }
}

三、AOP术语

3.1、连接点

        类里面哪些方法可以被增强,这些被增强的方法就被称为连接点。

3.2、切入点

        类里面实际真正被增强的方法,称为切入点。

3.3、通知(增强)

        实际增强的逻辑部分称为通知(增强),通知按照作用于目标方法的位置不同,可以分为如下5种类型。

3.3.1、前置通知

        前置通知因在目标方法之前执行,故名前置通知,注解为@Before。

3.3.2、后置通知

        后置通知又叫正常结束通知,即目标方法正常结束后执行,常用于做一些善后的操作,注解为@AfterReturning。

注意事项:如果方法有返回值,可以在通知中获取到方法的返回值;

3.3.3、异常通知

        异常通知故名思议是指当目标方法运行期间出现异常时才会执行,注解为@AfterThrowing。

3.3.4、最终通知

        最终通知有点儿类似于try..catch...finally中的finally代码块,不管目标方法运行期间是否出现异常,都会执行,用于兜底。注解为@After

3.3.5、环绕通知

        环绕通知是上述四个通知的集大成者,一个通知等价于上述四个通知。注解为@Around。

注意事项:

        (1)需要通过代码调用方法,在方法执行的周围进行增强;

        (2)使用代码调用连接点方法:proceedingJoinPoint.proceed();

        (3)环绕通知需要有返回值,此返回值就是方法调用的结果;

3.4、切面

        把通知应用到切入点的过程称为切面,是动作。

四、AOP案例

4.1、基于xml配置文件实现(maven)

4.1.1、pom

<dependencies>
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.1</version>
    </dependency>
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.19</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.16</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>


    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.76</version>
    </dependency>

    <!-- 普通maven项目中使用Sl4j注解 -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.32</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.10</version>
    </dependency>

</dependencies>

4.1.2、ATM

package org.star.component;

import lombok.extern.slf4j.Slf4j;

/**
 * 目标
 */
@Slf4j
public class ATM {

    public int take(int money) {
        log.info("取钱方法正在执行...");
        if (money == 100) {
            throw new RuntimeException("自定义的异常");
        }
        return 10000;
    }

}

4.1.3、LogAspect

package org.star.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 增强类
 */
@Slf4j
public class LogAspect {

    /**
     * 前置通知:方法执行之前执行
     */
    public void beforeLog() {
        log.info("前置通知执行...");
    }

    /**
     * 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行
     */
    public void afterReturningLog(Object result) {
        log.info("后置通知(正常结束通知)执行...,返回值:{}",result);
    }

    /**
     * 异常通知
     */
    public void afterThrowingLog(Exception ex) {
        log.info("异常通知(目标方法出现异常时执行)...,异常信息:{}",ex.getMessage());
    }

    /**
     * 最终通知:不管目标方法执行期间是否有异常,都会执行
     *
     */
    public void afterLog() {
        log.info("最终通知(不管目标方法执行期间是否有异常,都会执行)...");
    }

    /**
     * 环绕通知
     * @param joinPoint
     * @return
     */
    public Object aroundLog(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            log.info("around before");
            result = joinPoint.proceed(); // 调用目标方法返回的值
            log.info("around afterReturning");
        } catch (Throwable e) {
            log.info("around afterThrowing");
        } finally {
            log.info("around after");
        }
        return result;
    }

}

4.1.4、applicationContext

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- bean配置 -->
    <bean id="atm" class="org.star.component.ATM"></bean>
    <bean id="logAspect" class="org.star.aspect.LogAspect"></bean>

    <!-- aop配置 -->
    <aop:config>
        <!--
            切入点
            说明:execution中的第一个参数代表返回值,*代表所有
        -->
        <aop:pointcut id="p" expression="execution(* org.star.component.ATM.take(..))"/>
        <!-- 切面 -->
        <aop:aspect ref="logAspect">
            <!-- 增强作用在具体的方法上 -->
            <!-- 前置通知 -->
            <!--<aop:before method="beforeLog" pointcut-ref="p"></aop:before>-->

            <!-- 后置通知(正常结束通知) -->
            <!--<aop:after-returning method="afterReturningLog" pointcut-ref="p" returning="result"></aop:after-returning>-->

            <!-- 异常通知 -->
            <!--<aop:after-throwing method="afterThrowingLog" pointcut-ref="p" throwing="ex"></aop:after-throwing>-->

            <!-- 最终通知 -->
            <!--<aop:after method="afterLog" pointcut-ref="p"></aop:after>-->

            <!-- 环绕通知 -->
            <aop:around method="aroundLog" pointcut-ref="p"></aop:around>

        </aop:aspect>
    </aop:config>

</beans>

4.1.5、AppTest

package org.star;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.star.component.ATM;

/**
 * Unit test for simple App.
 */
@Slf4j
public class AppTest {

    @Test
    public void aopTest() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        ATM atm = context.getBean(ATM.class);
        int take = atm.take(100);
        log.info("aopTest take:{}",take);
    }

}

4.2、基于全注解方式实现(maven)

4.2.1、pom

<dependencies>
	<dependency>
	  <groupId>cglib</groupId>
	  <artifactId>cglib</artifactId>
	  <version>3.1</version>
	</dependency>
	<dependency>
	  <groupId>aopalliance</groupId>
	  <artifactId>aopalliance</artifactId>
	  <version>1.0</version>
	</dependency>
	<dependency>
	  <groupId>org.aspectj</groupId>
	  <artifactId>aspectjweaver</artifactId>
	  <version>1.9.19</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-aop</artifactId>
	  <version>5.2.5.RELEASE</version>
	</dependency>
	<dependency>
	  <groupId>commons-logging</groupId>
	  <artifactId>commons-logging</artifactId>
	  <version>1.1.1</version>
	</dependency>
	<dependency>
	  <groupId>com.alibaba</groupId>
	  <artifactId>druid</artifactId>
	  <version>1.2.16</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-beans</artifactId>
	  <version>5.2.5.RELEASE</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-context</artifactId>
	  <version>5.2.5.RELEASE</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-core</artifactId>
	  <version>5.2.5.RELEASE</version>
	</dependency>
	<dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-expression</artifactId>
	  <version>5.2.5.RELEASE</version>
	</dependency>


	<dependency>
	  <groupId>junit</groupId>
	  <artifactId>junit</artifactId>
	  <version>4.13.2</version>
	</dependency>

	<dependency>
	  <groupId>com.alibaba</groupId>
	  <artifactId>fastjson</artifactId>
	  <version>1.2.76</version>
	</dependency>

	<!-- 普通maven项目中使用Sl4j注解 -->
	<dependency>
	  <groupId>org.projectlombok</groupId>
	  <artifactId>lombok</artifactId>
	  <version>1.18.22</version>
	</dependency>
	<dependency>
	  <groupId>org.slf4j</groupId>
	  <artifactId>slf4j-api</artifactId>
	  <version>1.7.32</version>
	</dependency>
	<dependency>
	  <groupId>ch.qos.logback</groupId>
	  <artifactId>logback-classic</artifactId>
	  <version>1.2.10</version>
	</dependency>

</dependencies>

4.2.2、MySpringConfig

package org.star.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = {"org.star"})
@EnableAspectJAutoProxy(proxyTargetClass = true) // 启用CGLB动态代理
public class MySpringConfig {

}

4.2.3、ATM

package org.star.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class ATM {

    public int take(int money) {
        log.info("取钱方法正在执行...");
        if (money == 100) {
            throw new RuntimeException("自定义的异常");
        }
        return 10000;
    }

}

4.2.4、LogAspect

package org.star.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@Aspect // 生成代理对象
public class LogAspect {

    /**
     * 相同的切入点进行抽取
     */
    @Pointcut(value = "execution(* org.star.component.ATM.take(..))")
    public void commonPoint() {

    }

    /**
     * 前置通知:方法执行之前执行
     */
    // @Before(value = "execution(* org.star.component.ATM.take(..))")
    // @Before(value = "commonPoint()")
    public void beforeLog() {
        log.info("前置通知执行...");
    }

    /**
     * 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行
     */
    // @AfterReturning(value = "execution(* org.star.component.ATM.take(..))",returning = "result")
    // @AfterReturning(value = "commonPoint()",returning = "result")
    public void afterReturningLog(Object result) {
        log.info("后置通知(正常结束通知)执行...,返回值:{}",result);
    }

    // @AfterThrowing(value = "execution(* org.star.component.ATM.take(..))",throwing = "ex")
    // @AfterThrowing(value = "commonPoint()",throwing = "ex")
    public void afterThrowingLog(Exception ex) {
        log.info("异常通知(目标方法出现异常时执行)...,异常信息:{}",ex.getMessage());
    }

    /**
     * 最终通知:不管目标方法执行期间是否有异常,都会执行
     * 思考:目标方法正常执行结束,控制台打印的日志如下:
     *      07:55:40.702 [main] INFO org.star.aspect.LogAspect - 前置通知执行...
     *      07:55:40.717 [main] INFO org.star.component.ATM - 取钱方法正在执行...
     *      07:55:40.717 [main] INFO org.star.aspect.LogAspect - 最终通知(不管目标方法执行期间是否有异常,都会执行)...
     *      07:55:40.717 [main] INFO org.star.aspect.LogAspect - 后置通知(正常结束通知)执行...,返回值:10000
     *  为什么最终通知先于后置通知执行?
     *  答:后置通知是在方法执行return后执行的,是不可能修改方法的返回值的,而最终通知是在目标方法返回前执行的,即便目标方法出现抛出异常,最终通知
     *  也会执行,但当抛出异常时,后置通知将不会执行。
     */
    // @After(value = "execution(* org.star.component.ATM.take(..))")
    // @After(value = "commonPoint()")
    public void afterLog() {
        log.info("最终通知(不管目标方法执行期间是否有异常,都会执行)...");
    }

    /**
     * 环绕通知
     * @param joinPoint
     * @return
     */
    // @Around(value = "execution(* org.star.component.ATM.take(..))")
    @Around(value = "commonPoint()")
    public Object aroundLog(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            log.info("around before");
            result = joinPoint.proceed(); // 调用目标方法返回的值
            log.info("around afterReturning result:{}",result);
        } catch (Throwable e) {
            log.info("around afterThrowing");
        } finally {
            log.info("around after");
        }
        return result;
    }

}

4.2.5、AppTest

package org.star;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.star.component.ATM;
import org.star.config.MySpringConfig;

/**
 * Unit test for simple App.
 */
@Slf4j
public class AppTest {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
        ATM atm = context.getBean(ATM.class);
        log.info("atm:{}",atm);
        if (atm != null) {
            atm.take(1000);
        }
    }

}

4.3、基于自定义注解方式实现(springboot)

4.3.1、pom

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-aop</artifactId>
	</dependency>

	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<optional>true</optional>
	</dependency>
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-collections4</artifactId>
		<version>4.4</version>
	</dependency>
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-lang3</artifactId>
	</dependency>
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>fastjson</artifactId>
		<version>2.0.6</version>
	</dependency>
	<dependency>
		<groupId>cn.hutool</groupId>
		<artifactId>hutool-all</artifactId>
		<version>5.7.22</version>
	</dependency>

</dependencies>

4.3.2、LogAnnotation

package org.star.annotation;

import java.lang.annotation.*;

/**
 * @Author: liuhaibo
 * @Date: 2023/8/28 10:16
 * @Description:
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {

    String value() default "";

}

4.3.3、UserVO

package org.star.entity.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @Author: liuhaibo
 * @Date: 2023/8/28 10:22
 * @Description:
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserVO implements Serializable {

    /**
     * 编号
     */
    private Integer id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

}

4.3.4、UserService

package org.star.service;

import org.star.entity.vo.UserVO;

import java.util.List;

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/8/28 7:55
 * @Description:
 */
public interface UserService {

    /**
     * 查询所有用户
     * @return
     */
    List<UserVO> listAllUser();

    /**
     * 根据id查询用户
     * @param id
     * @return
     */
    UserVO getUserById(Integer id);

    /**
     * 添加用户
     * @param param
     * @return
     */
    boolean saveUser(UserVO param);

    /**
     * 修改用户
     * @param param
     * @return
     */
    boolean editUser(UserVO param);

    /**
     * 根据id删除用户
     * @param id
     * @return
     */
    boolean delUserById(Integer id);
}

4.3.5、UserServiceImpl

package org.star.service.impl;

import org.springframework.stereotype.Component;
import org.star.entity.vo.UserVO;
import org.star.service.UserService;

import java.util.Arrays;
import java.util.List;

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/8/28 7:55
 * @Description:
 */
@Component
public class UserServiceImpl implements UserService {

    @Override
    public List<UserVO> listAllUser() {
        List<UserVO> users = Arrays.asList(
                new UserVO(1, "张三", 23),
                new UserVO(2, "李四", 24),
                new UserVO(3, "王五", 25)
        );
        return users;
    }

    @Override
    public UserVO getUserById(Integer id) {
        return new UserVO(1, "张三", 23);
    }

    @Override
    public boolean saveUser(UserVO param) {
        return true;
    }

    @Override
    public boolean editUser(UserVO param) {
        return true;
    }

    @Override
    public boolean delUserById(Integer id) {
        return true;
    }
}

4.3.6、UserController

package org.star.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.star.annotation.LogAnnotation;
import org.star.entity.vo.UserVO;
import org.star.service.UserService;

import java.util.List;

/**
 * @Author: liuhaibo
 * @Date: 2023/8/28 10:19
 * @Description:
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @LogAnnotation(value = "查询所有用户")
    @GetMapping("/listAllUser")
    public List<UserVO> listAllUser() {
        return userService.listAllUser();
    }

    @GetMapping("/getUserById/{id}")
    public UserVO getUserById(@PathVariable("id") Integer id) {
        return userService.getUserById(id);
    }

    @LogAnnotation(value = "添加用户")
    @PostMapping("/saveUser")
    public boolean saveUser(@RequestBody UserVO param) {
        return userService.saveUser(param);
    }

    @LogAnnotation(value = "修改用户")
    @PutMapping("/editUser")
    public boolean editUserById(@RequestBody UserVO param) {
        return userService.editUser(param);
    }

    @LogAnnotation(value = "根据ID删除用户")
    @DeleteMapping("/delUserById/{id}")
    public boolean delUserById(@PathVariable("id") Integer id) {
        return userService.delUserById(id);
    }

}

4.3.7、测试

        启动服务,在Postman中访问接口,观察控制台的日志输出。

五、切入点表达式

5.1、概述

5.2、语法

execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))

5.3、案例

表达式含义
execution(* org.star.ArithmeticCalculator.*(..))(1)ArithmeticCalculator接口中声明的所有方法
(2)第一个*代表任意修饰符及任意返回值
(3)第二个*代表任意方法
(4)..代表匹配任意数量、任意类型的参数。若目标类、接口与该切面类在同一个包中可以省略包名。
execution(public * ArithmeticCalculator.*(..))ArithmeticCalculator接口的所有公有方法
execution(public double ArithmeticCalculator.*(..))ArithmeticCalculator接口中返回值类型为double的方法
execution(public double ArithmeticCalculator.*(double,..))ArithmeticCalculator接口中第一个参数为double类型的方法。第二个参数".."代表匹配任意数量、任意类型的参数
execution(public double ArithmeticCalculator.*(double, double))ArithmeticCalculator接口中参数类型为double,double类型的方法

5.4 、高级应用

        在AspectJ中,切入点表达式还可以与逻辑运算符结合起来使用

# 任意类中第一个参数为int类型的add方法或sub方法
execution (* org.star.ArithmeticCalculator.add(int,..)) || execution(* org.star.ArithmeticCalculator.sub(int,..))

# 匹配不是任意类中第一个参数为int类型的add方法
!execution (* org.star.ArithmeticCalculator.add(int,..)) 

六、切面的优先级

6.1、概述

        如果系统定义了多个切面,如何让某些切面先运行?可以通过设置切面的优先级来改变切面的执行顺序,注解为@Order(数字),其中数字越小,优先级越高。

6.2、案例代码

6.2.1、pom

<dependencies>
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.1</version>
    </dependency>
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.19</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.16</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>


    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.76</version>
    </dependency>

    <!-- 普通maven项目中使用Sl4j注解 -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.32</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.10</version>
    </dependency>

</dependencies>

6.2.2、ATM

package org.star.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class ATM {

    public int take(int money) {
        log.info("取钱方法正在执行...");
        if (money == 100) {
            throw new RuntimeException("自定义的异常");
        }
        return 10000;
    }

}

6.2.3、MySpringConfig

package org.star.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = {"org.star"})
@EnableAspectJAutoProxy(proxyTargetClass = true) // 启用CGLB动态代理
public class MySpringConfig {

}

6.2.4、LogAspect

package org.star.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @Aspect : 生成代理类
 */
@Slf4j
@Component
@Aspect
@Order(1)
public class LogAspect {

    /**
     * 相同的切入点进行抽取
     */
    @Pointcut(value = "execution(* org.star.component.ATM.take(..))")
    public void commonPoint() {

    }

    /**
     * 前置通知:方法执行之前执行
     */
    // @Before(value = "execution(* org.star.component.ATM.take(..))")
    @Before(value = "commonPoint()")
    public void beforeLog() {
        log.info("log前置通知执行...");
    }

    /**
     * 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行
     */
    // @AfterReturning(value = "execution(* org.star.component.ATM.take(..))",returning = "result")
    @AfterReturning(value = "commonPoint()",returning = "result")
    public void afterReturningLog(Object result) {
        log.info("log后置通知(正常结束通知)执行...,返回值:{}",result);
    }

}

6.2.5、ArgsAspect

package org.star.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @Aspect : 生成代理类
 */
@Slf4j
@Component
@Aspect
@Order(2)
public class ArgsAspect {

    /**
     * 相同的切入点进行抽取
     */
    @Pointcut(value = "execution(* org.star.component.ATM.take(..))")
    public void commonPoint() {

    }

    /**
     * 前置通知:方法执行之前执行
     */
    // @Before(value = "execution(* org.star.component.ATM.take(..))")
    @Before(value = "commonPoint()")
    public void beforeLog() {
        log.info("args前置通知执行...");
    }

    /**
     * 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行
     */
    // @AfterReturning(value = "execution(* org.star.component.ATM.take(..))",returning = "result")
    @AfterReturning(value = "commonPoint()",returning = "result")
    public void afterReturningLog(Object result) {
        log.info("args后置通知(正常结束通知)执行...,返回值:{}",result);
    }

}

6.2.6、AppTest

package org.star;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.star.component.ATM;
import org.star.config.MySpringConfig;

/**
 * Unit test for simple App.
 */
@Slf4j
public class AppTest {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
        ATM atm = context.getBean(ATM.class);
        log.info("atm:{}",atm);
        if (atm != null) {
            atm.take(1000);
        }
    }

}

// 控制台打印结果
15:39:18.209 [main] INFO org.star.AppTest - atm:org.star.component.ATM@223aa2f7
15:39:18.213 [main] INFO org.star.aspect.LogAspect - log前置通知执行...
15:39:18.213 [main] INFO org.star.aspect.ArgsAspect - args前置通知执行...
15:39:18.224 [main] INFO org.star.component.ATM - 取钱方法正在执行...
15:39:18.224 [main] INFO org.star.aspect.ArgsAspect - args后置通知(正常结束通知)执行...,返回值:10000
15:39:18.224 [main] INFO org.star.aspect.LogAspect - log后置通知(正常结束通知)执行...,返回值:10000

七、使用AOP注意事项

(1)连接点方法不能是private,否则AOP不能进行增强;

(2)连接点在其他方法内部被调用时,不会被增强;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/942719.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL中的free链表,flush链表,LRU链表

一、free链表 1、概述 free链表是一个双向链表数据结构&#xff0c;这个free链表里&#xff0c;每个节点就是一个空闲的缓存页的描述数据块的地址&#xff0c;也就是说&#xff0c;只要你一个缓存页是空闲的&#xff0c;那么他的描述数据块就会被放入这个free链表中。 刚开始数…

PHP自己的框架PDO数据表前缀、alias、model、table、join方法实现(完善篇九--结束)

一、实现功能&#xff0c;数据表前缀、alias、model、table、join方法实现 二、表前缀实现 1、config.php增加表前缀 DB_PEX>fa_,//数据库前缀 2、增加表前缀方法function.php function model($table){$modelnew ModelBase($table,config("DB_PEX"));return $m…

curl通过webdav操作alist

创建目录: url202320230828;curl -v -u "admin":"这里是密码" -X MKCOL "http://127.0.0.1:5244/dav/my189tianyi/${url2023}/" 上传文件: curl -v -u "admin":"这里是密码" -T /tmp/aa.json "http://127.0.0.1:52…

基于SSH的电影票预订系统

基于SSH的电影票预订系统 一、系统介绍二、功能展示1.其他系统实现五.获取源码 一、系统介绍 项目类型&#xff1a;Java web项目 项目名称&#xff1a;基于SSH的电影票预定系统 [dingpiao] 用户类型&#xff1a;有用户和管理员&#xff08;双角色&#xff09; 项目架构&…

C# 多线程交替按照指定顺序执行

1.关于AutoResetEvent和ManualResetEvent的区别解释如下&#xff1a; AutoResetEvent和ManualResetEvent是.NET中的两个线程同步类。它们之间的主要区别在于其释放信号的方式以及对等待线程的影响。 AutoResetEvent的作用是在等待的线程被信号唤醒后&#xff0c;将信号自动重…

Matlab图像处理-平移运算

几何运算 几何运算又称为几何变换&#xff0c;是将一幅图像中的坐标映射到另外一幅图像中的新坐标位置&#xff0c;它不改变图像的像素值&#xff0c;只是改变像素所在的几何位置&#xff0c;使原始图像按照需要产生位置、形状和大小的变化。 图像几何运算的一般定义为&#…

Atcoder Beginner Contest 317

A - Potions (atcoder.jp) AC代码: #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #define endl \n //#define int long long using namespace std; typedef long long ll; const int N110; int a[N]; int n,h,x; void…

【高阶数据结构】map和set的介绍和使用 {关联式容器;键值对;map和set;multimap和multiset;OJ练习}

map和set的介绍和使用 一、关联式容器 关联式容器和序列式容器是C STL中的两种不同类型的容器。 关联式容器是基于键值对的容器&#xff0c;其中每个元素都有一个唯一的键值&#xff0c;可以通过键值来访问元素。关联式容器包括set、multiset、map和multimap。 序列式容器是…

从零做软件开发项目系列之七——用户培训

开展用户培训工作是确保软件项目成功的重要一环。以下是一个详细的步骤指南&#xff0c;用于规划和执行用户培训。 1. 确定培训目标和用户。 首先需要识别用户角色。确定项目中不同用户角色和职能&#xff0c;例如管理员、操作员、管理人员等。不同角色可能需要不同的培训内容…

14.工作式指针和数字时钟

源码 源码 <!doctype html> <html><head><meta charset="utf-8"><title>Clock</title><link rel="stylesheet" href="style.css"></head><body><div class="container">…

epoll 基于多线程的边沿非阻塞处理

往期文章推荐&#xff1a; epoll() 多路复用 和 两种工作模式_呵呵哒(&#xffe3;▽&#xffe3;)"的博客-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/132523789?spm1001.2014.3001.5501 epoll_server.c #include <stdio.h> #include &l…

python venv 打包,更换路径后,仍然读取到旧路径 ,最好别换路径,采用docker封装起来

机械盘路径 /home/yeqiang/code/xxx 移动到 /opt/xxx 编辑/opt/xxx/venv/bin/activate VIRTUAL_ENV"/home/yeqiang/code/xxx/venv" 改为 VIRTUAL_ENV"/opt/xxx/venv" 下面还有这么多&#xff0c;参考&#xff1a; (venv) yeqiangyeqiang-MS-7B23:/…

Kubernetes入门 十一、网络之Service

目录 概述Service 原理Service 四种类型创建 Service代理 k8s 外部服务反向代理外部域名 概述 在 Kubernetes 中&#xff0c;Pod 是应用程序的载体&#xff0c;我们可以通过 Pod 的 IP 来访问应用程序&#xff0c;但是 Pod 的 IP 地址不是固定的&#xff0c;这就意味着不方便直…

MyBatisPlus实现多租户功能

前言&#xff1a;多租户是一种软件架构技术&#xff0c;在多用户的环境下&#xff0c;共有同一套系统&#xff0c;并且要注意数据之间的隔离性。 一、SaaS多租户简介 1.1、SaaS多租户 SaaS&#xff0c;是Software-as-a-Service的缩写名称&#xff0c;意思为软件即服务&#x…

【电源专题】读一读单节锂电池保护IC规格书

在文章【电源专题】单节锂离子电池的保护的基本原理 中我们了解了电池包的过充、过放、过流、短路等保护功能。那么这些功能都会在电池保护IC规格书中体现吗?体现在哪些地方?哪些参数是我们应关注的呢? 对于手册中的电压检测,如放电过流、充电过流和负载短路等检测电压都代…

开源软件的国际化和本地化

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

centos7删除乱码文件

centos7删除乱码文件1. 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.解释 当文件名为乱码的时候&#xff0c;无法通过键盘输入文件名&#xff0c;所以在终端下就不能直接利用rm&#xff0c;mv等命令管理文件了。 但是每个文件都有一个i节点号&#xff0c;可以通过…

《Flink学习笔记》——第三章 Flink的部署模式

不同的应用场景&#xff0c;有时候对集群资源的分配和占用有不同的需求。所以Flink为各种场景提供了不同的部署模式。 3.1 部署模式&#xff08;作业角度/通用分类&#xff09; 根据集群的生命周期、资源的分配方式、main方法到底在哪里执行——客户端还是Client还是JobManage…

AIGC - 生成模型

AIGC - 生成模型 0. 前言1. 生成模型2. 生成模型与判别模型的区别2.1 模型对比2.2 条件生成模型2.3 生成模型的发展2.4 生成模型与人工智能 3. 生成模型示例3.1 简单示例3.2 生成模型框架 4. 表示学习5. 生成模型与概率论6. 生成模型分类小结 0. 前言 生成式人工智能 (Generat…