Spring学习笔记AOP(三)

时间:2021-10-06 10:01:36

鲁春利的工作笔记,好记性不如烂笔头



基于XML配置方式声明切面


基于Schema的AOP从Spring2.0之后通过“aop”命名空间来定义切面、切入点及声明通知。

前面我们用到了<aop:aspectj-autoproxy/>标签,Spring在aop的命名空间里面还提供了其他的配置元素:

<aop:advisor>            定义一个AOP通知者<aop:after>              后置通知
<aop:after-returning>    返回通知
<aop:after-throwing>     异常通知
<aop:around>             环绕通知
<aop:aspect>             定义一个切面
<aop:before>             前置通知
<aop:config>             AOP*配置元素,其他元素都位于该元素之下
<aop:pointcut>           定义一个切点,可重用

接口

package com.invicme.apps.aop;/** *  * @author lucl *  * 数学计算接口类 * */public interface ArithmeticCalculate {    public int add (int i, int j);    public int div (int i, int j);    public String validateNum (String level, int i);}

实现类

package com.invicme.apps.aop.schema;import org.apache.log4j.Logger;import com.invicme.apps.aop.ArithmeticCalculate;/** *  * @author lucl *  * 数学计算实现类 * */public class ArithmeticCalculateImpl implements ArithmeticCalculate {        private static final Logger logger = Logger.getLogger(ArithmeticCalculateImpl.class);        private int i = 0;    private int j = 0;        public ArithmeticCalculateImpl () {        this(0, 0);    }        public ArithmeticCalculateImpl (int i, int j) {        this.i = i;        this.j = j;    }        @Override    public int add(int i, int j) {        logger.info("The method add was invoke with args [" + i + ", " + j + "]");        int sum = i + j;        logger.info("The method add ends with result [" + sum + "]");        return sum;    }    @Override    public int div(int i, int j) {        logger.info("The method div was invoke with args [" + i + ", " + j + "]");        int result = i / j;        logger.info("The method div ends with result [" + result + "]");        return result;    }    @Override    public String validateNum(String level, int i) {        logger.info("The method validateNum was invoke with args [" + level + ", " + i + "]");        String result = this.getMsg(i);        logger.info("The method validateNum ends with result [" + result + "]");        return result;    }    private String getMsg (int i) {        if (i > 0) {            return "正数";        }        return "负数";    }}

切面POJO

package com.invicme.apps.aop.schema;import java.util.Arrays;import java.util.List;import org.apache.log4j.Logger;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;/** *  * @author lucl *  */public class LogAdapter {    private static final Logger logger = Logger.getLogger(LogAdapter.class);         /**     * Pointcut     * 定义Pointcut,Pointcut的名称为poincutMethod(),此方法没有返回值和参数     * 该方法就是一个标识,不进行调用     */    public void poincutMethod () {        // ......    }        /**     * 前置通知,在目标方法执行之前被调用(JoinPoint参数不是必须的,传入是为了获取目标对象的相关属性)     */    public void beforeAdvice (JoinPoint joinPoint) {        // 目标对象        Object target = joinPoint.getTarget();        // 目标方法        String methodName = joinPoint.getSignature().getName();        // 方法参数        List<Object> asList = Arrays.asList(joinPoint.getArgs());        /**         * target.getClass().getName() : 获取的是全路径名(包名+类名)         * target.getClass().getSimpleName() : 获取类名         */        logger.info("[<aop:before>]" + target.getClass().getSimpleName() + "@" + methodName + " was invoke with args " + asList + ".");    }        /**     * 后置通知(在目标方法执行之后被执行,无论该方法是否抛出异常)     */    public void afterAdvice (JoinPoint joinPoint) {        // 目标对象        Object target = joinPoint.getTarget();        // 目标方法        String methodName = joinPoint.getSignature().getName();        logger.info("[<aop:after>]" + target.getClass().getSimpleName() + "@" + methodName + " ends.");    }        /**     * 返回通知(在方法正常执行后执行,若出现异常不会被执行)     * 返回通知可以获取到目标方法的返回值     */    public void afterReturningAdvice (JoinPoint joinPoint, Object result) {        // 目标对象        Object target = joinPoint.getTarget();        // 目标方法        String methodName = joinPoint.getSignature().getName();        logger.info("[<aop:after-returning>]" + target.getClass().getSimpleName() + "@" + methodName + " ends with result " + result + ".");    }        /**     * 异常通知(当目标方法出现异常时会被执行,可以访问到异常,也可以通过指定异常类型,     * 如Exception ex,也可以为NullPointerException ex则只有空指针异常才会被执行)     */    public void afterThrowingAdvice (JoinPoint joinPoint, Exception ex) {        // 目标对象        Object target = joinPoint.getTarget();        // 目标方法        String methodName = joinPoint.getSignature().getName();        logger.info("[<aop:after-throwing>]" + target.getClass().getSimpleName() + "@" + methodName + " occurs exception : " + ex + ".");    }        /**     * 环绕通知     * 说明:     *         环绕通知需要携带ProceedingJoinPoint类型的参数     *         环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型参数可以决定是否执行目标方法     *         环绕通知必须有返回值,且返回值为目标方法的返回值     */    public Object aroundAdvice (ProceedingJoinPoint joinPoint) {        // 目标对象        Object target = joinPoint.getTarget();        // 目标方法        String methodName = joinPoint.getSignature().getName();        // 参数        List<Object> asList = Arrays.asList(joinPoint.getArgs());        //         Object result = null;        // 执行目标方法        try {            // 前置通知            result = joinPoint.proceed();            // 返回通知        } catch (Throwable e) {            throw new RuntimeException(e);            // 异常通知            // 或者            // 直接throw出去(否则程序会执行到最后的return result,而result为null,目标方法可能需要类型转换,当试图将null转化为特定类型时,出错)        }        // 后置通知        logger.info("[<aop:around>]" + target.getClass().getSimpleName() + "@" + methodName + " was invoke with args " + asList + ", ends with " + result + " .");        // 注意这里的result实际上是目标方法的返回值,如果出现问题返回值不匹配会出现错误        return result;    }}

Spring配置文件spring-context-aop-schema.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/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd        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">    <!-- 业务类 -->    <bean id="calculate" class="com.invicme.apps.aop.schema.ArithmeticCalculateImpl" />        <!-- 切入的日志类 -->    <bean id="logAdapter" class="com.invicme.apps.aop.schema.LogAdapter" />        <!--         proxy-target-class             true : 表示声明的切面使用CGLib动态代理技术;            false : 默认值,使用JDK动态代理技术。        一个配置文件可以有多个<aop:config>,不同的<aop:config>采用不同的代理技术。    -->    <aop:config proxy-target-class="false">        <!-- 声明一个切点 -->        <aop:pointcut id="logPointcut" expression="execution(public * com.invicme.apps.aop.schema.ArithmeticCalculateImpl.*(..))"/>        <!-- 定义切面 -->        <aop:aspect id="logAspect" ref="logAdapter">            <!-- 在<aop:aspect>中也可以定义<aop:pointcut>,但建议在外层定义,在通知处引用 -->            <!--                 <aop:before>                    pointcut和pointcut-ref:二者选一,指定切入点;                    method:指定前置通知实现方法名,如果是多态需要加上参数类型,多个用“,”隔开,如beforeAdvice(java.lang.String);                    arg-names:指定通知实现方法的参数名字,多个用“,”分隔,可选;如arg-names="param"表示beforeAdvice(String param)。            -->            <aop:before method="beforeAdvice" pointcut-ref="logPointcut"/>            <aop:after method="afterAdvice" pointcut-ref="logPointcut"/>            <aop:after-returning method="afterReturningAdvice" pointcut-ref="logPointcut" returning="result"/>            <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="logPointcut" throwing="ex"/>            <aop:around method="aroundAdvice" pointcut-ref="logPointcut"/>        </aop:aspect>    </aop:config></beans>

测试类

package com.test.apps.spring.aop;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.invicme.apps.aop.ArithmeticCalculate;/** *  * @author lucl * */public class TestSpringAopBySchema {    @Test    public void testSpringAopBySchema() {        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-context-aop-schema.xml");        ArithmeticCalculate calculate = context.getBean("calculate", ArithmeticCalculate.class);        calculate.add(1, 2);        System.out.println("----------------------------------------------------------------");        try {            Thread.sleep(1 * 1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        calculate.div(1, 0);    }}

运行结果:

Spring学习笔记AOP(三)


本文出自 “闷葫芦的世界” 博客,请务必保留此出处http://luchunli.blog.51cto.com/2368057/1812343