Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AOP编程比较

本篇博文用一个稍复杂点的案例来对比一下基于XML配置与基于AspectJ注解配置的AOP编程的不同。

相关引入包等Spring  AOP编程准备,请参考小编的其他博文,这里不再赘述。

案例要求:

写一个简单的实现四则运算的计算器。

加入AOP功能:日志功能;检测参数中是否有负数的功能。

废话不多说了,直接上代码:

(一)基于XML配置:

定义了一个接口类:

package com.edu.aop;

public interface ArithmeticCalculator {

    int add(int i,int j);
    int sub(int i,int j);
    int mul(int i,int j);
    int div(int i,int j);
}

实现类:

package com.edu.aop;

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    @Override
    public int add(int i, int j) {
        return i+j;
    }

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

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

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

}

日志切片类:

package com.edu.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;

public class LoggingAspect {
/**
 * 日志切面类
 */
    
    public void beforeMethod(JoinPoint joinPoint){
        //获取方法名
        String methodName=joinPoint.getSignature().getName();
        //获取方法实参值列表
        Object[] args=joinPoint.getArgs();
        System.out.println("The method "+methodName+" begin with "+Arrays.asList(args));
    }
    
    public void afterMethod(JoinPoint joinPoint){
        String methodName=joinPoint.getSignature().getName();
        System.out.println("The method "+methodName+" ends");
    }
}

检测参数中是否有负数的切片类:

package com.edu.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;

public class ValidationAspect {

    public void validationArgs(JoinPoint joinPoint){
        String methodName=joinPoint.getSignature().getName();
        Object[] args=joinPoint.getArgs();
        System.out.println("待验证参数:"+Arrays.asList(args));
        if(args!=null&&args.length>0){
            for(int i=0;i<args.length;++i){
                if(((Integer)args[i]).intValue()<0){
                    System.out.println("警告:方法"+methodName+"()第"+(i+1)+"个参数为负数:"+args[i]);
                }
            }
        }
    }
}

xml配置文件applicationContext.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 配置bean -->
<bean id="arithmetic" class="com.edu.aop.ArithmeticCalculatorImpl"></bean>

<!-- 分别将切面类声明配置成一个bean -->
<bean id="logging" class="com.edu.aop.LoggingAspect"></bean>
<bean id="validation" class="com.edu.aop.ValidationAspect"></bean>

<aop:config>
<!-- 配置日志切片类 -->
<aop:aspect ref="logging" order="2">
<!-- 配置前置通知 及前置通知的切入点-->
<aop:before method="beforeMethod" pointcut="execution(* com.edu.aop.ArithmeticCalculatorImpl.*(..))"></aop:before>
<!-- 配置后置通知及后置通知的切入点 -->
<aop:after method="afterMethod" pointcut="execution(* com.edu.aop.ArithmeticCalculatorImpl.*(..))"></aop:after>
</aop:aspect>

<!-- 配置检测参数切片类 -->
<aop:aspect ref="validation" order="1">
<!-- 配置前置通知 及前置通知的切入点-->
<aop:before method="validationArgs" pointcut="execution(* com.edu.aop.ArithmeticCalculatorImpl.*(..))"></aop:before>
</aop:aspect>
</aop:config>
</beans>

主方法检测类:

package com.edu.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void main(String[] args) {

        ApplicationContext act=new ClassPathXmlApplicationContext("applicationContext.xml");
        ArithmeticCalculator arithmetic=(ArithmeticCalculator)act.getBean("arithmetic");
        int result=arithmetic.add(10, 20);
        System.out.println("result:"+result);
        result=arithmetic.div(-36, 4);
        System.out.println("result:"+result);
    }

}

运行结果:

(二)基于AspectJ注解配置的AOP编程:

直接上代码:

接口类定义同上。

xml配置文件applicationContext.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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 自动扫描的包,实现对注解Bean的管理 -->
<context:component-scan base-package="com.edu.aop"></context:component-scan>
<!-- 使Aspect的注解起作用,从而实现:自动为匹配的类生成代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

接口实现类:

package com.edu.aop;

import org.springframework.stereotype.Component;

//@Component 注解Bean,并设定其名称为arithmetic
@Component("arithmetic")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    @Override
    public int add(int i, int j) {
        return i+j;
    }

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

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

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

}

日志切片类:

package com.edu.aop;

import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

//注释声明切面,切面的名称为默认名称(loggingAspect)(即类名首字母小写)
@Order(2)        //注释切面的优先级别,数字越小,级别越高
@Aspect            //@Aspect 注解声明该Bean是个切面
@Component        //@Component 注解Bean,默认名称为loggingAspect
public class LoggingAspect {
/**
 * 日志切面类
 */
    //注释“前置通知”及其切入点
    @Before("execution(public int com.edu.aop.ArithmeticCalculator.add(int,int))")
    public void beforeMethod(JoinPoint joinPoint){
        //获取方法名
        String methodName=joinPoint.getSignature().getName();
        //获取方法实参值列表
        Object[] args=joinPoint.getArgs();
        System.out.println("The method "+methodName+" begin with "+Arrays.asList(args));
    }
    
    //注解“后置通知”及其切入点
    @After("execution(* com.edu.aop.ArithmeticCalculator.*(..))")
    public void afterMethod(JoinPoint joinPoint){
        String methodName=joinPoint.getSignature().getName();
        System.out.println("The method "+methodName+" ends");
    }
}

检测参数中是否有负数的切片类:

package com.edu.aop;

import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

//注释声明切面,切面的名称为默认名称(loggingAspect)(即类名首字母小写)
@Order(1)    //注解切面的优先级别,数字越小,级别越高
@Aspect        //注解为一个切面
@Component    //注解为一个Bean组件,默认名称为validationAspect
public class ValidationAspect {

    //注解为前置通知,并注解其切入点表达式
    @Before("execution(* com.edu.aop.ArithmeticCalculator.*(..))")
    public void validationArgs(JoinPoint joinPoint){
        String methodName=joinPoint.getSignature().getName();
        Object[] args=joinPoint.getArgs();
        System.out.println("待验证参数:"+Arrays.asList(args));
        if(args!=null&&args.length>0){
            for(int i=0;i<args.length;++i){
                if(((Integer)args[i]).intValue()<0){
                    System.out.println("警告:方法"+methodName+"()第"+(i+1)+"个参数为负数:"+args[i]);
                }
            }
        }
    }
}

主方法检测类同上。

运行结果:

由以上案例的两个版本,可以看到两种AOP编程方式在设计思想和设计过程基本一致,只不过,“基于XML的AOP编程”是将配置Bean、切面、通知等操作放在了配置文件中。而“基于AspectJ的AOP编程”则是将这些配置信息放在了源码中,只在配置文件中配置了“AspectJ的注解支持”(即空的<aop:aspectj-autoproxy>元素)和自动扫描的包的支持。

原文地址:https://www.cnblogs.com/dudududu/p/8487750.html