11 - Spring AOP介绍与使用2 - 简单配置

2、Spring AOP 的简单配置 

(1)添加pom依赖

 完整:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zhoulz</groupId>
    <artifactId>spring_aop_study</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--添加的maven依赖-->

        <!--添加的spring-context依赖-->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <!--添加junit的依赖-->
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!--添加的spring-aop依赖-->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <!--cglib依赖-->
        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

        <!--aspectj weaver依赖 —— 提供了一种功能更强的织入方式-->
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
            <!--<scope>runtime</scope>--> <!--这个一定要注释掉,不然@Aspect出不来-->
        </dependency>

        <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

    </dependencies>


</project>

(2)编写配置

首先进行pom依赖的添加,然后是xml配置文件的配置:applicatonContext.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>

</beans>

添加注解,见 LogUtil:

package com.zhoulz.util;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

import java.util.Arrays;

@Aspect
@Component
public class LogUtil {
    /**
     * 通知注解有以下几种类型:
     * @Before:前置通知,在方法执行之前完成
     * @After:后置通知,在方法执行之后执行
     * @AfterReturning:返回通知,在返回结果之后运行
     * @AfterThrowing:异常通知,出现异常的时候使用
     * @Around:环绕通知
     *
     * 注意:在方法 的参数列表中不要随便添加参数值,会有异常信息
     * */

    @Before("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
    public static void start(){ //方法里面暂时不用传入参数
        System.out.println("方法开始执行,参数是:" );
    }
    @AfterReturning("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
    public static void stop(){
        System.out.println("方法执行结束,结果是:" );
    }
    @AfterThrowing("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
    public static void logException(){
        System.out.println("方法抛出异常:");
    }
    @After("execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))")
    public static void logFinally(){
        System.out.println("方法执行结束。。。。over");
    }
}
/**
 * 通知注解有以下几种类型:
 * @Before:前置通知,在方法执行之前完成
 * @After:后置通知,在方法执行之后执行
 * @AfterReturning:返回通知,在返回结果之后运行
 * @AfterThrowing:异常通知,出现异常的时候使用
 * @Around:环绕通知
 *
 * 注意:在方法 的参数列表中不要随便添加参数值,会有异常信息
 *
 * 切入点表达式:
 *  最精确的匹配方式: —— 但是太死板了(别的方法就用不了了)
 *      execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))
 * 在实际的生产环境中,更多的时候是使用通配符的方式: “*”、“..”
 *   * :
 *      1、可以用来匹配一个或多个字符:
 *          execution(public Integer com.zhoulz.service.MyCalculator.*(Integer,Integer))
 *          execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,Integer))
 *      2、匹配任意类型的参数:
 *          execution(public Integer com.zhoulz.service.M*Calculator.*(Integer,*))
 *      3、* 在进行匹配的时候,只能匹配一层路径,不能匹配多层
 *          execution(public Integer com.zhoulz.service.*.*.M*Calculator*.*(Integer,*))
 *      4、* 不能够匹配访问修饰符,如果不确定访问修饰符是什么,可以直接省略不写
 *            execution(Integer com.zhoulz.service.M*Calculator.*(Integer,*)) —— 省略了public
 *      5、返回值可以使用*来代替,但是不能空着不写
 *           execution(* com.zhoulz.service.M*Calculator.*(Integer,*))
 *  .. :
 *      1、可以匹配多个参数,任意类型
 *          execution(public Integer com.zhoulz..M*Calculator*.*(..))
 *      2、可以匹配多层路径,
 *          execution(* com.zhoulz..M*Calculator.*(..))
 *
 *  最偷懒的方式:
 *           execution(* *(..))
 *
 *   如果表达式是以*开头,那么可以代替所有:
 *                      execution(* com.*(..)) —— 不可以
 *                      execution(* com..*(..)) —— 可以
 *                      execution(* com.*.*(..)) —— 可以
 *
 *  使用通配符的时候不是越简洁越好,更多的是要选择符合要求或者符合规则的匹配方式,
 *  此时就要求在定义标识符的时候必须要遵守项目规范
 *
 *  在使用 表达式的时候还支持逻辑运算符:
 *      && : 多个条件必须同时满足;
 *      || : 多个条件只要满足其中一个即可;
 *      !  : 取反,处理这种情况的其他都满足;
 *          如:execution(public Integer com.zhoulz.service.MyCalculator.*(..)) && execution(* *(..))
 *              !execution(public Integer com.zhoulz.service.MyCalculator.add(Integer,Integer))
 *
 * 通知的正常执行顺序:
 * 如果正常执行:@Before —> @After —> @AfterReturning
 * 如果异常结束:@Before —> @After —> @AfterThrowing
 *
 * 如果想要在方法中获取对应的参数或者方法参数等信息的时候,必须要使用JoinPoint对象,并且此参数必须是第一个
 *  getSignature()、getArgs()、signature.getName()
 *         如果方法中有返回值,那么必须要在注解中添加returning = "result",这个result必须要和参数列表中的参数名称保持一致
 *         如果需要添加异常信息,那么在注解中要添加throwing = "e",这个e必须要和参数列表中的参数名称保持一致
 *     如果想要添加其他参数,必须要添加args(参数列表),ArgName(参数列表)
 *
 *     通知方法(即下面的start、stop等方法)在定义的时候有没有什么特殊的要求?
 *          通知方法在定义的时候对访问修饰符、返回值类型都没有明确的要求,
 *          但是要注意,参数不能随便添加
 *
 *  环绕通知:
 *      环绕通知在执行的时候是优于普通通知的
 *      如果是正常结束,那么执行的顺序是:
 *               环绕前置通知— @Before—环绕后置通知—环绕返回通知—@After—@AfterReturning
 *      如果是异常结束,那么执行的顺序是:
 *              环绕前置通知— @Before—环绕异常通知—环绕返回通知—@After—@AfterReturning
 *              (注意:异常时,外部(即环绕通知外的通知)没有异常通知@AfterThrowing了,即,没有异常捕获了)
 *      即;如果出现异常的时候,在环绕通知中解决了,那么普通通知是接受不到异常的,
 *         如果想让普通通知接收到,需要进行抛出:在环绕通知中加上throw throwable
 *         然后执行顺序是:
 *              环绕前置通知— @Before—环绕异常通知—环绕返回通知—@After—@AfterThrowing
 *
 *  当应用程序中包含多个切面的时候,具体的执行顺序是怎样的?
 *      :按照切面类的首字母进行排序操作,按照字典序A->Z
 *       如果需要人为的规定顺序,可以在切面上添加@Order注解,同时可以添加具体的值
 *       值越小越优先 1 —> 2147483647
 * */

代码示例: 代码说明见上面

LogUti:

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.lang.reflect.Method;

import java.util.Arrays;

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

 再创建一个切面类:SecurityUtil

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

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 ——  注意,这里取消了对Calculator 接口 的继承(所以不是其实现类了)

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;
    }

}

测试类:

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");

    @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,0);
    }
}

结果:

Security----div方法开始执行,参数是:[10, 2]
Log----div方法开始执行,参数是:[10, 2]
Log----div方法执行结束。。。。over
Log----div方法执行结束,结果是:5
Security----div方法执行结束。。。。over
Security----div方法执行结束,结果是:5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值