spring5(五):AOP操作

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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

二分查找(二)

2.练习题 3&#xff09; 力扣https://leetcode.cn/problems/search-in-rotated-sorted-array-ii/这题需要分三种情况&#xff0c;第一种是区间有序&#xff0c;正常二分查找&#xff0c;第二种是区间 被旋转&#xff0c;左区间的值大于右区间&#xff0c;需要比较目标值和左区…

机器视觉行业公司2023今年最大的特点-老员工真香现象出现,事出反常必有妖

老员工真香现象出现&#xff0c;事出反常必有妖。-老人涨薪&#xff0c;新人不招或降薪&#xff0c;工作2-3年最值得跳槽&#xff0c;培训后好找工作。 你可能看到以上现象很反感&#xff0c;但是现在机器视觉行业环境就是这个样子的。 今年机器视觉行业最大的特点就是项目技术…

Atlassian Server用户新选择 | 迁移到数据中心版前,您需要做这些准备(2)

2024年2月&#xff0c;也就是一年不到&#xff0c;Atlassian将终止对Server产品及插件的所有支持。 此公告发布后&#xff0c;许多用户需要了解怎样的前进方向才是最适合企业的。为此&#xff0c;Atlassian不仅提供云版&#xff0c;还提供了本地部署的数据中心&#xff08;Data…

排序算法之希尔排序

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;数据结构与算法 &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;快给我点赞赞&#x1f497; 文章目录1. 希尔排序2. 算法思路3. 算法实现4. 算法性能分析&#x1f49e;总结&#x1f49e;1. 希尔排…

帆软FineReport学习篇(一)

帆软FineReport学习篇(一) 1 FineReport 11版下载 1.1 进入下载官网 fineReport 11版本下载链接 1.2 选择合适的版本,点击下载即可 2 解决问题的途径 2.1 社区搜索 2.1.1 进入社区官网 社区官网 2.1.2 输入搜索内容,点击搜索 1.1.3 选择你所需要的答案,建议优先点击官方…

易基因-单细胞甲基化测序单细胞转录组测序

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 易基因单细胞甲基化测序&单细胞转录组测序 高通量单细胞DNA甲基化测序简介 单细胞DNA甲基化组学研究很大程度上受制于建库技术&#xff0c;传统的文库构建方法或类似于基因组DNA的单…

【Python】1分钟就能制作精美的框架图?太棒啦

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、准备二、基本使用与例子1.初始化与导出2.节点类型3.集群块4.自定义线的颜色与属性总结前言 Diagrams 是一个基于Python绘制云系统架构的模块&#xff0c;它能…

使用Pytorch实现对比学习SimCLR 进行自监督预训练

SimCLR&#xff08;Simple Framework for Contrastive Learning of Representations&#xff09;是一种学习图像表示的自监督技术。 与传统的监督学习方法不同&#xff0c;SimCLR 不依赖标记数据来学习有用的表示。 它利用对比学习框架来学习一组有用的特征&#xff0c;这些特征…

linux 匿名管道 pipe

专栏内容&#xff1a;linux下并发编程个人主页&#xff1a;我的主页座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物&#xff0e;目录 前言 概述 原理介绍 接口说明 pipe 与 fifo的区别 代码演示 结尾 前言 本专栏主…

【K8S系列】深入解析无状态服务

目录 序言 1. 无服务介绍 1.1 优点 1.2 使用场景 1.3 资源类型 1.4 总结 2 使用介绍 2.1 Deployment 使用场景&#xff1a; 2.2 ReplicaSet 使用场景 2.3 pod Pod 资源定义示例 2.4 service 创建一个Deployment&#xff1a; 创建一个Service&#xff1a; 总结…

ChatGPT,让程序员从一片黑暗森林奔向另一片黑暗森林!

几年前看过一个电影&#xff0c;叫做《隐藏人物》&#xff0c;主要讲三位女性在NASA工作时反抗“种族歧视”和“性别歧视”的故事&#xff0c;其中有个情节让我印象极其深刻&#xff1a;NASA计算部门有一群女生&#xff0c;她们的工作是计算飞船轨道&#xff0c;纯手工计算。某…

【算法基础】(一)基础算法 --- 离散化

✨个人主页&#xff1a;bit me ✨当前专栏&#xff1a;算法基础 &#x1f525;专栏简介&#xff1a;该专栏主要更新一些基础算法题&#xff0c;有参加蓝桥杯等算法题竞赛或者正在刷题的铁汁们可以关注一下&#xff0c;互相监督打卡学习 &#x1f339; &#x1f339; &#x1f3…

软件测试,自学3个月出来就是高薪工作?你以为还是2019年以前?

朋友&#xff0c;作为一个曾经从机械转行到IT的行业的过来人&#xff0c;已在IT行业工作4年&#xff0c;分享一下我的经验&#xff0c;供你参考。 讲真&#xff0c;现在想通过培训班培训几个月就进入IT行业&#xff0c;越来越来难了&#xff1b;如果是在2018年以前&#xff0c;…

Spark 算子

目录 什么是Spark rdd算子 算子的分类 Transformation算子 Action算子 转换算子 Value类型 map mapPartitions mapPartitionsWithIndex glom groupBy filter sample distinct coalesce sortBy 双Value类型 intersection union subtract zip K-V类型 par…

【Java基础】-【SpringMVC】

目录什么是MVC&#xff1f;DAO层是做什么的&#xff1f;Spring MVC的执行流程Spring MVC常用注解Spring MVC的拦截器怎么去做请求拦截&#xff1f;其他cookie和session的区别cookie和session各自适合的场景session的工作原理get请求与post请求的区别get请求的参数能放到body里面…

JAVASE基础(一)

这里写目录标题一、javaSE基础1.jdk文档2.代码量统计工具3.文档注释4.反编译工具5.JDK、JRE、JVM&#xff08;java虚拟环境&#xff09;*6.变量命名规则7.变量的作用域8.数据类型9.进制10.反汇编器javap一、javaSE基础 1.jdk文档 Overview (Java Platform SE 8 ) (oracle.com…

stable-diffusion安装和简单测试

参考&#xff1a; https://github.com/CompVis/stable-diffusion 理解DALLE 2&#xff0c; Stable Diffusion和 Midjourney的工作原理 Latent Diffusion Models论文解读 【生成式AI】淺談圖像生成模型 Diffusion Model 原理 【生成式AI】Stable Diffusion、DALL-E、Imagen 背後…

面向对象编程(基础)3:对象的内存解析

目录 3.1 JVM内存结构划分 3.2 对象内存解析 举例&#xff1a; 内存解析图&#xff1a; 面试题&#xff1a;对象名中存储的是什么呢&#xff1f; 3.3 练习 3.1 JVM内存结构划分 HotSpot Java虚拟机的架构图如下。其中我们主要关心的是运行时数据区部分&#xff08;Runtime …

python字符编码

目录 ❤ 前言 文本编辑器存取文件的原理&#xff08;nodepad&#xff0c;pycharm&#xff0c;word&#xff09; python解释器执行py文件的原理 &#xff0c;例如python test.py 总结 ❤ 什么是字符编码? ASCII MBCS Unicode ❤ 字符编码的发展史 阶段一: 现代计算…

vue - vue中混入mixin的使用

vue中mixin混入的使用1&#xff0c;概念2&#xff0c;使用场景3&#xff0c;开始使用4&#xff0c;局部混入和全局混入5&#xff0c;总结1&#xff0c;概念 官方解释&#xff1a; 混入 (mixin) 提供了一种非常灵活的方式&#xff0c;来分发 Vue 组件中的可复用功能。一个混入对…