Spring4(二)AOP

需求:假如,一个业务类有四个方法(加减),我们要在每个方法都加上日志操作。

1、没有AOP,实现日志

1.1 定义接口

定义一个数字运算的接口,其中有两个方法:加法运算和减法运算:

package com.spring.aop;

public interface MathCalculator {
    public int add(int a,int b);
    public int sub(int a,int b);
}

1.2 定义实现类

定义实现类,除了进行运算,还要打印简单的日志功能:

package com.spring.aop;

public class NonAopMathCalculator implements MathCalculator {
    @Override
    public int add(int a, int b) {
        System.out.println("方法执行前参数是:"+a+","+b);
        int result = a+b;
        System.out.println("方法执行后结果是:"+result);
        return result;
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("方法执行前参数是:"+a+","+b);
        int result = a-b;
        System.out.println("方法执行后结果是:"+result);
        return result;
    }
}

1.3 测试

package com.spring.aop;

public class MathCalculatorTest {
    public static void main(String[] args) {
        MathCalculator mathCalculator = new NonAopMathCalculator();
        int add = mathCalculator.add(9, 6);
        System.out.println("加法计算结果:"+add);
        int sub = mathCalculator.sub(9, 6);
        System.out.println("减法计算结果:"+sub);
    }
}

打印结果:

方法执行前参数是:9,6
方法执行后结果是:15
加法计算结果:15
方法执行前参数是:9,6
方法执行后结果是:3
减法计算结果:3

1.4 存在的问题

代码混乱:越来越多的非业务需求(日志和验证等)加入后原有的业务方法急剧膨胀 每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点

代码分散以日志需求为例只是为了满足这个单一需求就不得不在多个模块(方法)里多次重复相同的日志代码如果日志需求发生变化必须修改所有模块。

 

2、通过动态代理实现日志

代理设计模式的原理使用一个代理将对象包装起来然后用该代理对象取代原始对象任何对原始对象的调用都要通过代理代理对象决定是否以及何时将方法调用转到原始对象上。这种方式可以解决上述存在的问题:

接口还是上面的,不用改变,现在我们使用动态代理的方式来实现,代码如下:

2.1 创建接口实现

创建接口的实现类,实现类中只做业务相关的事情,日志的事情不需要管:

package com.spring.aop;

public class MathCalculatorImpl implements MathCalculator {
    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public int sub(int a, int b) {
        return a-b;
    }
}

2.2 创建代理类

创代理类,代理类持有被代理对象的引用

package com.spring.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class MathCalculatorProxyImpl {
    //持有被代理对象的引用
    private MathCalculatorImpl target;

    //通过构造方法传入被代理对象
    public MathCalculatorProxyImpl(MathCalculatorImpl target) {
        this.target = target;
    }

    //返回代理对象
    public MathCalculator getLoggingProxy(){
        MathCalculator proxy = null;
        ClassLoader classLoader = target.getClass().getClassLoader();//类加载器
        Class[] interfaces = new Class[]{MathCalculator.class};//被代理对象实现的接口的数组
        //处理器对象,进行日志操作,被代理对象的所有方法执行都要先经过该类的invoke方法
        InvocationHandler handler = new InvocationHandler() {
            /**
             * proxy: 代理对象。 一般不使用该对象
             * method: 正在被调用的方法
             * args: 调用方法传入的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String methodName = method.getName();
                //打印日志
                System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));

                //调用目标方法
                Object result = null;//目录方法返回值
                try {
                    //前置通知
                    result = method.invoke(target, args);
                    //返回通知, 可以访问到方法的返回值
                } catch (NullPointerException e) {
                    e.printStackTrace();
                    //异常通知, 可以访问到方法出现的异常
                }
                //后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值
                //打印日志
                System.out.println("[after] The method ends with " + result);
                return result;
            }
        };
        /**
         * classLoader: 代理对象使用的类加载器。
         * interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法.
         * handler: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
         */
        proxy = (MathCalculator) Proxy.newProxyInstance(classLoader,interfaces,handler);
        return proxy;
    }
}

2.3 测试

package com.spring.aop;

public class MathCalculatorProxyTest {
    public static void main(String[] args) {
        //创建被代理对象
        MathCalculatorImpl mathCalculatorImpl = new MathCalculatorImpl();
        //创建代理对象
        MathCalculatorProxyImpl mathCalculatorProxy = new MathCalculatorProxyImpl(mathCalculatorImpl);
        MathCalculator proxy = mathCalculatorProxy.getLoggingProxy();
        int add = proxy.add(9, 6);
        System.out.println("加法计算结果:"+add);
        int sub = proxy.sub(9, 6);
        System.out.println("减法计算结果:"+sub);
    }
}

打印结果:

[before] The method add begins with [9, 6]
[after] The method ends with 15
加法计算结果:15
[before] The method sub begins with [9, 6]
[after] The method ends with 3
减法计算结果:3

2.4 总结

动态代理的方式可以实现日志操作,这样实现起来很统一,就算目标类中加了其他方法,不需要任何操作,就能实现日志功能。但是如果每次这样手动实现,也会比较麻烦,所以Spring给我们提供了AOP模块。

 

3、AOP

3.1 AOP简介

AOP(Aspect-Oriented Programming, 面向切面编程 ): 是一种新的方法论 是对传统 OOP(Object-Oriented Programming, 面向对象编程 ) 的补充
AOP 的主要编程对象是 切面 (aspect), 切面模块化横切关注点
在应用 AOP 编程时 仍然 需要 定义 公共功能 但可以明确的定义这个功能在哪里 以什么方式应用 并且不必修改受影响的类 这样一来 横切关注点就被模块化到特殊的对象 ( 切面 )
AOP 的好处
每个事物逻辑位于一个位置 代码不分散 便于维护和升级。

                 业务模块更简洁,只包含核心业务代码。

3.2 AOP术语

切面 (Aspect):  横切关注点 ( 跨越应用程序多个模块的功能 ) 被模块化的特殊对象
通知(Advice):   切面必须要完成的工作
目标(Target):    被通知的对象
代理(Proxy):     向目标对象应用通知之后创建的对象
连接点( J oinpoint):  程序 执行的某个特定位置 :如类某个方法调用前、调用后、方法抛出异常后等 连接点 由两个信息                                        确定:方法表示的程序执行点;相对点表示的方位 。例如 ArithmethicCalculator#add () 方法执行 前的                                  连接点,执行点为 ArithmethicCalculator#add () ; 方位为该方法执行前的 位置
切点( pointcut ): 每个 类都拥有多个连接点 :例如 ArithmethicCalculator 的所有方法实际上都是连接点,即 连接点是程序                                  类中 客观存在 的事务 AOP 通过切点定位到特定的连接点 。类比 :连接点相当于数据库中的记录,切                                     点相当于查询条件 。切点和连接点不是一对一的关系,一个切点匹配多个 连接点, 切点通过                                                     org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的 查询条件

 

3.3 AOP的实现AspectJ使用

AspectJ Java 社区里最完整最流行的 AOP 框架 .
Spring2.0 以上版本 可以 使用基于 AspectJ 注解或基于 XML 配置的 AOP。
 
AspectJ需要的jar包: com.springsource.org.aopalliance-1.0.0.jar    com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar   spring-aop-4.0.0.RELEASE.jar    spring-aspects-4.0.0.RELEASE.jar

3.3.1 注解配置使用AspectJ实现AOP

首先要把jar包导入到工程中

1、创建接口和实现类,并在实现类加上Spring注解

package com.spring.aspectjAop.annotation;

public interface MathCalculator {
    public int add(int a, int b);
    public int sub(int a, int b);
}

package com.spring.aspectjAop.annotation;

import org.springframework.stereotype.Component;

@Component("mathCalculator")
public class MathCalculatorImpl implements MathCalculator {
    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public int sub(int a, int b) {
        return a-b;
    }
}

2、创建AspectJ代理类

package com.spring.aspectjAop.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

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

@Component//首先切面也是spring的一个Bean
@Aspect//表明这是一个AspectJ切面
public class LoggingAspectJAop {

    /**
     *     标识这个方法是个前置通知,  切点表达式表示执行任意类的任意方法. 第
     *     一个 * 代表匹配任意修饰符及任意返回值,  第二个 * 代表任意类的对象,
     *     第三个 * 代表任意方法, 参数列表中的 ..  匹配任意数量的参数
     */
    @Before("execution(* com.spring.aspectjAop.annotation.*.*(..))")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("执行的方法是:"+methodName+",参数是:"+args);
    }
}

3、配置文件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-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!--配置包扫描-->
    <context:component-scan base-package="com.spring.aspectjAop.annotation"/>

    <!--开启AspectJ的注解-->
    <aop:aspectj-autoproxy/>

</beans>

4、测试

package com.spring.aspectjAop.annotation;

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

public class MainTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        MathCalculator mathCalculator = (MathCalculator) applicationContext.getBean("mathCalculator");
        int add = mathCalculator.add(9, 3);
        System.out.println("加法结果:"+add);
        int sub = mathCalculator.sub(9, 3);
        System.out.println("减法结果:"+sub);
    }
}

打印结果:

执行的方法是:add,参数是:[9, 3]
加法结果:12
执行的方法是:sub,参数是:[9, 3]
减法结果:6

4、总结

1、要使用AspectJ,就要先导入相关的jar包;

2、applicationContext.xml文件中,要加入aop名称空间,启用 AspectJ 注解支持, 只要Bean 配置文件中定义一个空的 XML 元素 <aop:aspectj-autoproxy>;Spring IOC 容器侦测到 Bean 配置文件中的 <aop:aspectj-autoproxy> 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理。

3、切面类要使用注解@Components和@Aspect

4、定义方法,方法上使用注解表明这个方法的执行时机,有如下注解:

@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行(无法是否发生异常)
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行

5、切面方法的注解中,要编写AspectJ表达式,告诉该方法要监听哪些方法;

6、如果要拿到目标方法的方法名称和参数,就可以在方法形参中直接写上:JoinPoint joinPoint

5、方法签名编写 AspectJ 切入点表达式

最典型的切入点表达式时根据方法的签名来匹配各种方法 :
execution * com.atguigu.spring.ArithmeticCalculator . * (..): 匹配 ArithmeticCalculator 中声明的所有方法 , 第一个 * 代表任意修饰符及任意返回值 . 第二个 * 代表任意方法 . .. 匹配任意数量的参数 . 若目标类与接口与该切面在同一个包中 , 可以省略包名 .
execution public * ArithmeticCalculator.*(..): 匹配 ArithmeticCalculator 接口的 所有公有方法 .
execution public double ArithmeticCalculator.*(..): 匹配 ArithmeticCalculator 返回 double 类型数值的方法
execution public double ArithmeticCalculator.*( double , ..): 匹配第一个参数为 double 类型的方法 , .. 匹配任意数量任意类型的参数

                  execution public double ArithmeticCalculator.*(double, double): 匹配参数类型为 double, double 类型的方法

AspectJ 切入点表达式可以通过操作符 &&,||, ! 结合起来:

6、后置通知

在刚才的LoggingAspectJAop加上后置通知 @After:

 /**
     * 后置通知是在连接点完成之后执行的, 即连接点返回结果或者抛出异常的时候, 下面的后置通知记录了方法的终止
     * @param joinPoint
     */
    @After("execution(* com.spring.aspectjAop.annotation.*.*(..))")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println(methodName+"执行完了");
    }

7、返回通知

无论连接点是正常返回还是抛出异常后置通知都会执行如果只想在连接点返回的时候(方法正常结束)记录日志应使用返回通知代替后置通知。返回通知可以访问方法的返回值。

返回通知中, 只要将 returning 属性添加到 @AfterReturning 注解中就可以访问连接点的返回值,该属性的值即为用来传入返回值的参数名称;必须在通知方法的签名中添加一个同名参数在运行时,Spring AOP 会通过这个参数传递返回值;原始切点表达式需要出现在 pointcut 属性中

在刚才的LoggingAspectJAop加上返回通知 @AfterReturning:

@AfterReturning(value = "execution(* com.spring.aspectjAop.annotation.*.*(..))",returning = "result")
    public void afterRunningMethod(JoinPoint joinPoint, Object result){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println(methodName+"方法的返回值是:"+result);
    }

8、异常通知

只在连接点抛出异常时才执行异常通知;

将throwing 属性添加到 @AfterThrowing 注解中也可以访问连接点抛出的异常Throwable 是所有错误和异常类的超类所以在异常通知方法可以捕获到任何错误和异常。

如果只对某种特殊的异常类型感兴趣可以将参数声明为其他异常的参数类型然后通知就只在抛出这个类型及其子类的异常时才被执行。

/**
     * 异常通知
     */
    @AfterThrowing(value = "execution(* com.spring.aspectjAop.annotation.*.*(..))",throwing = "exception")
    public void afterThrowableMethod(JoinPoint joinPoint, Exception exception){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println(methodName+"方法的出现了异常,异常信息是:"+exception.getMessage());
    }
 

9、环绕通知

环绕通知是所有通知类型中功能最为强大的能够全面地控制连接点甚至可以控制是否执行连接点;

对于环绕通知来说连接点的参数类型必须是 ProceedingJoinPoint它是 JoinPoint 的子接口允许控制何时执行是否执行连接点;

在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法, 如果忘记这样做就会导致通知被执行了,但目标方法没有被执行

注意:环绕通知的方法 需要返回目标方法执行之后的结果即调用 joinPoint.proceed(); 的返回值否则会出现空指针异常

/**
     * 环绕通知需要携带 ProceedingJoinPoint 类型的参数. 
     * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
     * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
     */
    @Around(value = "execution(* com.spring.aspectjAop.annotation.*.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjd){
        Object result = null;//定义返回值
        String methodName = pjd.getSignature().getName();
        List<Object> args = Arrays.asList(pjd.getArgs());
        try {
            //前置通知
            System.out.println(methodName+"方法执行之前,参数是:"+args);
            result = pjd.proceed();
            //后置通知
            System.out.println(methodName+"方法执行完成,结果是:"+result);
        } catch (Throwable throwable) {
            //异常通知
            System.out.println(methodName+"方法执行出现了异常,异常信息:"+throwable.getMessage());
            throw  new RuntimeException(throwable);
        }
        //返回通知
        System.out.println(methodName+"执行没有发生异常,一切正常返回,返回结果是:"+result);
        return result;
    }

10、切面的优先级

在同一个连接点上应用不止一个切面时 除非明确指定 否则它们的优先级是不确定的
切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定
实现 Ordered 接口 getOrder () 方法的返回值越小 优先级越高
若使用 @Order 注解 序号出现在注解中。

11、重(chong)用切入点定义

1、在编写AspectJ 切面时可以直接在通知注解中书写切入点表达式但同一个切点表达式可能会在多个通知中重复出现;

2、在AspectJ 切面中可以通过 @Pointcut 注解将一个切入点声明成简单的方法切入点的方法体通常是空的因为将切入点定义与应用程序逻辑混在一起是不合理的;

3、切入点方法的访问控制符同时也控制着这个切入点的可见性如果切入点要在多个切面中共用最好将它们集中在一个公共的类中在这种情况下它们必须被声明为 public;在引入这个切入点时必须将类名也包括在内如果类没有与这个切面放在同一个包中还必须包含包名;

4、其他通知可以通过方法名称引入该切入点。

切面的完整代码:

package com.spring.aspectjAop.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

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

@Order(0)//可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
@Component//首先切面也是spring的一个Bean
@Aspect//表明这是一个AspectJ切面
public class LoggingAspectJAop {

    /**
     * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
     * 使用 @Pointcut 来声明切入点表达式.
     * 后面的其他通知直接使用方法名来引用当前的切入点表达式.
     */
    @Pointcut("execution(* com.spring.aspectjAop.annotation.*.*(..))")
    public void JoinPointCut(){}

    /**
     *     标识这个方法是个前置通知,  切点表达式表示执行任意类的任意方法. 第
     *     一个 * 代表匹配任意修饰符及任意返回值,  第二个 * 代表任意类的对象,
     *     第三个 * 代表任意方法, 参数列表中的 ..  匹配任意数量的参数
     */
    @Before("JoinPointCut()")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("执行的方法是:"+methodName+",参数是:"+args);
    }

    /**
     * 后置通知是在连接点完成之后执行的, 即连接点返回结果或者抛出异常的时候, 下面的后置通知记录了方法的终止
     * @param joinPoint
     */
    @After("JoinPointCut()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println(methodName+"执行完了");
    }

    @AfterReturning(pointcut = "JoinPointCut()",returning = "result")
    public void afterRunningMethod(JoinPoint joinPoint, Object result){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println(methodName+"方法的返回值是:"+result);
    }

    /**
     * 异常通知
     */
    @AfterThrowing(pointcut = "JoinPointCut()",throwing = "exception")
    public void afterThrowableMethod(JoinPoint joinPoint, Exception exception){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println(methodName+"方法的出现了异常,异常信息是:"+exception.getMessage());
    }

    /**
     * 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
     * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
     * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
     */
    @Around(value = "JoinPointCut()")
    public Object aroundMethod(ProceedingJoinPoint pjd){
        Object result = null;//定义返回值
        String methodName = pjd.getSignature().getName();
        List<Object> args = Arrays.asList(pjd.getArgs());
        try {
            //前置通知
            System.out.println(methodName+"方法执行之前,参数是:"+args);
            result = pjd.proceed();
            //后置通知
            System.out.println(methodName+"方法执行完成,结果是:"+result);
        } catch (Throwable throwable) {
            //异常通知
            System.out.println(methodName+"方法执行出现了异常,异常信息:"+throwable.getMessage());
            throw  new RuntimeException(throwable);
        }
        //返回通知
        System.out.println(methodName+"执行没有发生异常,一切正常返回,返回结果是:"+result);
        return result;
    }
}

 

12、引入通知

       引入通知是一种特殊的通知类型. 它通过为接口提供实现类, 允许对象动态地实现接口, 就像对象已经在运行时扩展了实现类一样。

 

引入通知可以使用两个实现类 MaxCalculatorImpl MinCalculatorImpl , ArithmeticCalculatorImpl 动态地实现 MaxCalculator MinCalculator 接口 . 而这与从 MaxCalculatorImpl MinCalculatorImpl 中实现多继承的效果相同 . 但却不需要修改 ArithmeticCalculatorImpl 的源代码
引入通知也必须在切面中声明
在切面中 , 通过为 任意字段 添加 @ DeclareParents 注解来引入声明 .
注解类型的 value 属性表示哪些类是当前引入通知的目标 . value 属性值也可以是一个 AspectJ 类型的表达式 , 以将一个即可引入到多个类中 defaultImpl 属性中指定这个接口使用的实现类

引入通知示例代码:

3.3.2 基于xml配置文件使用AspectJ实现AOP

1、业务接口和业务实现类

package com.spring.aspectAop.xml;

public interface MathCalculator {
    public int add(int a, int b);
    public int sub(int a, int b);
}


package com.spring.aspectAop.xml;

import org.springframework.stereotype.Component;

public class MathCalculatorImpl implements MathCalculator {
    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public int sub(int a, int b) {
        return a-b;
    }
}

2、切面类

package com.spring.aspectAop.xml;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

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

public class LoggingAspectJAop {

    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("执行的方法是:"+methodName+",参数是:"+args);
    }

    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println(methodName+"执行完了");
    }

    public void afterRunningMethod(JoinPoint joinPoint, Object result){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println(methodName+"方法的返回值是:"+result);
    }

    public void afterThrowableMethod(JoinPoint joinPoint, Exception exception){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println(methodName+"方法的出现了异常,异常信息是:"+exception.getMessage());
    }

    public Object aroundMethod(ProceedingJoinPoint pjd){
        Object result = null;//定义返回值
        String methodName = pjd.getSignature().getName();
        List<Object> args = Arrays.asList(pjd.getArgs());
        try {
            //前置通知
            System.out.println(methodName+"方法执行之前,参数是:"+args);
            result = pjd.proceed();
            //后置通知
            System.out.println(methodName+"方法执行完成,结果是:"+result);
        } catch (Throwable throwable) {
            //异常通知
            System.out.println(methodName+"方法执行出现了异常,异常信息:"+throwable.getMessage());
            throw  new RuntimeException(throwable);
        }
        //返回通知
        System.out.println(methodName+"执行没有发生异常,一切正常返回,返回结果是:"+result);
        return result;
    }
}

3、配置文件

<?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-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!--配置业务类Bean-->
    <bean id="mathCalculator" class="com.spring.aspectAop.xml.MathCalculatorImpl"/>

    <!--配置切面的 bean-->
    <bean id="loggingAspectJAop" class="com.spring.aspectAop.xml.LoggingAspectJAop"/>

    <!--配置AOP-->
    <aop:config>
        <!--配置切点表达式-->
        <aop:pointcut id="joinPointCut" expression="execution(* com.spring.aspectAop.xml.*.*(..))"/>

        <!--配置切面及通知-->
        <aop:aspect ref="loggingAspectJAop" order="0">
            <!--前置通知-->
            <aop:before method="beforeMethod" pointcut-ref="joinPointCut"/>
            <!--后置通知-->
            <aop:after method="afterMethod" pointcut-ref="joinPointCut"/>
            <!--异常通知-->
            <aop:after-throwing method="afterThrowableMethod" pointcut-ref="joinPointCut" throwing="exception"/>
            <!--返回通知-->
            <aop:after-returning method="afterRunningMethod" pointcut-ref="joinPointCut" returning="result"/>
            <!--  环绕通知
            <aop:around method="aroundMethod" pointcut-ref="joinPointCut"/>
            -->
        </aop:aspect>
    </aop:config>
</beans>

4、测试

package com.spring.aspectAop.xml;

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

public class MainTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/applicationContext-xml.xml");
        MathCalculator mathCalculator = (MathCalculator) applicationContext.getBean("mathCalculator");
        int add = mathCalculator.add(9, 3);
        System.out.println("加法结果:"+add);
        int sub = mathCalculator.sub(9, 3);
        System.out.println("减法结果:"+sub);
    }
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值