spring AOP切面编程

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: spring AOP切面编程

spring AOP切面编程


AOP 讲解:


感兴趣的可以看看官方说明:spring-framework-5.3.8/docs/reference/html/core.html#aop


动态代理小案例


需求:一个接口有两个实现类,两个实现类实现接口的方法时,只有输出对象不同,其他操作一致。

//接口//接口
public interface Vehicle {
  public void run();
}
//实现类
public class Car implements Vehicle {
  @Override
    public void run() {
            System.out.println("交通工具开始运行了...");
            System.out.println("小汽车在公路running..");
            System.out.println("交通工具停止运行了...");
        }
}
public class Ship implements Vehicle {
    @Override
    public void run() {
        System.out.println("交通工具开始运行了...");
        System.out.println("大轮船在水上running...");
        System.out.println("交通工具停止运行了...");
    }
}


用传统的方法实现,单个方法简单直接,就是直接在单个的方法之间的调用,不够灵活。多个方法代码冗余,操作一多就很麻烦,不能够很好的解决问题。日志代码维护不方便,代码复用性差。

Vehicle vehicle = new Car();//可以切换成new Ship()
vehicle.run();


我们可以使用动态代理的方式解决。动态代理解决思路,就是在调用方法时,使用反射机制,根据方法去决定调用哪个对象方法。

package com.nlc.spring.aop.proxy;
import com.nlc.spring.aop.proxy.SmartAnimalable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class VehicleProxyProvider {
    //设置一个将来要运行的对象,只要是实现了Vehicle 接口的
    private Vehicle target_vehicle;
    public VehicleProxyProvider(Vehicle target_vehicle) {
      this.target_vehicle = target_vehicle;
    }
    //编写代码,返回一个Vehicle接口 的代理对象
    public Vehicle getProxy() {
        // 1.获取类加载对象
        ClassLoader loader = target_vehicle.getClass().getClassLoader();
        // 2.获取接口类型数组, 为什么是接口信息,不是方法,因为我们都是走接口的.
        Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();
        // 3. 创建InvocationHandler 对象- 以匿名内部类的方式方式来获取 InvocationHandler
        // 这个对象有一个方法是invoke 到时可以通过反射,动态调用目标对象的方法
        InvocationHandler invocationHandler = new InvocationHandler() {
        @Override
            //传入代理对象,目标方法,方法参数
            public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable
            {
                System.out.println("交通工具开始运行了...");
                //这个地方的方法,就是你调用时,动态传入的可能是run , 可能是hi 等
                Object result = method.invoke(target_vehicle, args);
                System.out.println("交通工具停止运行了...");
                return result;
            }
        };
        //将上面的loader, interfaces, invocationHandler 构建一个Vehicle的代理对象.
        Vehicle proxy = (Vehicle)Proxy.newProxyInstance(loader, interfaces,invocationHandler);
        //将创建的代理对象返回
        return proxy;
    }
}


测试

package com.nlc.spring.aop.proxy;
import com.nlc.spring.aop.proxy.MyProxyProvider;
public class Test {
    public static void main(String[] args) {
        //切换Vehicle 的实现类(对象)
        Vehicle vehicle = new Car();
        VehicleProxyProvider vehicleProxyProvider = new VehicleProxyProvider(vehicle);
        //获取对应的代理对象
        Vehicle proxy = vehicleProxyProvider.getProxy();
        System.out.println("proxy 编译类型是Vehicle");
        System.out.println("proxy 运行类型" + proxy.getClass());
        proxy.run();
        //动态代理的动态体现
        //1. proxy 运行类型是com.sun.proxy.$Proxy0 该类型被转型成Vehicle,因此可以调用Vehicle 的接口方法
        //2. 当执行run() 的时候会调用, 根据Java 的动态绑定机制, 这时直接调用Car 的run(),而是proxy 对象的invocationHandler 的invoke 方法(!!!!!!)
        //3. invoke 方法使用反射机制来调用run()方法注意这个run 方法也可以是 Vehicle 的其它方法, 这时就可以在调用run()方法前,进行前置处理和后置处理
        //4. 也就是说proxy 的target_vehicle 运行类型只要是实现了Vehicle 接口 ,就可以去调用不同的方法, 是动态的,变化的,底层就是使用反射完成的.     
    }
}


动态代理深入案例


需求说明

有一个SmartAnimal 接口,可以完成简单的加减法, 要求在执行getSum()和getSub()时,输出执行前,执行过程,执行后的日志输出,请思考如何实现.


传统的解决思路,在各个方法的[前,执行过程, 后]输出日志


定义接口

public interface SmartAnimalable {
    float getSum(float i, float j);
    float getSub(float i, float j);
}


方法实现

public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {
        System.out.println("日志--方法名--getSum 方法开始--参数:" + i + "," + j);
        float result = i + j;
        System.out.println("方法内部打印:result=" + result);
        System.out.println("日志--方法名--getSum 方法结束--结果:result=" + result);
        return result;
    }
    @Override
    public float getSub(float i, float j) {
        System.out.println("日志--方法名--getSub 方法开始--参数:" + i + "," + j);
        float result = i - j;
        System.out.println("方法内部打印:result=" + result);
        System.out.println("日志--方法名--getSub 方法结束--结果:result=" + result);
        return result;
    }
}


测试

public class AopTest {
@Test
public void smartDogTest() {
        SmartDog smartDog = new SmartDog();
        smartDog.getSum(5.23f, 6.89f);
        smartDog.getSub(5.23f, 6.89f);
    }
}
  1. 优点:实现简单直接
  2. 缺点:日志代码维护不方便,代码复用性差


动态代理


public class MyProxyProvider {
    private SmartAnimalable target_obj;
    // 构造器
    public MyProxyProvider(SmartAnimalable target_obj) {
       this.target_obj = target_obj;
        }
    public SmartAnimalable getProxy() {
        // 1.获取类加载对象
        ClassLoader loader = target_obj.getClass().getClassLoader();
        // 2.获取接口类型数组
        Class<?>[] interfaces = target_obj.getClass().getInterfaces();
        // 3.获取InvocationHandler 以匿名内部类的方式方式来获取InvocationHandler
        InvocationHandler h = new InvocationHandler() {
            // 4.以动态代理的方式调用目标对象的目标方法
            public Object invoke(Object proxy, Method method, Object[] args)  throws Throwable {
                Object result = null;
                String methodName = method.getName();
                try {
                    // 1. 在调用目标方法之前打印“方法开始”日志
                    System.out.println("日志--方法名:" + methodName + "--方法开始--参数:"+ Arrays.asList(args));
                    // 2. 调用目标方法并接收返回值
                    result = method.invoke(target_obj, args);
                    // 3. 在目标方法结束后打印“方法结束”日志
                    System.out.println("日志--方法名:" + methodName + "--方法正常结束--结果:result=" + result);
                } catch (Exception e) {
                    // 4.如果目标方法抛出异常,打印“方法异常”日志
                    e.printStackTrace();
                    System.out.println("日志--方法名:" + methodName + "--方法抛出异常--异常类型:" + e.getClass().getName());
                } finally {
                    // 5.在finally 中打印“方法最终结束”日志
                    System.out.println("日志--方法名:" + methodName + "--方法最终结束");
                }
                // 6. 返回目标方法的返回值
                return result;
            }
        };
        //生成SmartAnimaleable 的代理对象
        //需要三个参数,
        //1.就是loader(获取类加载对象)
        //2.接口数组
        //3.InvocationHandler 对象[这个相对麻烦..]
        SmartAnimalable proxy = (SmartAnimalable) Proxy.newProxyInstance( loader, interfaces, h);
        return proxy;
    }
}


实现接口

public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {
        float result = i + j;
        return result;
    }
    @Override
    public float getSub(float i, float j) {
        float result = i - j;
        return result;
    }
}


测试

@Test
public void smartDogTestByProxy() {
    SmartAnimalable smartDog = new SmartDog();
    //1.使用动态代理的方式来调用
    //2.当使用动态代理调用是SmartDog 的各个函数的日志输出就可以不写了,有代理对象帮我们完成.
    MyProxyProvider provider = new MyProxyProvider(smartDog);
    smartDog = provider.getProxy();
    smartDog.getSum(5.23f, 6.89f);
    smartDog.getSub(5.23f, 6.89f);
}


在上面的代码中,我们的输出语句功能比较弱,在实际开发中,使用方法嵌入目标方法会更方便。

public class MyProxyProvider {
    private SmartAnimalable target_obj;
    // 构造器
    public MyProxyProvider(SmartAnimalable target_obj) {
      this.target_obj = target_obj;
    }
    public void before(Object proxy, Method method, Object[] args) {
    // 1. 在调用目标方法之前打印“方法开始”日志
        System.out.println("before 日志--方法名:" + method.getName() + "--方法开始--参数:"+ Arrays.asList(args));
    }
    public void after(Method method, Object result) {
        // 3. 在目标方法结束后打印“方法结束”日志
        System.out.println("after 日志--方法名:" + method.getName() + "--方法正常结束--结果:result=" + result);
    }
    //可以更多方法
    public SmartAnimalable getProxy() {
        // 1.获取类加载对象
        ClassLoader loader = target_obj.getClass().getClassLoader();
        // 2.获取接口类型数组, 为什么是接口信息,而不是方法,是因为我们都是走接口
        的.
        Class<?>[] interfaces = target_obj.getClass().getInterfaces();
        // 3. 创建InvocationHandler 对象- 以匿名内部类的方式方式来获取InvocationHandler
        InvocationHandler h = new InvocationHandler() {
        // 4.以动态代理的方式调用目标对象的目标方法
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                String methodName = method.getName();
                try {
                    before(proxy,method,args);//切入到目标方法前
                    // 2. 调用目标方法并接收返回值
                    result = method.invoke(target_obj, args);
                    after(method, result);//切入到目标方法后
                } catch (Exception e) {
                    // 4.如果目标方法抛出异常,打印“方法异常”日志
                    e.printStackTrace();
                    System.out.println("日志--方法名:" + methodName+ "-- 方法抛出异常-- 异常类型: " +
                    e.getClass().getName());
                } finally {
                    // 5.在finally 中打印“方法最终结束”日志
                    System.out.println("日志--方法名:" + methodName + "--方法最终结束");
                }
                // 6. 返回目标方法的返回值
                  return result;
            }
        };
        //生成SmartAnimaleable 的代理对象
        //需要三个参数,
        //1.就是loader(获取类加载对象)
        //2.接口数组
        //3.InvocationHandler 对象[这个相对麻烦..]
        SmartAnimalable proxy = (SmartAnimalable) Proxy.newProxyInstance(loader, interfaces, h);
        return proxy;
    }
}

这样写耦合度高。


优化:


自己写的一个切入类

public class nAOP {
    public static void before(Object proxy, Method method, Object[] args) {
        // 1. 在调用目标方法之前打印“方法开始”日志
        System.out.println("自己的切入类的before 日志--方法名:" + method.getName()+ "--方法开始--参数:"+ Arrays.asList(args));
    }
    public static void after(Method method, Object result) {
        // 3. 在目标方法结束后打印“方法结束”日志
        System.out.println("自己的切入类的after 日志--方法名:" + method.getName() + "--方法正常结束--结果:result=" + result);
    }
}


核心

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object result = null;
    String methodName = method.getName();
    try {
        //切入到目标方法前
        nAOP.before(proxy,method,args);
        // 2. 调用目标方法并接收返回值
        result = method.invoke(target_obj, args);
        //切入到目标方法后
        nAOP.after(method,result);
    } catch (Exception e) {
        // 4.如果目标方法抛出异常,打印“方法异常”日志
        e.printStackTrace();
        System.out.println("日志--方法名:" + methodName+ "--方法抛出异常--异常类型:" + e.getClass().getName());
    } finally {
        // 5.在finally 中打印“方法最终结束”日志
        System.out.println("日志--方法名:" + methodName + "--方法最终结束");
    }
    // 6. 返回目标方法的返回值
    return result;
}

还是不够灵活,复用性差。是一种硬编码(没有注解和反射支撑)。


spring AOP 入门


spring aop的底层是ASPECTJ。AOP 的全称(aspect oriented programming) ,面向切面编程。就是切面类的方法可以插入到任意类、任意方法前、后、异常时、最终进行使用。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yyTtS2k6-1688871244832)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20230707150731509.png)]


使用aop前有一些准备工作:


需要引入核心的aspect 包


在切面类中声明通知方法:


前置通知:@Before


返回通知:@AfterReturning


异常通知:@AfterThrowing


后置通知:@After


环绕通知:@Around


举例


SmartAnimalAspect 作用就是去实现切面编程,原来的MyProxyProvider 类就可以去掉了.

package com.nlc.spring.aop.springaop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
// 使用切面编程替代原来的动态代理类,机制是一样的.
@Aspect //表示这个类是一个切面类,采用注解@Aspect 一定要记得配置开启基于注解的AOP<aop:aspectj-autoproxy/>,不然不会实现
@Component //声明这个组件需要加入到IOC 容器
public class SmartAnimalAspect {
    //这个就对应动态代理类的
    //System.out.println(" 日志-- 方法名: "+methodName+"-- 方法开始-- 参数:"+Arrays.asList(args));
    @Before(value = "execution(public float com.nlc.spring.aop.springaop.SmartDog.getSum(float ,float))")
    //表示要在符合路径和方法参数类型的方法前执行
    public void showBeginLog(JoinPoint joinPoint) {
        //JoinPoint joinPoint表示连接点
        System.out.println("前置通知");
        Signature signature = joinPoint.getSignature();
        // 1. 在调用目标方法之前打印“方法开始”日志
        System.out.println("日志--方法名:" + signature.getName() + "--方法开始--参数: "+ Arrays.asList(joinPoint.getArgs()));
    }
        //这个就对应动态代理类的
    //System.out.println("日志--方法名: "+methodName+"--方法正常结束--结果: result="+result);
    @AfterReturning(value = "execution(public float com.nlc.spring.aop.springaop.SmartDog.getSum(float ,float))")
    //表示要在符合路径和方法参数类型的方法执行结束后执行
    public void showSuccessEndLog(JoinPoint joinPoint) {
        System.out.println("返回通知");
        Signature signature = joinPoint.getSignature();
        // 3. 在目标方法结束后打印“方法结束”日志
        System.out.println("日志--方法名:" + signature.getName() + "--方法正常结束--~");
    }
    //这个就对应动态代理类的
    //System.out.println("日志--方法名: "+methodName+"--方法抛出异常--异常类型:"+e.getClass().getName());
    @AfterThrowing(value = "execution(public float  com.nlc.spring.aop.springaop.SmartDog.getSum(float ,float))")
    //表示要在符合路径和方法参数类型的方法异常时执行
    public void showExceptionLog() {
      System.out.println("异常通知");
    }
    //这个就对应动态代理类的
    //System.out.println("日志--方法名:"+methodName+"--方法最终结束");
    @After(value = "execution(public float  com.nlc.spring.aop.springaop.SmartDog.getSum(float ,float))")
    //表示要在符合路径和方法参数类型的方法最后执行
    public void showFinallyEndLog() {
       System.out.println("最终通知");
    }
}


配置beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 配置自动扫描的包,根据实际情况配置即可-->
    <context:component-scan base-package="com.nlc.spring.aop.aspectj"/>
    <!-- 开启基于注解的AOP 功能-->
    <aop:aspectj-autoproxy/>
</beans>


测试

@Test
public void smartDogTestByProxy() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans6.xml");
    //通过接口来
    SmartAnimalable bean = ioc.getBean(SmartAnimalable.class);
    float sum = bean.getSum(101.0f, 11.2f);
    System.out.println("sum= " + sum);
}


注意


  1. 1.切入表达式可以使用模糊配置
@Before(value="execution(* com.nlc.aop.proxy.SmartDog.*(..))")


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


3.切面类方法是由程序员自己命名的,可按照规范命名


4.当spring 容器开启了 aop:aspectj-autoproxy/ , 我们获取注入的对象, 需要以接口的类型来获取, 因为你注入的对象.getClass() 已经是代理类型了!


5.当spring 容器开启了 aop:aspectj-autoproxy/ , 我们获取注入的对象, 也可以通过id 来获取, 但是也要转成接口类型.


AOP-切入表达式


AOP-切入点表达式重用


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

//=====AOP-切入点表达式重用start ======
/*
* 这样定义的一个切入点表达式,就可以在其它地方直接使用
*/
@Pointcut(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))")
public void myPointCut() {
}
// @Before(value="execution(public floatcom.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))")没有用表达式重用
@Before(value = "myPointCut()")//采用表达式重用
public void showBeginLog(JoinPoint joinPoint) { //前置方法
    //得到方法的签名
    // 调用前置通知对应的方法签名:public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float)
    Signature signature = joinPoint.getSignature();
    //得到方法名.
    String method_name = signature.getName();
    //得到参数
    Object[] args = joinPoint.getArgs();
    System.out.println("前置通知" + "--调用的方法是" + method_name + "--参数是--" + Arrays.asList(args));
}
  //@After(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))")
@After(value = "myPointCut()")
public void showFinallyEndLog() {
  System.out.println("最终通知-- AOP-切入点表达式重用");
}
/**
* returning = "res", Object res 名称保持一致
* @param joinPoint
* @param res 调用getSum() 返回的结果
*/
    @AfterReturning(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))",returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
  System.out.println("返回通知" + "--结果是--" + res);
}
@AfterThrowing(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))",throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
  System.out.println("异常通知-- 异常信息--" + throwable);
}
//=====AOP-切入点表达式重用end ======

注意事项和细节

  1. 切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效。
  2. 切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效。
  3. 切入表达式也可以对没有实现接口的类,进行切入。


AOP-JoinPoint


通过JoinPoint 可以获取到调用方法的信息。

public void beforeMethod(JoinPoint joinPoint){
        joinPoint.getSignature().getName(); // 获取目标方法名
        joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名
        joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名
        joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)
        Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组
        joinPoint.getTarget(); // 获取被代理的对象
        joinPoint.getThis(); // 获取代理对象自己
}


AOP-返回通知获取结果


查看@AfterReturning注解的源码可以发现里面有一个属性returning,我们只要把returning属性加到表达式后面就可以接收方法的返回值。

@AfterReturning(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))",returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
  System.out.println("返回通知" + "--结果是--" + res );
}


AOP-异常通知中获取异常


查看@AfterThrowing注解的源码可以发现里面有一个属性throwing,我们只要把throwing属性加到表达式后面就可以接收方法的异常情况。

@AfterThrowing(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))",throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
  System.out.println("异常通知-- 异常信息--" + throwable);
}


AOP-环绕通知


环绕通知可以完成其它四个通知要做的事情。

@Aspect //表示这个类是一个切面类
@Component //需要加入到IOC 容器
public class SmartAnimalAspect {
    //=====环绕通知start=====
    @Around(value="execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))")
    public Object doAround(ProceedingJoinPoint joinPoint) {
            Object result = null;
            String methodName = joinPoint.getSignature().getName();
            try {
                        //1.相当于前置通知完成的事情
                        Object[] args = joinPoint.getArgs();
                        List<Object> argList = Arrays.asList(args);
                        System.out.println("AOP 环绕通知--" + methodName + "方法开始了--参数有:  " + argList);
                        //在环绕通知中一定要调用joinPoint.proceed()来执行目标方法
                        result = joinPoint.proceed();
                        //2.相当于返回通知完成的事情
                        System.out.println("AOP 环绕通知" + methodName + "方法结束了--结果是:"+ result);  
            } catch (Throwable throwable) {
                //3.相当于异常通知完成的事情
                System.out.println("AOP 环绕通知" + methodName + "方法抛异常了--异常对象:" + throwable);
            } finally {
                //4.相当于最终通知完成的事情
                System.out.println("AOP 后置通知" + methodName + "方法最终结束了...");
            }
            return result;
     }
}
//=====环绕通知end=====


AOP-切面优先级问题


基本语法:@order(value=n) 来控制n 值越小,优先级越高.


直接在方法上写好注解就可以使用。切面类方法执行和返回信息顺序类似Filter 的过滤链式调用机制,但是前面和返回相反,后执行的会被先执行的包裹起来。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QaXjjPmk-1688871244833)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20230707160803527.png)]


AOP-基于XML 配置AOP


创建SmartAnimalAspect类

public class SmartAnimalAspect {
    public void showBeginLog(JoinPoint joinPoint) { //前置通知
            //得到方法的签名
                Signature signature = joinPoint.getSignature();
            //得到方法名.
            String method_name = signature.getName();
            //得到参数
            Object[] args = joinPoint.getArgs();
            System.out.println("XML 方式: 前置通知" + "--调用的方法是" + method_name + "--参数是--" + Arrays.asList(args));
    }
    public void showFinallyEndLog() {
      System.out.println("XML 方式: 最终通知-- AOP-切入点表达式重用");
    }
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
      System.out.println("XML 方式: 返回通知" + "--结果是--" + res);
    }
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
      System.out.println("XML 方式: 异常通知-- 异常信息--" + throwable);
    }
}


创建配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置自动扫描的包-->
<context:component-scan base-package="com.nlc.spring.aop.xml"/>
<!-- 开启基于注解的AOP 功能-->
<aop:aspectj-autoproxy/>
<!-- 配置SmartAnimalAspect bean -->
<bean id="smartAnimalAspect" class="com.nlc.spring.aop.xml.SmartAnimalAspect"/>
<!--配置SmartDog-->
<bean class="com.nlc.spring.aop.xml.SmartDog" id="smartDog"/>
    <aop:config>
        <!-- 配置统一切入点-->
        <aop:pointcut expression="execution(public float com.nlc.spring.aop.xml.SmartDog.getSum(float, float))"  id="myPointCut"/>
        <aop:aspect ref="smartAnimalAspect" order="1">
                <!-- 配置各个通知对应的切入点-->
                <aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
                <aop:after-returning method="showSuccessEndLog" pointcut-ref="myPointCut" returning="res"/>
                <aop:after-throwing method="showExceptionLog"  pointcut-ref="myPointCut" throwing="throwable"/>
                <aop:after method="showFinallyEndLog" pointcut-ref="myPointCut"/>
        </aop:aspect>
   </aop:config>
</beans>


测试

public class AopXMLTest {
    /**
    * 切面编程aop:aspectj
    */
    @Test
    public void testAopJoinPoint() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("aop_ioc.xml");
        SmartAnimalable bean = ioc.getBean(SmartAnimalable.class);
        bean.getSum(10.0f, 11.2f);
        System.out.println("-----------------");
    }
}

=“showSuccessEndLog” pointcut-ref=“myPointCut” returning=“res”/>

<aop:after-throwing method=“showExceptionLog” pointcut-ref=“myPointCut” throwing=“throwable”/>

<aop:after method=“showFinallyEndLog” pointcut-ref=“myPointCut”/>

</aop:aspect>

</aop:config>

测试
```java
public class AopXMLTest {
    /**
    * 切面编程aop:aspectj
    */
    @Test
    public void testAopJoinPoint() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("aop_ioc.xml");
        SmartAnimalable bean = ioc.getBean(SmartAnimalable.class);
        bean.getSum(10.0f, 11.2f);
        System.out.println("-----------------");
    }
}


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
7天前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
40 1
什么是AOP面向切面编程?怎么简单理解?
|
21天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
28 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
6天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
15 1
|
12天前
|
XML Java 开发者
论面向方面的编程技术及其应用(AOP)
【11月更文挑战第2天】随着软件系统的规模和复杂度不断增加,传统的面向过程编程和面向对象编程(OOP)在应对横切关注点(如日志记录、事务管理、安全性检查等)时显得力不从心。面向方面的编程(Aspect-Oriented Programming,简称AOP)作为一种新的编程范式,通过将横切关注点与业务逻辑分离,提高了代码的可维护性、可重用性和可读性。本文首先概述了AOP的基本概念和技术原理,然后结合一个实际项目,详细阐述了在项目实践中使用AOP技术开发的具体步骤,最后分析了使用AOP的原因、开发过程中存在的问题及所使用的技术带来的实际应用效果。
36 5
|
2天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
6 0
|
1月前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
51 2
|
1月前
|
Java 数据库连接 Spring
【2021Spring编程实战笔记】Spring开发分享~(下)
【2021Spring编程实战笔记】Spring开发分享~(下)
25 1
|
29天前
|
Java 容器
AOP面向切面编程
AOP面向切面编程
38 0
|
30天前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
44 0
|
1月前
|
XML Java 数据库连接
【2020Spring编程实战笔记】Spring开发分享~(上)
【2020Spring编程实战笔记】Spring开发分享~
48 0