12 – Spring AOP介绍与使用3 – AOP的xml配置文件方式

之前,aop是通过注解的方式,在resources文件夹下定义了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: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命名空间-->
    <!--然后又添加了aop命名空间-->

    <!--开启包的扫描-->
    <context:component-scan base-package="com.zhoulz"></context:component-scan>

    <!--开启aop的注解功能。见上面:先像context命名空间那样加后,再开启:-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <!--注意:如果这里没有开启注解功能,那么LogUtil中的@Before、@After都是无效的-->

</beans>

下面介绍aop的xml配置文件的方式:

同样在resources文件夹下定义了aop.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
">
    <!--aop命名空间的添加,见上-->


    <!--
        之前,aop是通过注解的方式,下面介绍aop的xml配置文件的方式
     -->

    <!--1、首先保证切面被扫描到-->
    <bean id="logUtil" class="com.zhoulz.util.LogUtil" ></bean>
    <bean id="securityUtil" class="com.zhoulz.util.SecurityUtil"></bean>

    <!--2、然后指定好 往哪个地方去设置这些日志/安全-->
    <!--之前是往service下的MyCalculator中设置的,所以:(注意:先把之前aop的注解给注释掉,方便对比)-->
    <bean id="myCalculator" class="com.zhoulz.service.MyCalculator"></bean>

    <!--接着,对应之前aop注解的方式,在配置文件中应该对应进行如下配置:-->
    <!--3、配置aop-->
    <aop:config>
        <!--保险起见,或者说最好将通用表达式的抽象提取到两个切面的外部,作为公共的来用-->
        <aop:pointcut id="globalPoint" expression="execution(public Integer com.zhoulz.service.MyCalculator.*(..))"/>

        <!--3.1、声明切面-->
        <aop:aspect ref="logUtil">
            <!--发现,两个切面有通用的表达式,现在给抽取出来:(同LogUtil中表达式的抽象-创建空方法的方式)-->
            <!--同时注意:这里把抽象定义在 aop:aspect ref="logUtil" 这个切面里,则表示该抽象在当前切面内可以使用。但是在 aop:aspect ref="securityUtil" 这个切面里也能用??(可以用)-->
            <aop:pointcut id="myPoint" expression="execution(public Integer com.zhoulz.service.MyCalculator.*(..))"/>

            <!--3.2、通知定义在哪些方法上使用-->
            <!--<aop:before method="start" pointcut="execution(public Integer com.zhoulz.service.MyCalculator.*(..))"></aop:before>-->
            <!--上面进行抽象后,然后,后面的表达式就都可以简写了:记住把 pointcut 改成pointcut-ref-->
            <aop:before method="start" pointcut-ref="myPoint"></aop:before>

            <aop:after method="logFinally" pointcut-ref="myPoint"></aop:after>
            <aop:after-returning method="stop" pointcut-ref="myPoint" returning="result"></aop:after-returning>
            <aop:after-throwing method="logException" pointcut-ref="myPoint" throwing="e"></aop:after-throwing>
            <aop:around method="around" pointcut-ref="myPoint"></aop:around>
        </aop:aspect>

        <!--然后,同理,声明另一个切面:SecurityUtil-->
        <aop:aspect ref="securityUtil">
            <aop:before method="start" pointcut-ref="myPoint"></aop:before>
            <aop:after method="logFinally" pointcut-ref="myPoint"></aop:after>
            <aop:after-returning method="stop" pointcut-ref="myPoint" returning="result"></aop:after-returning>
            <aop:after-throwing method="logException" pointcut-ref="globalPoint" throwing="e"></aop:after-throwing>
            <aop:around method="around" pointcut-ref="globalPoint"></aop:around>
        </aop:aspect>

    </aop:config>

</beans>

代码示例: —— 注意,相关注解已取消

参考:11 - Spring AOP介绍与使用2 - 简单配置_哆啦A梦的_梦的博客-CSDN博客

基本相同。

Calculator接口:

package com.zhoulz.service;

import org.springframework.stereotype.Service;
//@Service
public interface Calculator {

    public Integer add(Integer i,Integer j) throws NoSuchMethodException;
    public Integer sub(Integer i,Integer j) throws NoSuchMethodException;
    public Integer mul(Integer i,Integer j) throws NoSuchMethodException;
    public Integer div(Integer i,Integer j) throws NoSuchMethodException;
}

MyCalculator 类:

package com.zhoulz.service;

import com.zhoulz.util.LogUtil;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;

//@Service
public class MyCalculator /*implements Calculator */{

    public Integer add(Integer i, Integer j) throws NoSuchMethodException {
        //现在,想添加日志的输出功能,怎么做?
        //最简单的做法:在每行代码里面都做一个最基本的输出 —— 但是这样做效率太低了
        //System.out.prIntegerln("add方法开始执行,参数是:" + i + "----" + j );
        //改进: —— 定义一个LogUtil类(包含start()方法和stop()方法)

        //通过反射
        /*Method add = MyCalculator.class.getMethod("add", Integer.class, Integer.class);
        //然后add传入到下面的方法中 —— 下面的都同理
        LogUtil.start(add,i,j);*/
        Integer result = i + j;
        //System.out.prIntegerln("add方法执行结束,结果是:" + result);
        //LogUtil.stop(add,result);
        return result;
    }

    public Integer sub(Integer i, Integer j) throws NoSuchMethodException {
        //System.out.prIntegerln("sub方法开始执行,参数是:" + i + "----" + j );
        /*Method sub = MyCalculator.class.getMethod("sub", Integer.class, Integer.class);
        LogUtil.start(sub,i,j);*/
        Integer result = i - j;
        //System.out.prIntegerln("sub方法执行结束,结果是:" + result);
        //LogUtil.stop(sub,result);
        return result;
    }

    public Integer mul(Integer i, Integer j) throws NoSuchMethodException {
        //System.out.prIntegerln("mul方法开始执行,参数是:" + i + "----" + j );
        //Method mul = MyCalculator.class.getMethod("mul", Integer.class, Integer.class);
        //LogUtil.start(mul,i,j);
        Integer result = i * j;
        //System.out.prIntegerln("mul方法执行结束,结果是:" + result);
        //LogUtil.stop(mul,result);
        return result;
    }

    public Integer div(Integer i, Integer j) throws NoSuchMethodException {
        //System.out.prIntegerln("div方法开始执行,参数是:" + i + "----" + j );
        //Method div = MyCalculator.class.getMethod("div", Integer.class, Integer.class);
        //LogUtil.start(div,i,j);
        Integer result = i / j;
        //System.out.prIntegerln("div方法执行结束,结果是:" + result);
        //LogUtil.stop(div,result);
        return result;
    }

    //再加一个方法
    public Integer show(Integer i, Double j){
        System.out.println("show ........");
        return i;
    }

}

util文件夹下,两个切面类:LogUtil、SecurityUtil

package com.zhoulz.util;

import com.zhoulz.service.Calculator;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

import java.util.Arrays;

//@Aspect
//@Component
//@Order(200)
public class LogUtil {

    //如果有多个匹配的表达式相同,能否做抽象?可以
    //  : 定义一个没有返回值的空方法,给该方法添加@Pointcut注解,后续在使用(匹配表达式)的时候直接调用该方法名称
    //    此处的方法只是起一个声明的作用,能够供内部的其他通知方法进行调用
    @Pointcut("execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))")
    public void myPointCut(){} // —— 然后后面用的时候直接写:myPointCut() 即可
    //还可以定义多个
    @Pointcut("execution(* *(..))")
    public void myPointCut2(){}


    @Before("execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,*))")
    //public static void start(Method method,Object ... args){    //原来的写法       //方法里面暂时不用传入参数
    public static void start(JoinPoint joinPoint){
        //获取方法签名
        Signature signature = joinPoint.getSignature();
        //获取方法参数信息
        Object[] args = joinPoint.getArgs();
        //获取方法名称
        //System.out.println(signature.getName());
        System.out.println("Log----"+signature.getName()+"方法开始执行,参数是:" +Arrays.asList(args));
    }

    //@AfterReturning(value = "execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))",returning = "result")
    //匹配表达式进行了抽象,见上面的 myPointCut()方法
    @AfterReturning(value = "myPointCut()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){ // —— 怎么加结果result?:上面的通知注解要改动
        Signature signature = joinPoint.getSignature();
        System.out.println("Log----"+signature.getName()+"方法执行结束,结果是:" +result);
    }

    @AfterThrowing(value = "myPointCut()",throwing = "e")
    public static void logException(JoinPoint joinPoint,Exception e){
        Signature signature = joinPoint.getSignature();
        System.out.println("Log----"+signature.getName()+"方法抛出异常:" + e.getMessage());
    }

    @After("myPointCut2()")
    public static void logFinally(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        System.out.println("Log----"+signature.getName()+"方法执行结束。。。。over");
    }

    //环绕通知 —— 把上面4个通知任意拿过来执行
    @Around("myPointCut2()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable { //需要传参
        Signature signature = pjp.getSignature();
        Object[] args = pjp.getArgs();

        Object result = null;
        try {
            System.out.println("Log----"+"环绕通知start:"+signature.getName()+"方法开始执行,参数为:"+ Arrays.asList(args));
            //通过反射的方式调用目标的方法,相当于执行method.invoke(),可以自己修改结果值
            result = pjp.proceed(args);
            //result = 100;
            System.out.println("Log----"+"环绕通知stop:"+signature.getName()+"方法执行结束了");
        } catch (Throwable throwable) {
            //throwable.printStackTrace();
            System.out.println("Log----"+"环绕异常通知:"+signature.getName()+"出现异常了");
            throw throwable;
        }finally {
            System.out.println("Log----"+"环绕返回通知:"+signature.getName()+"方法返回结果是:"+result);
        }
        return result;
    }
}
package com.zhoulz.util;

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

import java.util.Arrays;

/*@Aspect
@Component
@Order(100)*/
public class SecurityUtil {
    //如果有多个匹配的表达式相同,能否做抽象?可以
    //  : 定义一个没有返回值的空方法,给该方法添加@Pointcut注解,后续在使用(匹配表达式)的时候直接调用该方法名称
    //    此处的方法只是起一个声明的作用,能够供内部的其他通知方法进行调用
    @Pointcut("execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))")
    public void myPointCut(){} // —— 然后后面用的时候直接写:myPointCut() 即可
    //还可以定义多个
    @Pointcut("execution(* *(..))")
    public void myPointCut2(){}


    @Before("execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,*))")
    //public static void start(Method method,Object ... args){    //原来的写法       //方法里面暂时不用传入参数
    public static void start(JoinPoint joinPoint){
        //获取方法签名
        Signature signature = joinPoint.getSignature();
        //获取方法参数信息
        Object[] args = joinPoint.getArgs();
        //获取方法名称
        //System.out.println(signature.getName());
        System.out.println("Security----"+signature.getName()+"方法开始执行,参数是:" +Arrays.asList(args));
    }

    //@AfterReturning(value = "execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,*))",returning = "result")
    //匹配表达式进行了抽象,见上面的 myPointCut()方法
    @AfterReturning(value = "myPointCut()",returning = "result")
    public static void stop(JoinPoint joinPoint, Object result){ // —— 怎么加结果result?:上面的通知注解要改动
        Signature signature = joinPoint.getSignature();
        System.out.println("Security----"+signature.getName()+"方法执行结束,结果是:" +result);
    }

    @AfterThrowing(value = "myPointCut()",throwing = "e")
    public static void logException(JoinPoint joinPoint,Exception e){
        Signature signature = joinPoint.getSignature();
        System.out.println("Security----"+signature.getName()+"方法抛出异常:" + e.getMessage());
    }

    @After("myPointCut2()")
    public static void logFinally(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        System.out.println("Security----"+signature.getName()+"方法执行结束。。。。over");
    }

    //环绕通知 —— 把上面4个通知任意拿过来执行
    @Around("myPointCut2()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable { //需要传参
        Signature signature = pjp.getSignature();
        Object[] args = pjp.getArgs();

        Object result = null;
        try {
            System.out.println("Security----"+"环绕通知start:"+signature.getName()+"方法开始执行,参数为:"+ Arrays.asList(args));
            //通过反射的方式调用目标的方法,相当于执行method.invoke(),可以自己修改结果值
            result = pjp.proceed(args);
            //result = 100;
            System.out.println("Security----"+"环绕通知stop:"+signature.getName()+"方法执行结束了");
        } catch (Throwable throwable) {
            //throwable.printStackTrace();
            System.out.println("Security----"+"环绕异常通知:"+signature.getName()+"出现异常了");
            throw throwable;
        }finally {
            System.out.println("Security----"+"环绕返回通知:"+signature.getName()+"方法返回结果是:"+result);
        }
        return result;
    }
}

测试类:—— 注意:切换到 aop.xml (原来是 applicationContext.xml )

import com.zhoulz.service.Calculator;
import com.zhoulz.service.MyCalculator;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    //ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //采用xml配置文件的方式,一定要注意切换到aop.xml
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");

    @Test
    public void test01() throws NoSuchMethodException {
        //ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //Calculator calculator = context.getBean("myCalculator", Calculator.class);
        //或者直接写
        //Calculator calculator = context.getBean(Calculator.class);
        //取消MyCalculator对Calculator的继承,再测试 —— 结果正常(即没有接口实现的话,则用的是cglib进行动态代理)
        MyCalculator calculator = context.getBean(MyCalculator.class);
        calculator.add(10,2);
       // calculator.sub(10,2);
        calculator.div(10,0);
        calculator.show(10,2.5);
        //System.out.println(calculator.getClass()); //class com.sun.proxy.$Proxy24
        //取消继承后,结果为:class com.zhoulz.service.MyCalculator$$EnhancerBySpringCGLIB$$77df863d
    }

    @Test
    public void test02() throws NoSuchMethodException {
        MyCalculator calculator = context.getBean(MyCalculator.class);
        //calculator.add(10,2);
        calculator.div(10,2);
    }
}

结果:

Log----div方法开始执行,参数是:[10, 2]
Log----环绕通知start:div方法开始执行,参数为:[10, 2]
Security----div方法开始执行,参数是:[10, 2]
Security----环绕通知start:div方法开始执行,参数为:[10, 2]
Security----环绕通知stop:div方法执行结束了
Security----环绕返回通知:div方法返回结果是:5
Security----div方法执行结束,结果是:5
Security----div方法执行结束。。。。over
Log----环绕通知stop:div方法执行结束了
Log----环绕返回通知:div方法返回结果是:5
Log----div方法执行结束,结果是:5
Log----div方法执行结束。。。。over


版权声明:本文为zhoulizhengZ原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>