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日志并进行多维度分析。
相关文章
|
17天前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
38 1
|
8天前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
26天前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
【9月更文挑战第9天】AOP(面向切面编程)通过分离横切关注点提高模块化程度,如日志记录、事务管理等。Micronaut AOP基于动态代理机制,在应用启动时为带有特定注解的类生成代理对象,实现在运行时拦截方法调用并执行额外逻辑。通过简单示例展示了如何在不修改 `CalculatorService` 类的情况下记录 `add` 方法的参数和结果,仅需添加 `@Loggable` 注解即可。这不仅提高了代码的可维护性和可扩展性,还降低了引入新错误的风险。
37 13
|
22天前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
2月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
23 0
Spring高手之路22——AOP切面类的封装与解析
|
2月前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
38 0
|
2月前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
35 0
|
2月前
|
Java Spring 供应链
Spring 框架事件发布与监听机制,如魔法风暴席卷软件世界,开启奇幻编程之旅!
【8月更文挑战第31天】《Spring框架中的事件发布与监听机制》介绍了Spring中如何利用事件发布与监听机制实现组件间的高效协作。这一机制像城市中的广播系统,事件发布者发送消息,监听器接收并响应。通过简单的示例代码,文章详细讲解了如何定义事件类、创建事件发布者与监听器,并确保组件间松散耦合,提升系统的可维护性和扩展性。掌握这一机制,如同拥有一把开启高效软件开发大门的钥匙。
39 0
|
8天前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
2月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
下一篇
无影云桌面