spring-aop的使用(注解 +配置类的方式)(多个增强类的优先级问题)

spring-aop

首先,准备一个接口以及其实现类

Calculator接口:src/main/java/com.atguigu.service/Calculator接口

package com.atguigu.service;

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);
}

Calculator接口的实现类:src/main/java/com.atguigu.service/impl/CalculatorImpl.java

package com.atguigu.service.impl;

import com.atguigu.service.Calculator;
import org.springframework.stereotype.Component;

@Component
public class CalculatorImpl implements Calculator {
    @Override
    public int add(int i, int j) {
        int ret = i + j;
        return ret;
    }

    @Override
    public int sub(int i, int j) {
        int ret = i - j;
        return ret;
    }

    @Override
    public int mul(int i, int j) {
        int ret = i * j;
        return ret;
    }

    @Override
    public int div(int i, int j) {
        int ret = i / j;
        return ret;
    }
}

然后,写一个配置类JavaConfig.java

src/main/java/com.atguigu.config/JavaConfig.java

package com.atguigu.config;

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

@Configuration
@ComponentScan("com.atguigu")
@EnableAspectJAutoProxy:开启aspectj的注解,等同于.xml配置文件的<aop:aspectj-autoproxy />
@EnableAspectJAutoProxy
public class JavaConfig {
}

再然后,写一个专门的切面表达式的类

src/main/java/com.atguigu.pointcut/MyPointCut.java

package com.atguigu.pointcut;

import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

这是专门存储切点表达式类
@Component
public class MyPointCut {
    @Pointcut("execution(* com.atguigu.service.impl.*.*(..))")
    public void pc(){}

    @Pointcut("execution(* com..impl.*.*(..))")
    public void myPc(){}
}

再再然后,专门的增强方法

src/main/java/com.atguigu.advice/LogAdvice.java

	package com.atguigu.advice;

	* 1、定义方法存储增强代码
			具体定义几个方法,根据插入的位置决定

	* 2、使用注解配置,指定插入目标方法的位置
			前置:@Before
			后置:@AfterReturning
			异常:@AfterThrowing
			最后:@After
			环绕:@Around

			try{
				前置
				目标方法执行
				后置
			}catch(){
				异常
			}finally{
				最后
			}

	* 3、配置切点表达式[选中插入的方法,切点]

	* 4、将这个类用@Component修饰,加入Ioc容器
		 配置切面:@Aspect

	* 5、开启Aspect支持
			在配置类中使用@EnableAspectJAutoProxy
			或者,在.xml文件中,使用<aop:aspectj-autoproxy />

	import org.aspectj.lang.annotation.After;
	import org.aspectj.lang.annotation.AfterThrowing;
	import org.aspectj.lang.annotation.Aspect;
	import org.aspectj.lang.annotation.Before;
	import org.springframework.core.annotation.Order;
	import org.springframework.stereotype.Component;

	@Component
	@Aspect
	@Order(20)
	public class LogAdvice {
		@Before("execution(* com.atguigu.service.impl.*.*(..))")
		public void start(){
			System.out.println("方法开始了");
		}

		@After("execution(* com.atguigu.service.impl.*.*(..))")
		public void after(){
			System.out.println("方法结束了");
		}

		@AfterThrowing("execution(* com.atguigu.service.impl.*.*(..))")
		public void error(){
			System.out.println("方法报错了");
		}
	}

src/main/java/com.atguigu.advice/MyAdvice.java

package com.atguigu.advice;

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

import java.lang.reflect.Modifier;

在增强方法中,获取目标方法的信息
	* 1、获取目标方法的信息(方法名、参数、访问修饰符、所属的类信息...)
	       在方法的形参列表里,加(JoinPoint joinPoint)  import org.aspectj.lang.JoinPoint包下的
	       JoinPoint就包含了目标方法的信息
	
	* 2、获取返回的结果,在 @AfterReturning 时
	        在方法的形参列表里,加一个 returning = "ret",如下:
	        @AfterReturning(value = "execution(* com..impl.*.*(..))", returning = "ret")
	        public void afterReturning(JoinPoint joinPoint, Object ret){}

	* 3、获取异常一新,在 @AfterThrowing 时
	         在方法的形参列表里,加一个throwing = "throwable",如下:
	         @AfterThrowing(value = "execution(* com..impl.*.*(..))", throwing = "throwable")
	         public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {}


切点表达式:固定语法:execution(1 2 3.4.5(6))
	1:访问修饰符,public / privae
    2:方法的返回参数类型,String / int / void
    如果不考虑访问修饰符 和 返回值类型的话,这两位整合在一起,写成*
    这两位是一体的,如果不考虑就都不考虑,不能出现 * String 这种情况

    3:包的位置
        具体包:com.atguigu.service.impl
        单层模糊:com.atguigu.service.*
        多层模糊:com..impl
        记住,..不能开头哦
        例如:找出所有impl包:com..impl,不能写成..impl,能写成 *..impl

    4:类的名称
        具体:myCLass
        模糊:*
        部分模糊:*Impl,意思是类名以Impl结尾的

    5:方法名
        语法和 上面4类的名称一样

    6:形参列表
        没有参数:()
        有具体的参数:(String)(String,int)
        模糊参数:(..),意思是有没有参数都行,有多少个参数都行
        部分模糊:(String..),意思是String后面有没有无所谓
                 (..int),意思是,最后一个参数是int
                 (String..int),意思是,第一个参数是String,最后一个是int,中间的无所谓


说一下切点表达式的提取和复用
	方式1、在当前类中提取
         首先,定义一个public void xxx(){} 这样的空方法
         然后,用注解@Pointcut("execution(* com..impl.*.*(..))")
         最后,增强注解中,引用上面定义的xxx这个空方法即可

	方式2、创建一个专门存储切点表达式的Xxx类
         只不过使用的时候,要这样:Xxx类的权限定符.方法名()

如果,有多个增强类,优先使用哪一个?
	这就要用到:@Order(10),指定一个优先级的值,值越小,优先级越高,越高的前置先执行,后置后执行
	给增强类加上@Order(10)这个注解,就能控制这些增强类的优先级了


@Component
@Aspect
@Order(10)
public class MyAdvice {

	切点表达式的提取和复用 - 方式1、在当前类中提取
    @Pointcut("execution(* com..impl.*.*(..))")
    public void pc(){}

    @Before("execution(* com..impl.*.*(..))")
    public void before(JoinPoint joinPoint) {
        获取方法属于的类的信息
        String simpleName = joinPoint.getTarget().getClass().getSimpleName();
        System.out.println("simpleName=" + simpleName);

        获取访问修饰符
        int modifiers = joinPoint.getSignature().getModifiers();
        String s = Modifier.toString(modifiers);

        获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println("name= " + name);

        获取目标方法的参数列表(就是获取目标方法的参数)
        Object[] args = joinPoint.getArgs();
    }

    @AfterReturning(value = "execution(* com..impl.*.*(..))", returning = "ret")
    public void afterReturning(JoinPoint joinPoint, Object ret) {
    }

    // @AfterThrowing(value = "execution(* com..impl.*.*(..))", throwing = "throwable")
    @AfterThrowing(value = "com.atguigu.pointcut.MyPointCut.myPc()", throwing = "throwable")
    public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
    }

    // @After("execution(* com..impl.*.*(..))") 未使用 “切点表达式的提取和复用” 之前
    @After("pc()") // 使用 “切点表达式的提取和复用” 之后
    public void after(JoinPoint joinPoint) {
    }
}

最后,写一个测试类

src/text/java/com.atguigu.test/SpringAopTest.java

package com.atguigu.test;

import com.atguigu.config.JavaConfig;
import com.atguigu.service.Calculator;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

@SpringJUnitConfig(value = JavaConfig.class)
public class SpringAopTest {
    @Autowired
    private Calculator calculator;

    @Test
    public void test() {
        int add = calculator.add(1, 1);
        System.out.println("add= " + add);
    }
}
  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring中,可以使用@Order注解来指定AOP的加载顺序。具体步骤如下: 1. 在需要进行AOP上添加@Aspect注解,表示该是一个切面 2. 在切面中定义多个切点和通知 3. 在通知方法上添加@Order注解,指定通知的优先级,数字越小优先级越高(默认优先级为0) 4. 在Spring配置文件中使用<aop:aspectj-autoproxy>标签启用自动代理功能 例如,我们有两个切面,分别是LoggingAspect和TransactionAspect,其中LoggingAspect负责记录日志,TransactionAspect负责管理事务。我们希望先记录日志再管理事务,那么可以按照以下方式指定顺序: ``` @Aspect public class LoggingAspect { @Pointcut("execution(* com.example.service.*.*(..))") public void servicePointcut() {} @Before("servicePointcut()") @Order(1) public void beforeService() { System.out.println("记录日志开始"); } @AfterReturning("servicePointcut()") @Order(1) public void afterService() { System.out.println("记录日志结束"); } } @Aspect public class TransactionAspect { @Pointcut("execution(* com.example.service.*.*(..))") public void servicePointcut() {} @Before("servicePointcut()") @Order(2) public void beforeService() { System.out.println("开启事务"); } @After("servicePointcut()") @Order(2) public void afterService() { System.out.println("提交事务"); } } ``` 在这个例子中,我们在LoggingAspect和TransactionAspect的通知方法上分别添加了@Order注解,指定了它们的执行顺序。在Spring配置文件中,我们启用了自动代理功能,让Spring自动将切面转换为代理对象,并在调用服务方法时自动触发切面的通知方法,从而实现AOP功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值