spring5(五):AOP操作

news2024/12/23 10:01:26

spring5(五):AOP操作

  • 前言
  • 一、代理模式
    • 1、场景模拟
    • 2、代理模式
      • 2.1 概念
      • 2.2 静态代理
      • 2.3 动态代理
  • 二、AOP概述
    • 1、什么是 AOP?
    • 2、相关术语
    • 3、作用
  • 三、AOP底层原理
    • 1、AOP 底层使用动态代理
    • 2、AOP(JDK 动态代理)
      • 2.1 编写 JDK 动态代理代码
  • 四、AOP 操作的准备工作
    • 1、AspectJ概述
    • 2、依赖的引入
  • 五、AOP操作(注解版)
    • 1、添加依赖
    • 2、准备被代理的目标资源
    • 3、在Spring的配置文件中配置
    • 4、创建切面类并配置
    • 5、测试
  • 六、重点
    • 1、 切入点表达式
    • 2、重用切入点表达式
    • 3、获取通知的相关信息
    • 4、环绕通知
    • 5、切面的优先级
    • 6、完全使用注解开发
  • 七、AOP 操作(AspectJ 配置文件)
    • 1、创建两个类,增强类和被增强类,创建方法
    • 2、在 spring 配置文件中创建两个类对象
    • 3、在 spring 配置文件中配置切入点



前言

本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识,有兴趣的小伙伴可以关注博主!也许一个人独行,可以走的很快,但是一群人结伴而行,才能走的更远!让我们在成长的道路上互相学习,欢迎关注!

一、代理模式

1、场景模拟

(1)声明计算器接口Calculator,包含加减乘除的抽象方法

public interface Calculator {
	int add(int i, int j);
	int sub(int i, int j);
	int mul(int i, int j);
	int div(int i, int j);
}

(2)创建实现类
在这里插入图片描述

public class CalculatorPureImpl implements Calculator {
	@Override
	public int add(int i, int j) {
		int result = i + j;
		System.out.println("方法内部 result = " + result);
		return result;
	}
	@Override
	public int sub(int i, int j) {
		int result = i - j;
		System.out.println("方法内部 result = " + result);
		return result;
	}
	@Override
	public int mul(int i, int j) {
		int result = i * j;
		System.out.println("方法内部 result = " + result);
		return result;
	}
	@Override
	public int div(int i, int j) {
		int result = i / j;
		System.out.println("方法内部 result = " + result);
		return result;
	}
}

(3)创建带日志功能的实现类
在这里插入图片描述

public class CalculatorLogImpl implements Calculator {
	@Override
	public int add(int i, int j) {
		System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
		int result = i + j;
		System.out.println("方法内部 result = " + result);
		System.out.println("[日志] add 方法结束了,结果是:" + result);
		return result;
	}
	@Override
	public int sub(int i, int j) {
		System.out.println("[日志] sub 方法开始了,参数是:" + i + "," + j);
		int result = i - j;
		System.out.println("方法内部 result = " + result);
		System.out.println("[日志] sub 方法结束了,结果是:" + result);
		return result;
	}
	@Override
	public int mul(int i, int j) {
		System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);
		int result = i * j;
		System.out.println("方法内部 result = " + result);
		System.out.println("[日志] mul 方法结束了,结果是:" + result);
		return result;
	}
	@Override
	public int div(int i, int j) {
		System.out.println("[日志] div 方法开始了,参数是:" + i + "," + j);
		int result = i / j;
		System.out.println("方法内部 result = " + result);
		System.out.println("[日志] div 方法结束了,结果是:" + result);
		return result;
	}
}

(4)提出问题

现有代码缺陷:
针对带日志功能的实现类,我们发现有如下缺陷: 对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力
附加功能分散在各个业务功能方法中,不利于统一维护
解决思路:
解决这两个问题,核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。
困难:
解决问题的困难:要抽取的代码在方法内部,靠以前把子类中的重复代码抽取到父类的方式没法解决。 所以需要引入新的技术。

2、代理模式

2.1 概念

①介绍

二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。

⭕ 使用代理前:
在这里插入图片描述

⭕ 使用代理后:
在这里插入图片描述

②生活中的代理

● 广告商找大明星拍广告需要经过经纪人
● 合作伙伴找大老板谈合作要约见面时间需要经过秘书
● 房产中介是买卖双方的代理

③相关术语

● 代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。
● 目标:被代理“套用”了非核心逻辑代码的类、对象、方法。

2.2 静态代理

public class CalculatorStaticProxy implements Calculator {
	// 将被代理的目标对象声明为成员变量
	private Calculator target;
	public CalculatorStaticProxy(Calculator target) {
	this.target = target;
	}
	@Override
	public int add(int i, int j) {
		// 附加功能由代理类中的代理方法来实现
		System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
		// 通过目标对象来实现核心业务逻辑
		int addResult = target.add(i, j);
		System.out.println("[日志] add 方法结束了,结果是:" + addResult);
		return addResult;
	}
}

静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来
说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代
码,日志功能还是分散的,没有统一管理。
提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理
类来实现。这就需要使用动态代理技术了

2.3 动态代理

在这里插入图片描述
生产代理对象的工厂类:

public class ProxyFactory {
    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }
    public Object getProxy(){
        /**
         * newProxyInstance():创建一个代理实例
         * 其中有三个参数:
         * 1、classLoader:加载动态生成的代理类的类加载器
         * 2、interfaces:目标对象实现的所有接口的class对象所组成的数组
         * 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接
         口中的抽象方法
         */
        ClassLoader classLoader = target.getClass().getClassLoader();
        Class<?>[] interfaces = target.getClass().getInterfaces();
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /**
                 * proxy:代理对象
                 * method:代理对象需要实现的方法,即其中需要重写的方法
                 * args:method所对应方法的参数
                 */
                Object result = null;
                try {
                    System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
                    result = method.invoke(target, args);
                    System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());
                } finally {
                    System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
                }
                return result;
            }
        };
        return Proxy.newProxyInstance(classLoader, interfaces,invocationHandler);
    }
}

测试

@Test
public void testDynamicProxy(){
	ProxyFactory factory = new ProxyFactory(new CalculatorLogImpl());
	Calculator proxy = (Calculator) factory.getProxy();
	proxy.div(1,0);
	//proxy.div(1,1);
}

二、AOP概述

1、什么是 AOP?

AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。

⭕ 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能

2、相关术语

⭕ 横切关注点

从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。
这个概念不是语法层面天然存在的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点,比如上例中CalculatorImpl类中的日志代码是非核心代码,我们把这些非核心代码抽取出来,作为一些附加功能,即横切关注点。
附加功能相对于目标类来说是横切关注点,相对于目标类来说是通知。

在这里插入图片描述
⭕ 通知

每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。

  1. 前置通知:在被代理的目标方法前执行
  2. 返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
  3. 异常通知:在被代理的目标方法异常结束后执行(死于非命)
  4. 后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
  5. 环绕通知:使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

在这里插入图片描述

⭕ 切面

封装通知方法的类。

在这里插入图片描述
⭕目标

被代理的目标对象。

⭕代理

向目标对象应用通知之后创建的代理对象。

⭕连接点

这也是一个纯逻辑概念,不是语法定义的。
把方法排成一排,每一个横切位置看成x轴方向,把方法从上到下执行的顺序看成y轴,x轴和y轴的交叉点就是连接点。

在这里插入图片描述
⭕切入点

定位连接点的方式。
每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物(从逻辑上来说)。
如果把连接点看作数据库中的记录,那么切入点就是查询记录的 SQL 语句。
SpringAOP 技术可以通过切入点定位到特定的连接点。
切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

3、作用

  1. 简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。
  2. 代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。

三、AOP底层原理

1、AOP 底层使用动态代理

有两种情况动态代理:

⭕ 第一种 有接口情况,使用 JDK 动态代理

创建接口实现类代理对象,增强类的方法

在这里插入图片描述

⭕ 第二种 没有接口情况,使用 cglib 动态代理
创建子类的代理对象,增强类的方法

在这里插入图片描述

2、AOP(JDK 动态代理)

① 使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象

在这里插入图片描述

② 调用 newProxyInstance() 方法
方法有三个参数:

  1. 第一参数,类加载器
  2. 第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
  3. 第三参数,实现这个接口InvocationHandler,创建代理对象,写增强的部分

在这里插入图片描述

2.1 编写 JDK 动态代理代码

① 创建接口,定义方法


public interface UserDao {
    public int add(int a,int b);
    public String update(String id);
}

② 创建接口实现类,实现方法

public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println("add方法执行了.....");
        return a+b;
    }

    @Override
    public String update(String id) {
        System.out.println("update方法执行了.....");
        return id;
    }
}

③ 使用 Proxy 类创建接口代理对象

public class JDKProxy {

    public static void main(String[] args) {
        //创建接口实现类代理对象
        Class[] interfaces = {UserDao.class};
//        Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                return null;
//            }
//        });
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int result = dao.add(1, 2);
        System.out.println("result:"+result);
    }
}

//创建代理对象代码
class UserDaoProxy implements InvocationHandler {

    //1 把创建的是谁的代理对象,把谁传递过来
    //有参数构造传递
    private Object obj;
    public UserDaoProxy(Object obj) {
        this.obj = obj;
    }

    //增强的逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前
        System.out.println("方法之前执行...."+method.getName()+" :传递的参数..."+ Arrays.toString(args));

        //被增强的方法执行
        Object res = method.invoke(obj, args);

        //方法之后
        System.out.println("方法之后执行...."+obj);
        return res;
    }
}

四、AOP 操作的准备工作

1、AspectJ概述

Spring 框架一般都是基于 AspectJ 实现 AOP 操作
AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJSpirng 框架一起使用,进行 AOP操作

⭕ 基于 AspectJ 实现 AOP 操作

  1. 基于 xml 配置文件实现
  2. 基于注解方式实现

⭕ 在项目工程里面引入 AOP 相关依赖

2、依赖的引入

五、AOP操作(注解版)

1、添加依赖

IOC所需依赖基础上再加入下面依赖即可:

<!-- spring-aspects会帮我们传递过来aspectjweaver -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>5.3.1</version>
</dependency>

2、准备被代理的目标资源

接口:

public interface Calculator {
	int add(int i, int j);
	int sub(int i, int j);
	int mul(int i, int j);
	int div(int i, int j);
}

实现类:

@Component
public class CalculatorPureImpl implements Calculator {
	@Override
	public int add(int i, int j) {
		int result = i + j;
		System.out.println("方法内部 result = " + result);
		return result;
	}
	@Override
	public int sub(int i, int j) {
		int result = i - j;
		System.out.println("方法内部 result = " + result);
		return result;
	}
	@Override
		public int mul(int i, int j) {
		int result = i * j;
		System.out.println("方法内部 result = " + result);
		return result;
	}
	@Override
	public int div(int i, int j) {
		int result = i / j;
		System.out.println("方法内部 result = " + result);
		return result;
	}
}

3、在Spring的配置文件中配置

<!--
基于注解的AOP的实现:
1、将目标对象和切面交给IOC容器管理(注解+扫描)
2、开启AspectJ的自动代理,为目标对象自动生成代理
3、将切面类通过注解@Aspect标识
-->
<context:component-scan base-package="com.atguigu.aop.annotation">
</context:component-scan>
<aop:aspectj-autoproxy />

4、创建切面类并配置

package com.reds.AOPTest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * Date:2022/7/4
 * Author:ybc
 * Description:
 * 1、在切面中,需要通过指定的注解将方法标识为通知方法
 * @Before:前置通知,在目标对象方法执行之前执行
 * @After:后置通知,在目标对象方法的finally字句中执行
 * @AfterReturning:返回通知,在目标对象方法返回值之后执行
 * @AfterThrowing:异常通知,在目标对象方法的catch字句中执行
 *
 *
 * 2、切入点表达式:设置在标识通知的注解的value属性中
 * execution(public int com.atguigu.spring.aop.annotation.CalculatorImpl.add(int, int)
 * execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..)
 * 第一个*表示任意的访问修饰符和返回值类型
 * 第二个*表示类中任意的方法
 * ..表示任意的参数列表
 * 类的地方也可以使用*,表示包下所有的类
 * 3、重用切入点表达式
 * //@Pointcut声明一个公共的切入点表达式
 * @Pointcut("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
 * public void pointCut(){}
 * 使用方式:@Before("pointCut()")
 *
 * 4、获取连接点的信息
 * 在通知方法的参数位置,设置JoinPoint类型的参数,就可以获取连接点所对应方法的信息
 * //获取连接点所对应方法的签名信息
 * Signature signature = joinPoint.getSignature();
 * //获取连接点所对应方法的参数
 * Object[] args = joinPoint.getArgs();
 *
 * 5、切面的优先级
 * 可以通过@Order注解的value属性设置优先级,默认值Integer的最大值
 * @Order注解的value属性值越小,优先级越高
 *
 */
@Component
@Aspect //将当前组件标识为切面
public class LoggerAspect {

    @Pointcut("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
    public void pointCut(){}

    //@Before("execution(public int com.atguigu.spring.aop.annotation.CalculatorImpl.add(int, int))")
    //@Before("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
    @Before("pointCut()")
    public void beforeAdviceMethod(JoinPoint joinPoint) {
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",参数:"+ Arrays.toString(args));
    }

    @After("pointCut()")
    public void afterAdviceMethod(JoinPoint joinPoint){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",执行完毕");
    }

    /**
     * 在返回通知中若要获取目标对象方法的返回值
     * 只需要通过@AfterReturning注解的returning属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法的返回值的参数
     */
    @AfterReturning(value = "pointCut()", returning = "result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint, Object result){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",结果:"+result);
    }

    /**
     * 在异常通知中若要获取目标对象方法的异常
     * 只需要通过AfterThrowing注解的throwing属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法出现的异常的参数
     */
    @AfterThrowing(value = "pointCut()", throwing = "ex")
    public void afterThrowingAdviceMethod(JoinPoint joinPoint, Throwable ex){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",异常:"+ex);
    }

    @Around("pointCut()")
    //环绕通知的方法的返回值一定要和目标对象方法的返回值一致
    public Object aroundAdviceMethod(ProceedingJoinPoint joinPoint){
        Object result = null;
        try {
            System.out.println("环绕通知-->前置通知");
            //表示目标对象方法的执行
            result = joinPoint.proceed();
            System.out.println("环绕通知-->返回通知");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->异常通知");
        } finally {
            System.out.println("环绕通知-->后置通知");
        }
        return result;
    }

}

5、测试

public class AOPByAnnotationTest {

@Test
public void testAOPByAnnotation(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("aop-annotation.xml");
    //Calculator :代理对象和目标对象共同实现的接口
    Calculator calculator = ioc.getBean(Calculator.class);
    calculator.div(10, 1);
}

如果直接获取目标类,此时会报无法找到实例的错误。
原因是:
我们为目标对象创建了代理对象,因此我们每次想访问目标对象中的方法时都不能直接通过目标对象去访问,必须通过代理对象来间接访问目标对象,进而去访问目标对象中的方法,所以我们下一步应该是去获取代理对象,可是我们并不知道代理对象是什么,但是我们知道代理对象跟我们的目标对象实现的是相同的接口,我们之前通过IOC获取某个bean的时候,我们不需要通过某个具体的类型来获取,我们可以用它所继承的父类或者所实现的接口,都可以获取到bean。

六、重点

1、 切入点表达式

作用

知道对哪个类里面的哪个方法进行增强

在这里插入图片描述

语法结构

execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )

为了方便,权限修饰符一般使用* 代替 ,返回类型一般省略不写

⭕ 用*号代替“权限修饰符”和“返回值”部分表示“权限修饰符”和“返回值”不限在包名的部分,一个“*”号只能代表包的层次结构中的一层,表示这一层是任意的。
例如:*.Hello匹配com.Hello,不匹配com.atguigu.Hello

⭕ 在包名的部分,使用“*..”表示包名任意、包的层次深度任意

⭕ 在类名的部分,类名部分整体用*号代替,表示类名任意

⭕ 在类名的部分,可以使用*号代替类名的一部分
例如:*Service匹配所有名称以Service结尾的类或接口

⭕ 在方法名部分,可以使用*号表示方法名任意

⭕ 在方法名部分,可以使用*号代替方法名的一部分
例如:*Operation匹配所有方法名以Operation结尾的方法

⭕ 在方法参数列表部分,使用(..)表示参数列表任意

⭕ 在方法参数列表部分,使用(int,..)表示参数列表以一个int类型的参数开头

⭕ 在方法参数列表部分,基本数据类型和对应的包装类型是不一样的 切入点表达式中使用 int 和实际方法中 Integer 是不匹配的

⭕ 在方法返回值部分,如果想要明确指定一个返回值类型,那么必须同时写明权限修饰符
例如:execution(public int ..Service.*(.., int)) 正确
例如:execution(* int ..Service.*(.., int)) 错误


举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
execution(*com.atguigu.dao.BookDao.add(..))
举例 2:对 com.atguigu.dao.BookDao类里面的所有的方法进行增强 execution(* com.atguigu.dao.BookDao.* (..))
举例 3:对com.atguigu.dao 包里面所有类,类里面所有方法进行增强 execution(* com.atguigu.dao.*.* (..))

在这里插入图片描述

2、重用切入点表达式

①声明

@Pointcut("execution(* com.atguigu.aop.annotation.*.*(..))")
public void pointCut(){}

②在同一个切面中使用

@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){
	String methodName = joinPoint.getSignature().getName();
	String args = Arrays.toString(joinPoint.getArgs());
	System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

③在不同切面中使用

@Before("com.atguigu.aop.CommonPointCut.pointCut()")
public void beforeMethod(JoinPoint joinPoint){
	String methodName = joinPoint.getSignature().getName();
	String args = Arrays.toString(joinPoint.getArgs());
	System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

3、获取通知的相关信息

①获取连接点信息

获取连接点信息可以在通知方法的参数位置设置JoinPoint类型的形参

@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint){
	//获取连接点的签名信息
	String methodName = joinPoint.getSignature().getName();
	//获取目标方法到的实参信息
	String args = Arrays.toString(joinPoint.getArgs());
	System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

②获取目标方法的返回值

@AfterReturning中的属性returning,用来将通知方法的某个形参,接收目标方法的返回值

@AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
	String methodName = joinPoint.getSignature().getName();
	System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
}

③获取目标方法的异常

@AfterThrowing中的属性throwing,用来将通知方法的某个形参,接收目标方法的异常

@AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
	String methodName = joinPoint.getSignature().getName();
	System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}

4、环绕通知

@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
	String methodName = joinPoint.getSignature().getName();
	String args = Arrays.toString(joinPoint.getArgs());
	Object result = null;
	try {
		System.out.println("环绕通知-->目标对象方法执行之前");
		//目标方法的执行,目标方法的返回值一定要返回给外界调用者
		result = joinPoint.proceed();
		System.out.println("环绕通知-->目标对象方法返回值之后");
	} catch (Throwable throwable) {
		throwable.printStackTrace();
		System.out.println("环绕通知-->目标对象方法出现异常时");
	} finally {
		System.out.println("环绕通知-->目标对象方法执行完毕");
	}
	return result;
}

5、切面的优先级

⭕ 相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。

  1. 优先级高的切面:外面
  2. 优先级低的切面:里面

⭕ 使用@Order注解可以控制切面的优先级:

  1. @Order(较小的数):优先级高
  2. @Order(较大的数):优先级低

在这里插入图片描述

6、完全使用注解开发

创建配置类,不需要创建 xml 配置文件

@Configuration
@ComponentScan(basePackages = {"aopanno"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {

}

七、AOP 操作(AspectJ 配置文件)

1、创建两个类,增强类和被增强类,创建方法

Book类

public class Book {
    public void buy() {
        System.out.println("buy.............");
    }
}

BookProxy类

public class BookProxy {
    public void before() {
        System.out.println("before.........");
    }
}

2、在 spring 配置文件中创建两个类对象

 <!--创建对象-->
    <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>

3、在 spring 配置文件中配置切入点

<!--配置aop增强-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/>
        <!--配置切面-->
        <aop:aspect ref="bookProxy">
            <!--增强作用在具体的方法上-->
            <aop:before method="before" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>

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

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

相关文章

VR全景展示,VR全景平台,助理全景展示新模式

引言&#xff1a; VR全景展示是一种新型的展示方式&#xff0c;它利用虚拟现实技术和全景拍摄技术&#xff0c;使参观者可以身临其境地进入虚拟展览空间。这种展示方式不仅能够提供更加沉浸式的参观体验&#xff0c;还可以解决传统展览所面临的时间和地域限制等问题。 VR全景展…

【Java实战篇】Day7.在线教育网课平台

文章目录一、需求&#xff1a;课程审核1、需求分析2、建表与数据模型3、接口定义4、Mapper层开发5、Service层开发6、完善controller层二、需求&#xff1a;课程发布1、需求分析2、建表与数据模型3、技术方案4、接口定义5、消息处理SDK6、Mapper层开发7、Service层开发8、页面静…

unity,射手游戏

文章目录介绍一&#xff0c;制作玩家具体函数脚本PlayerCharacter三、 制作玩家控制脚本 PlayerController&#xff0c;调用上面的函数方法四、 制作子弹脚本 shell五、 给玩家挂载脚本六、 制作坦克脚本七、 给坦克添加组件八、 开始游戏&#xff0c;播放动画九、 下载介绍 3…

seata学习笔记

Seata 官网&#xff1a; https://seata.io/zh-cn/index.html 是什么&#xff1f; Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式…

MySQL中的float类型慎用!慎用!慎用!

在前端输入一串数字后有时候展示值与输入的内容一致&#xff0c;有时候却不一致。经分析&#xff0c;原来是MySQL数据库中字该字段的类型是float&#xff0c;该字段的值超过6位有效数字后就会进行四舍五入截取&#xff0c;举例来说&#xff1a;假设float类型字段的输入值是1234…

十八、MySQL 变量、分支结构IF、CASE...WHEN详解

文章目录一、变量1.1 系统变量1.1.1 系统变量分类1.1.2 查看系统变量1.2 用户变量1.2.1 用户变量分类1.2.2 会话用户变量1.2.3 局部变量1.2.4 对比会话用户变量与局部变量二、定义条件与处理程序2.1 案例分析2.2 定义条件2.3 定义处理程序2.4 案例解决三、流程控制3.1 分支结构…

[C++]类与对象下篇

目录 类与对象下篇&#xff1a;&#xff1a; 1.再谈构造函数 2.static成员 3.友元 4.内部类 5.匿名对象 6.拷贝对象时的编译器优化 7.再次理解封装 8.求12...n(不能使用乘除法、循环、条件判断) 9.计算日期到天数的转换 10.日期差值 11.打印日期 12.累加天数 类与对象下篇&…

数据结构与算法七 堆

一 堆 1.1 堆定义 堆是计算机科学中一类特殊的数据结构的统称&#xff0c;堆通常可以被看做是一棵完全二叉树的数组对象。 堆的特性&#xff1a; 它是完全二叉树&#xff0c;除了树的最后一层结点不需要是满的&#xff0c;其它的每一层从左到右都是满的&#xff0c;如果最后…

4月最新编程排行出炉,第一名ChatGPT都在用~

作为一名合格的&#xff08;准&#xff09;程序员&#xff0c;必做的一件事是关注编程语言的热度&#xff0c;编程榜代表了编程语言的市场占比变化&#xff0c;它的变化更预示着未来的科技风向和机会&#xff01; 快跟着一起看看本月排行有何看点&#xff1a; 4月Tiobe排行榜前…

【CSS】使用 固定定位 实现顶部导航栏 ( 核心要点 | 固定定位元素居中设置 | 代码示例 )

文章目录一、核心要点分析1、顶部导航栏要点2、固定定位垂直居中设置二、代码示例一、核心要点分析 实现下图所示功能 : 上方有一个固定导航栏 , 水平居中设置 ;左右两侧各一个广告栏 , 垂直居中设置 ; 1、顶部导航栏要点 顶部导航栏要点 : 使用固定定位 , 上边偏移设置为 0 …

Linux Ubuntu虚拟机下载安装以及初始配置--VMware、Ubuntu、Xshell、Xftp

一、下载准备 Ubuntu系统下载链接&#xff08;系统本身&#xff09;&#xff1a;官网链接 VMware虚拟机下载链接&#xff08;搭载Ubuntu系统&#xff09;&#xff1a;网盘链接密码XMKD Xshell下载链接&#xff08;虚拟机远程连接&#xff09;&#xff1a;官网链接 Xftp下载…

MySQL索引数据结构入门

之前松哥写过一个 MySQL 系列&#xff0c;但是当时是基于 MySQL5.7 的&#xff0c;最近有空在看 MySQL8 的文档&#xff0c;发现和 MySQL5.7 相比还是有不少变化&#xff0c;同时 MySQL 又是小伙伴们在面试时一个非常重要的知识点&#xff0c;因此松哥打算最近再抽空和小伙伴们…

PyQt5学习笔记一、安装PyQt5和在PyCharm中配置工具

一、安装PyQt5 1. 可以在cmd窗口安装PyQt5和工具 可以在cmd窗口使用命令 pip install PyQt5 安装PyQt5&#xff0c;若指定版本使用命令 pip install PyQt5version&#xff0c;此时同时安装了PyQt5和sip。参考链接 在cmd命令窗口安装Python模块_Mr. 李大白的博客-CSDN博客htt…

potPlay——记忆播放位置、各种快捷键

potPlay——记忆播放位置、各种快捷键potPlay——各种快捷键简洁版完整版快捷键列表potPlay——记忆播放位置potPlay——各种快捷键 简洁版 Q 复位 亮度&#xff0c;对比度&#xff0c;色度复位键 W/E 调暗/调亮 R/T 对比度 Y/U 饱和度 I/O 色彩度 D 上一帧 F 下一帧 M 静音 …

Docker开启并配置远程安全访问

前言 在工作学习中&#xff0c;为了提高项目部署效率&#xff0c;一般会在Idea中直接使用Docker插件连接服务器Docker容器&#xff0c;然后将项目打包与DockerFile一起build成Docker镜像部署运行。但是不可能服务器总是跟着主机的&#xff0c;因此呢时常会面临的一个问题就是从…

【微信小程序】-- uni-app 项目--- 购物车 -- 配置 tabBar 效果(五十一)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

事务的ACID特性

1. 絮絮叨叨 重温Apache ORC时&#xff0c;发现ORC支持ACID想起自己之前一度不知道ACID是哪些单词的缩写&#xff0c;更别提面试中常提到的事物隔离级别等知识了因此&#xff0c;特地学习一下数据库中事务的ACID 2. ACID 2.1 What’s transaction&#xff1f; 考虑一个真实…

42.原型对象 prototype

目录 1 面向对象与面向过程 2 原型对象 prototype 3 在内置对象中添加方法 4 constructor 属性 5 实例对象原型 __proto__ 6 原型继承 7 原型链与instanceof 7.1 原型链 7.2 instanceof 8 案例-模态框 1 面向对象与面向过程 编程思想有 面向过程 与 面向…

几何-九种二次曲面类型

&#xff08;一&#xff09;椭圆锥面 &#xff08;1&#xff09;把z平方看成一个一直变大的常数&#xff0c;那么可以看出延z方向&#xff0c;是一个一直变大的椭圆。 &#xff08;2&#xff09;把一个x或y赋予0&#xff0c;显然是一个两条关于原点对称的直线。 由上即可判断…

不小心删除了文件能恢复吗 误删除文件怎么找回

电脑是我们平时工作或者生活、学习中使用频率非常高的电子设备&#xff0c;已经成为了我们日常生活中不可或缺的一部分。删除文件是电脑使用过程中常见的一种操作&#xff0c;因为电脑的储存空间是有限的&#xff0c;我们需要对电脑数据进行清理&#xff0c;避免电脑储存空间占…