spring-AOP

        AOP(Aspect Oriented Programming),即面向切面编程,是spring框架的核心内容之一。

 在使用时,需要将aspectj-1.8.5.jarspring-aop.jar,aspectjweaver-1.8.5.jar一起使用,以便在Spring应用程序中实现AOP功能。

1、AOP的全称(aspect oriented programming),面向切面编程

2、一张示意图说明AOP的相关概念

概念
1)日志
2)   连接点:切面类可以通过连接点,获取到目标类的目标方法签名
3)通知(五种)
4)切入表达式

3、aop的实现方式

(1)基于动态代理的方式[内置aop实现]

(2)使用框架aspectj来实现

4、aop编程的基本说明

基本说明:
1.需要引入核心的aspect包, 即aop的核心包


2.在切面类中声明通知方法

前置通知:@Before

返回通知:@AfterReturning

异常通知:@AfterThrowing

后置通知:@After

环绕通知:@Around   // 环绕通知可以完成另外四个通知的所有事情

执行顺序


3.五种通知和前面写的动态代理类方法的对应关系

5、关于@After与@AfterReturning的区别  (区别!!!!!)

  1. @After

    • @After 用于在目标方法执行之后执行一个通知(advice)。无论目标方法是否成功,@After 通知都会被执行。
    • 这是一种通用的后期通知,适用于任何返回类型的方法。
  2. @AfterReturning

    • @AfterReturning 用于在目标方法成功执行并返回值之后执行一个通知。只有当目标方法正常返回时,@AfterReturning 通知才会被执行。如果目标方法抛出异常,@AfterReturning 通知不会被执行。
    • 与 @After 不同,@AfterReturning 需要指定一个参数来接收从目标方法返回的值。这个参数的类型和目标方法的返回类型应该匹配。
    • 这是一种更细粒度的后期通知,通常用于在方法返回特定值之后执行某些操作。

以下是一个简单的示例来说明这两个注解的区别:

假设有一个名为 ServiceMethod 的类,其中有一个名为 execute 的方法:

public class ServiceMethod {  
    public String execute(String input) {  
        // 处理输入并返回结果  
        return "处理结果";  
    }  
}

 如果你想在 execute 方法执行之后打印一些信息,可以使用 @After

@Aspect  
public class LoggingAspect {  
    @After("execution(* com.example.ServiceMethod.execute(..))")  
    public void logAfterExecution(JoinPoint joinPoint) {  
        System.out.println("execute 方法执行后:");  
    }  
}

 如果你想在 execute 方法成功返回结果之后打印返回值,可以使用 @AfterReturning

@Aspect  
public class LoggingAspect {  
    @AfterReturning(pointcut = "execution(* com.example.ServiceMethod.execute(..))", returning = "result")  
    public void logAfterExecution(JoinPoint joinPoint, String result) {  
        System.out.println("execute 方法返回结果:" + result);  
    }  
}

 在这个例子中,@After 会无条件地打印信息,而 @AfterReturning 只有在 execute 方法成功返回结果时才会打印信息。

5.aop编程入门案例

        1>先开发两个类

package com.spring.aop;

public interface SmartAnimalable {
    float getSum(float i,float j);
    float getSub(float i, float j);
}
package com.spring.aop;

import org.springframework.stereotype.Component;

@Component(value = "dog")
public class SmartDog implements SmartAnimalable{
    float result ;
    @Override
    public float getSum(float i, float j) {
        result = i+j;
        System.out.println("method执行完");
        return result;
    }

    @Override
    public float getSub(float i, float j) {
        result = i-j;
        return result;
    }
}

        2>在容器中开启基于注解的aop功能

    <!--    开启基于注解的aop功能-->
    <aop:aspectj-autoproxy/>

    <context:component-scan base-package="com.spring.*"/>

        3>开发切面类, 注意 通配 写法

package com.spring.aop;

import org.aspectj.lang.annotation.*;

import org.springframework.stereotype.Component;
@Aspect
@Component
public class SmartAspect {
    //开发一个方法, 在目标类的目标方法之前执行
    /***@Before:前置通知
     *value=值后面写的时候切入表达式execution(public float com.spring.aop.SmartDog.*(..))
     *execution:关键字不要改
     *public float com.spring.aop.SmartDog·getSum(float,float)你有对哪个类的哪个方法切面编码
     *特别强调回显 public float 【访问修饰符和返回值要有..】
     */
    @Before(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public void showSmartlog() {
        System.out.println("前置通知");
    }


    @AfterReturning(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public void showSuccesslog() {
        System.out.println("返回通知");
    }

    @AfterThrowing(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public void showExceptionlog() {
        System.out.println("异常通知");
    }

    @After(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public void showFinallylog() {
        System.out.println("最终通知");
    }
}

4、测试

package com.spring.test;

import com.spring.aop.SmartAnimalable;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        SmartAnimalable smartAnimalable =  applicationContext.getBean(SmartAnimalable.class);
     //   SmartDog smartDog = (SmartDog) applicationContext.getBean("dog"); //会报错,因为”dog“
        //已经被代理类代理了,代理类也实现了SmartAnimalable接口。 具体原因是动态代理
        smartAnimalable.getSum(3.4f,3.2f);
    }
}

5、run

6、细节:切入表达式的更多说明

细节说明:
1.对切入表达式的基本使用

@Before(value="execution(public float com.spring.aop.SmartDog.getSum(float, float))"
2.切入表达式的更多配置,比如使用模糊配置

//@Before(value="execution(*com.spring.aop.SmartDog.*(..))")

//表示所有访问权限,的所有包的下所有类的所有方法,都会被执行该前置通知方法@Before(value="execution(* *.*(..))")

7、AOP-JoinPoint

JoinPoint连接点, 这个类可以获取到调用函数的签名。

可以在前置通知,后置通知,任意通知当中使用连接点。

具体示例

package com.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;

import org.springframework.stereotype.Component;

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

@Aspect
@Component
public class SmartAspect {
   
    @Before(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public void showSmartlog(JoinPoint joinPoint) {
        System.out.println("前置通知");
        //获取函数签名
        Signature signature = joinPoint.getSignature();
        //获取函数名
        String name = signature.getName();
        //获取方法参数
        Object[] args = joinPoint.getArgs();
        List<Object> list = Arrays.asList(args);
        System.out.println("目标方法的名称"+name+"参数"+list);
    }

8、在返回通知方法获取返回结果

用法示例

其中,需要在切入表达式后,再加 returning = "abc", 切入的方法参数加 Object abc

 @AfterReturning(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))",returning = "abc")
    public void showSuccesslog(JoinPoint joinPoint,Object abc) {
        System.out.println("返回通知");
        System.out.println("拿到的调用函数的返回值"+abc);
    }

9、在异常通知,获取异常对象

   @AfterThrowing(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))",throwing = "except")
    public void showExceptionlog(JoinPoint joinPoint,Throwable except) {
        System.out.println("异常通知");
        System.out.println("异常是 "+except);
    }

10、环绕通知 (可以做完其他四个通知的所有事情)

 @Around(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public Object showAroundlog(ProceedingJoinPoint joinPoint) {
       Object result = null;
       //获取调用函数的签名
            String name = joinPoint.getSignature().getName();
            //获取调用函数的参数
            Object[] args = joinPoint.getArgs();
            try{
                //输出前置通知的内容,
                System.out.println("前置通知 调用函数名称" +name+ "参数"+Arrays.asList(args));
                //调用目标方法,获取返回值
                result = joinPoint.proceed();
                System.out.println("返回通知"+result); //干的是@AfterReturning的事儿

            }catch (Throwable exp){
                System.out.println("异常通知"+ "异常消息" + exp.getMessage());
            }finally {
                System.out.println("最终通知"); //干的是@After的事儿
            }
            return result;
        }

11、切面表达式重用

为了统一管理切面表达式, 可以使用切面表达式重用技术。

切面表达式重用:  类似于在一个类当中声明一个属性,各个方法可以调用这个属性

具体示例:

使用

@PointCut(value= "切面表达式")

 public void myPointCut() {
    }

其他通知的切面表达式直接引用 即可, 例如

@Before(value="myPointCut")

  public void showSmartlog(JoinPoint joinPoint) {
       ........
    }

package com.spring.aop;

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;
import java.util.List;

@Aspect
@Component
public class SmartAspect {
   
    @Before(value="myPointCut()")
    public void showSmartlog(JoinPoint joinPoint) {
        System.out.println("前置通知");
        //获取函数签名
        Signature signature = joinPoint.getSignature();
        //获取函数名
        String name = signature.getName();
        //获取方法参数
        Object[] args = joinPoint.getArgs();
        List<Object> list = Arrays.asList(args);
        System.out.println("目标方法的名称"+name+"参数"+list);
    }



    @Pointcut(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public void myPointCut() {
    }

}

12、切面表达式优先级

如果同一个函数,有多个切面在同一个切入点切入,那么执行的优先级如何控制?

基本语法:

@Order(value=n)     // n值越小,代表优先级越高

示例:

package com.spring.aop;

@Aspect
@Order(value = 1)
@Component
public class SmartAspect {

    @Before(value="myPointCut()")
    public void showSmartlog(JoinPoint joinPoint) {
        System.out.println("前置通知");
        //获取函数签名
        Signature signature = joinPoint.getSignature();
        //获取函数名
        String name = signature.getName();
        //获取方法参数
        Object[] args = joinPoint.getArgs();
        List<Object> list = Arrays.asList(args);
      //  System.out.println("目标方法的名称"+name+"参数"+list);
    }


    @AfterReturning(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))",returning = "abc")
    public void showSuccesslog(JoinPoint joinPoint,Object abc) {

        System.out.println("最终通知"+"拿到的调用函数的返回值"+abc);
    }


    @After(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public void showFinallylog() {
        System.out.println("返回通知");
    }
    @Pointcut(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public void myPointCut() {
    }


}

 复制粘贴一个新的切面类, 改输出为**通知2, 优先级设置为2

package com.spring.aop;

@Aspect
@Order(value = 2)
@Component
public class SmartAspect2 {

    @Before(value="myPointCut()")
    public void showSmartlog(JoinPoint joinPoint) {
        System.out.println("前置通知2");
        //获取函数签名
        Signature signature = joinPoint.getSignature();
        //获取函数名
        String name = signature.getName();
        //获取方法参数
        Object[] args = joinPoint.getArgs();
        List<Object> list = Arrays.asList(args);
       // System.out.println("目标方法的名称"+name+"参数"+list);
    }


    @AfterReturning(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))",returning = "abc")
    public void showSuccesslog(JoinPoint joinPoint,Object abc) {

        System.out.println("最终通知2"+"拿到的调用函数的返回值"+abc);
    }


    @After(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public void showFinallylog() {
        System.out.println("返回通知2");
    }
    @Pointcut(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))")
    public void myPointCut() {
    }
=
}
public class AopTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        SmartAnimalable smartAnimalable =  applicationContext.getBean(SmartAnimalable.class);
    
        smartAnimalable.getSum(3.4f,3.2f);
    }
}

结果:

结果分析:

1、容易看到切面1的before通知被执行,然后才到切面2的before通知被执行。

2、但是返回通知 却是切面2先执行, 为什么呢?因为

想像 切面一的所有方法为一个整体, 切面二的所有方法为一个整体, 然后切面1的优先级高, 先压入栈中, 切面2后被压入栈中。 那么, 切面一的前置通知先被执行, 切面2的前置通知后执行。 

在出栈时,是切面2先出栈, 所以切面2的返回通知,最终通知先执行, 而切面1的后执行。

栈:后进先出

13、AOP的xml配置

像配置bean一样, AOP不仅可以使用注解配置, 也可以使用xml配置。 注意 AOP使用xml配置,  那么相关的 切面类 就是一个普通类,需要去掉任何注解

1、首先是, xml配置

  <!--    开启基于注解的aop功能-->
    <aop:aspectj-autoproxy/>

    <context:component-scan base-package="com.spring.*"/>
<!--    配置一个切面类的bean-->
    <bean id="smartAspect" class="com.spring.aop.SmartAspect"/>
<!--    配置切面与切面表达式-->
    <aop:config>
        <aop:pointcut id="myPointCut" expression
                ="execution(public float com.spring.aop.SmartDog.getSum(float,float))"/>
        <aop:aspect ref="smartAspect" order="1">
            <aop:before method="showSmartlog" pointcut-ref="myPointCut"/>
            <aop:after-returning method="showSuccesslog" pointcut-ref="myPointCut" returning="abc"/>
            <aop:after-throwing method="showExceptionlog" pointcut-ref="myPointCut" throwing="except"/>
            <aop:after method="showFinallylog" pointcut-ref="myPointCut" />
<!--            此处也可以配置环绕通知-->
        </aop:aspect>
    </aop:config>

2、 切面类, 注意此时是一个普通类

package com.spring.aop;


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

public class SmartAspect {
   
    public void showSmartlog(JoinPoint joinPoint) {
        System.out.println("前置通知");
        //获取函数签名
        Signature signature = joinPoint.getSignature();
        //获取函数名
        String name = signature.getName();
        //获取方法参数
        Object[] args = joinPoint.getArgs();
        List<Object> list = Arrays.asList(args);
        System.out.println("目标方法的名称"+name+"参数"+list);
    }


   // @AfterReturning(value="execution(public float com.spring.aop.SmartDog.getSum(float,float))",returning = "abc")
    public void showSuccesslog(JoinPoint joinPoint,Object abc) {

        System.out.println("返回通知"+"拿到的调用函数的返回值"+abc);
    }


    public void showExceptionlog(JoinPoint joinPoint,Throwable except) {
        System.out.println("异常通知");
        System.out.println("异常是 "+except);
    }

    public void showFinallylog() {
        System.out.println("最终通知");
    }


}

3、测试

package com.spring.test;

import com.spring.aop.SmartAnimalable;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        SmartAnimalable smartAnimalable =  applicationContext.getBean(SmartAnimalable.class);
     //   SmartDog smartDog = (SmartDog) applicationContext.getBean("dog"); //会报错,因为”dog“
        //已经被代理类代理了,代理类也实现了SmartAnimalable接口。 具体原因是动态代理
        smartAnimalable.getSum(3.4f,3.2f);
    }
}

4、结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值