Spring--AOP--细节详解


代码沿用 Spring–AOP简介

一. bean的实际类型

AOP底层就是动态代理,容器中保存的组件是他的代理对象:$Proxy23,不是本类类型。

代理对象和目标对象是用同一个接口,所以获取时一定要用接口类型

 @Test
    public void test() {
        //从IOC容器中拿到目标对象,如果想要用类型,一定要用接口类型,不要用本类MyCalculator.class
        Calculator bean = ioc.getBean(Calculator.class);
        System.out.println(bean);
        System.out.println(bean.getClass());
    }

在这里插入图片描述

若没有接口就是本类类型,cglib帮我们创建了代理对象

@Service
public class MyCalculator //implements Calculator{
    public int add(int i, int j) {
        int result=i+j;
        return result;
    }
    public int sub(int i, int j) {
        int result=i-j;
        return result;
    }
    public int mul(int i, int j) {
        int result=i*j;
        return result;
    }
    public int div(int i, int j) {
        int result=i/j;
        return result;
    }
}

二. 切入点表达式

固定格式:execution(访问权限服 返回值类型 方法全类名(参数表))

通配符
*

  1. 匹配一个或多个字符: execution(public int com.jess.aop.MyCal*.*(int,int))
  2. 匹配任意一个参数:第二个参数任意execution(public int com.jess.aop.MyCalculator.*(int,*))
  3. 只能匹配一层路径
  4. 权限位置不能使用*,不写就行:public可不写

..

  1. 匹配任意多个参数,任意参数类型:execution(public int com.jess.aop.MyCalculator.*(..))
  2. 匹配任意多层路径:execution(public int com.jess..MyCalculator.*(int,int))
  • 最模糊:不推荐execution(* *.*(..))
  • 最精确:execution(public int com.jess.aop.MyCalculator.add(int,int))"

表达式中还可以使用 && 、||、!
例如:execution(* *.*(..)) && execution(public int com.jess.aop.MyCalculator.add(int,int))"

抽取可重用的切入点表达式:

  1. 随便声明一个没有返回的空方法
  2. 给方法标注注解
  3. 在其他方法上使用
@Pointcut("execution(public int com.jess.aop.MyCalculator.*(int,int))")
public void MyPoint(){}

@Before("MyPoint()")

三. 通知方法的执行顺序

在上一篇博客中代码执行结果为
在这里插入图片描述
先执行了finally块中的 @After,在执行try中的@AfterReturning或者catch中@AfterThrowing

正常执行顺序:

  1. @Before:前置通知
  2. @After:后置通知
  3. @AfterReturning

异常执行通知:

  1. @Before:前置通知
  2. @After:后置通知
  3. @AfterThrowing

四. JoinPoint获取目标方法的信息

在通知方法运行时,拿到目标方法的详细信息

在通知方法的参数列表上写一个参数JoinPoint
JoinPoint joinPoint:封装了当前目标方法的详细信息
注意:JoinPoint 选用 org.aspectj.lang 包下的

@Before("execution(public int com.jess.aop.MyCalculator.*(int,int))")
public static void logStrat(JoinPoint joinPoint){
    Signature signature = joinPoint.getSignature();//获取方法签名
    String name = signature.getName();//获取方法名
    Object[] args = joinPoint.getArgs(); //获取方法参数
    System.out.println(name+"方法开始执行,使用的参数列表:"+ Arrays.asList(args));
}

在这里插入图片描述

五. throwing、returning

在通知方法的参数列表上写一个参数 Object result
并在 @AfterReturning后添加一个属性returning,值为你设定的Object参数名

throwing同

@AfterReturning(value = "execution(public int com.jess.aop.MyCalculator.*(int,int))",returning = "result")
public static void logReturn(JoinPoint joinPoint,Object result){
    System.out.println("方法执行完成,结果为:"+result);
}

@AfterThrowing(value = "execution(public int com.jess.aop.MyCalculator.*(int,int))",throwing = "exception")
public static void logException(JoinPoint joinPoint,Exception exception){
    System.out.println("方法出现异常:"+exception);
}

异常类型指定后,通知方法只接受此类异常,所以一般把异常的范围写大

@AfterThrowing(value = "execution(public int com.jess.aop.MyCalculator.*(int,int))",throwing = "exception")
public static void logException(JoinPoint joinPoint,NullPointerException exception){
    System.out.println("方法出现异常:"+exception);
}

六. Spring对通知方法的约束

  • spring对通知方法要求不严格(访问权限符,返回值)
  • 唯一要求参数列表不能乱写:通知方法是spring利用反射调用的,每次方法调用得绑定这个方法的参数列表的值,每一个参数spring都得知道是什么。

七. 环绕通知

Spring中最强大的通知方法
@Around:动态代理

环绕通知就是其余4种通知的四合一

@Around("MyPoint()")
public Object MyAround(ProceedingJoinPoint pjp){

	Object[] args = pjp.getArgs()
	//利用反射调用目标方法,就是method.invoke()
	Object proceed = pjp.proceed(args);
	//反射的返回值一定要返回
	return proceed;
}

关闭其他通知,只使用环绕通知:
在这里插入图片描述
在这里插入图片描述
其他通知和环绕通知一起使用:
在这里插入图片描述

在这里插入图片描述
异常已经被环绕通知获取了,普通通知获取不到了
若在环绕通知获取异常后(catch中)将异常抛出,普通通知就能捕获异常了

throw new RuntimeException(e);

环绕通知优先执行
在这里插入图片描述
在这里插入图片描述
前置通知顺序不一定,后续的通知顺序是一定的

八. 多切面运行顺序

@Aspect
@Component
public class ValidateAspect {
//内容与LogUtils相同
}

在这里插入图片描述
若要人为的改变顺序可以添加注解@Order(1),数值越小,优先级越高

在这里插入图片描述
环绕只是影响当前切面

九. 基于配置的AOP

不使用注解,配置xml

<bean id="myCalculator" class="com.jess.aop.MyCalculator"></bean>
<bean id="validateAspect" class="com.jess.aop.ValidateAspect"></bean>
<bean id="logUtils" class="com.jess.aop.LogUtils"></bean>

<!--告诉spring哪个是切面类:aop名称空间-->
<aop:config>
     <!--指定切面-->
     <aop:aspect ref="logUtils">
         <!-- 指定切面何时何地运行-->
         <aop:pointcut id="mypoint" expression="execution(* com.jess.aop.MyCalculator.*(..))"/>
         <aop:before method="logStrat" pointcut="execution(* com.jess.aop.MyCalculator.*(..))"></aop:before>
         <aop:after-returning method="logReturn" pointcut-ref="mypoint" returning="result"></aop:after-returning>
         <aop:after-throwing method="logException" pointcut-ref="mypoint" throwing="exception"></aop:after-throwing>
         <aop:after method="logEnd" pointcut-ref="mypoint"></aop:after>
     </aop:aspect>

     <aop:aspect ref="validateAspect"></aop:aspect>

 </aop:config>

十. AOP的应用

  1. 保存日志到数据库
  2. 做权限验证
  3. 做安全检查
  4. 做事务控制
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值