• Spring基础-06


    AOP相关知识点

    MyMathCalculator.java:

    package com.atguigu.impl;
    
    import com.atguigu.inter.Calculator;
    import org.springframework.stereotype.Service;
    
    /**
     * @Title: MyMathCalculator
     * @Description:
     * @Author:
     * @Version: 1.0
     * @create 2020/6/7 19:43
     */
    @Service
    public class MyMathCalculator /*implements Calculator*/ {
    
        //@Override
        public int add(int i, int j) {
            //System.out.println("【add】方法开始了,它使用的参数是:【"+i+"】,【"+j+"】");
            //考虑方法的兼容性
            //LogUtils.logStart(i, j);
            int result = i + j;
    //        System.out.println("【add】方法运行完成,计算结果是:【"+result+"】");
            System.out.println("方法内部执行");
            return result;
        }
    
        //@Override
        public int sub(int i, int j) {
            int result = i - j;
            System.out.println("方法内部执行");
            return result;
        }
    
        //@Override
        public int mul(int i, int j) {
            int result = i * j;
            System.out.println("方法内部执行");
            return result;
        }
    
        //@Override
        public int div(int i, int j) {
            int result = i / j;
            System.out.println("方法内部执行");
            return result;
        }
    }

    Calculator.java:

    package com.atguigu.inter;
    
    /**
     * 接口不加载在容器中
     * 实际上可以加,加了也不创建对象,只要一看这个组件是一个接口,
     * 相当于告诉Spring,ioc容器中可能有这种类型的组件
     */
    public interface Calculator {
    
        public int add(int i, int j);
    
        public int sub(int i, int j);
    
        public int mul(int i, int j);
    
        public int div(int i, int j);
    }

    LogUtils.java:

    package com.atguigu.utils;
    
    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//告诉spring这个类是切面类
    @Component
    @Order(1)//使用Order改变切面顺序,数值越小,优先级越高
    public class LogUtils {
    
        /*public static void logStart(Method method, Object... args) {
            System.out.println("[" + method.getName() + "]方法开始执行,使用的参数列表" + Arrays.asList(args) );
        }
    
        public static void logReturn(Method method,Object result){
            System.out.println("[" + method.getName() + "]方法正常执行完成了,计算结果是" + result);
        }
    
        public static void logException(Method method, Exception e) {
            System.out.println("["+method.getName()+"]方法出现异常了,异常信息是:"+e.getCause()+",这个异常已经通知测试测试小组进行排查");
        }
    
        public static void logEnd(Method method) {
            System.out.println("["+method.getName()+"]方法最终结束");
        }*/
    
        /**
         * try{
         *
         * @Before method.invoke(obj, args);
         * @AfterReturning }catch(Exception e){
         * @AfterThrowing }finally{
         * @After }
         * 告诉Spring每个方法都什么时候运行
         * 通知注解:
         * @Before:在目标方法之前运行 ———— 前置通知
         * @After:在目标方法运行结束之后 ———— 后置通知
         * @AfterReturning:在目标方法正常返回之后 ———— 返回通知
         * @AfterThrowing:在目标方法抛出异常之后执行 ———— 异常通知
         * @Around:环绕 ———— 环绕通知
         * <p>
         * 抽取可重用的切入点表达式:
         * 1.随便声明一个没有实现的返回void的空方法
         * 2.给方法标注@Pointcut注解
         */
    
        @Pointcut("execution(public int com.atguigu.impl.MyMathCalculator.*(..))")
        public void hahaMyPoint() {
        }
    
    
        //想在执行目标方法之前运行:写切入点表达式
        //execution(访问权限符 返回值类型 方法签名)
    //    @Before("execution(public int com.atguigu.impl.MyMathCalculator.add(int, int))")
        //所有方法*
        @Before("hahaMyPoint()")
        public static void logStart(JoinPoint joinPoint) {
            //获取目标方法运行时使用的参数
            Object[] args = joinPoint.getArgs();
            //获取到方法签名
            Signature signature = joinPoint.getSignature();
            String name = signature.getName();
            System.out.println("[LogUtils-前置][" + name + "]方法开始执行,使用的参数列表" + Arrays.asList(args));
        }
    
        /**
         * 切入点表达式的写法:
         * 固定格式:execution(访问权限符 返回值类型 方法全类名(参数表))
         *
         * 通配符:
         *      * :
         *          1)匹配一个或多个字符
         *          execution(public int com.atguigu.impl.MyMath*r.*(int, int))
         *          2)匹配任意一个参数  第一个是int类型,第二个是任意类型 (匹配两个参数)
         *          execution(public int com.atguigu.impl.MyMath*.*(int, *))")
         *          3)只能匹配一层路径
         *          4)权限位置*不能表示任意权限,权限位置不写就表示任意权限
         *             public是可选的
         *      .. :
         *          1)匹配任意多个参数,任意类型参数
         *          execution(public int com.atguigu.impl.MyMathCalculator.*(..))
         *          2)匹配任意多层路径
         *          execution(public int com.atguigu..MyMath*.*(..))
         *
         *  记住两种:
         *      最精确的:execution(public int com.atguigu.impl.MyMathCalculator.add(int, int))
         *      最模糊的:execution(* *.*(..))    *开头表示任意层,不再只表示一层
         *              任意包下的任意类的任意方法
         *              不建议写
         *
         * “&&”、“||”、“!”
         *
         * &&:我们要切入的位置满足这两个表达式
         * execution(public int com.atguigu..MyMath*.*(..))&&execution(* *.*(int,int))
         *
         * ||:满足任意一个表达式即可
         * execution(public int com.atguigu..MyMath*.*(..))||execution(* *.*(int,int))
         *
         * !:只要不是这个位置都切入
         * !execution(public int com.atguigu..MyMath*.*(..))
         */
    
        /**
         * 我们可以在通知方法运行的时候,拿到目标方法的详细信息
         * 1)只需要为通知方法的参数列表上写一个参数
         * JoinPoint joinPoint:封装了当前目标方法的详细信息
         */
    
        //想在目标方法正常执行完成之后执行
        //告诉spring这个result用来接收返回值
        //returning:参数名是什么,这里就写什么
        @AfterReturning(value = "execution(public int com.atguigu.impl.MyMathCalculator.*(int, int))", returning = "result")
        public static void logReturn(JoinPoint joinPoint, Object result) {
            Signature signature = joinPoint.getSignature();
            String name = signature.getName();
            System.out.println("[LogUtils-返回][" + name + "]方法正常执行完成了,计算结果是" + result);
        }
    
        /**
         * 想在目标方法出现异常的时候执行
         * 告诉spring哪个参数是用来接收异常
         * throwing = "exception"
         * Exception exception:指定通知方法可以接收哪些异常,如果范围过小,即该异常不是目标异常,该方法不会被调用
         * <p>
         * <p>
         * 类似ajax接收服务器数据
         * $.post(url,function(abc){
         * alert(abc)
         * })
         */
        @AfterThrowing(value = "execution(public int com.atguigu.impl.MyMathCalculator.*(int, int))", throwing = "exception")
        public static void logException(JoinPoint joinPoint, Exception exception) {
            System.out.println("[LogUtils-异常][" + joinPoint.getSignature().getName() + "]方法出现异常了,异常信息是:[" + exception + "],这个异常已经通知测试测试小组进行排查");
        }
    
        //想在目标方法结束的时候执行
    
        /**
         * Spring对通知方法的要求不严格
         * 唯一要求的就是方法的参数列表一定不能乱写
         * 通知方法是Spring利用反射调用的,每次方法调用得确定这个方法的参数表的值
         * 参数表上的每一个参数,Spring都得知道是什么
         * JoinPoint:认识
         * 不知道的参数一定要告诉spring这是什么
         */
        @After("execution(public int com.atguigu.impl.MyMathCalculator.*(int, int))")
        public static int logEnd(JoinPoint joinPoint) {
            System.out.println("[LogUtils-后置][" + joinPoint.getSignature().getName() + "]方法最终结束");
            return 0;
        }
    
        /**
         * @Around:环绕 ———— 环绕通知:是Spring中最强大的通知
         * @Around:环绕:动态代理 try{
         * @Before 前置通知
         * method.invoke(obj, args);
         * @AfterReturning 返回通知
         * }catch(Exception e){
         * @AfterThrowing 异常通知
         * }
         * finally{
         * @After 后置通知
         * }
         * <p>
         * 四合一通知就是环绕通知:
         * 环绕通知中有一个参数:ProceedingJoinPoint pjp
         *
         * 环绕通知:是优先于普通通知执行,执行顺序:
         * [普通通知]
         * {
         *
         *      try{
         *          环绕前置
         *          环绕执行:目标方法执行
         *          环绕返回
         *      }catch(Exception e){
         *          环绕出现异常
         *      }finally{
         *          环绕后置
         *      }
         *
         * }
         * [普通后置]
         * [普通方法返回/方法异常]
         *
         * 新的顺序:
         *      环绕前置---普通前置---目标方法执行---环绕正常返回/出现异常---环绕后置---普通后置---普通返回或异常
         * 注意:
         *
         */
    
        @Around("hahaMyPoint()")
        public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
            Object[] args = pjp.getArgs();
            String name = pjp.getSignature().getName();
            Object proceed = null;
            // idea ctrl + alt + t
            try {
                //利用反射调用目标方法即可,就是method.invoke(obj,args)
                //@Before
                System.out.println("[环绕前置通知]-[" + name + "方法开始]");
                proceed = pjp.proceed();
                //@AfterReturning
                System.out.println("[环绕返回通知]-[" + name + "方法返回,返回值为:" + proceed + "]");
            } catch (Exception e) {
                //@AfterThrowing
                System.out.println("[环绕异常通知]-[" + name + "方法出现异常],异常信息" + e.getCause());
                //为了让外界能知道这个异常,这个异常一定要抛出去
                throw new RuntimeException(e);
            } finally {
                //@After
                System.out.println("[环绕后置通知最终结束]-[" + name + "]方法结束");
            }
            //反射调用后的返回值也一定返回出去
            return proceed;
        }
    
    
    }

    ValidateAspect.java:

    package com.atguigu.utils;
    
    import org.aspectj.lang.JoinPoint;
    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;
    
    /**
     * @Title: ValidateApsect
     * @Description:
     * @Author:
     * @Version: 1.0
     * @create 2020/6/8 11:53
     */
    
    @Aspect
    @Component
    @Order(2)
    public class ValidateAspect {
    
        @Before("com.atguigu.utils.LogUtils.hahaMyPoint()")
        public void logStart(JoinPoint joinPoint) {
            //获取目标方法运行时使用的参数
            Object[] args = joinPoint.getArgs();
            //获取到方法签名
            Signature signature = joinPoint.getSignature();
            String name = signature.getName();
            System.out.println("[VaAspect-前置][" + name + "]方法开始执行,使用的参数列表" + Arrays.asList(args));
        }
    
        @AfterReturning(value = "com.atguigu.utils.LogUtils.hahaMyPoint()",returning = "result")
        public void logReturn(JoinPoint joinPoint, Object result) {
            Signature signature = joinPoint.getSignature();
            String name = signature.getName();
            System.out.println("[VaAspect-返回][" + name + "]方法正常执行完成了,计算结果是" + result);
        }
    
        @AfterThrowing(value = "com.atguigu.utils.LogUtils.hahaMyPoint()",throwing = "exception")
        public void logException(JoinPoint joinPoint, Exception exception) {
            System.out.println("[VaAspect-异常][" + joinPoint.getSignature().getName() + "]方法出现异常了,异常信息是:[" + exception + "],这个异常已经通知测试测试小组进行排查");
        }
    
        @After("com.atguigu.utils.LogUtils.hahaMyPoint()")
        public int logEnd(JoinPoint joinPoint) {
            System.out.println("[VaAspect-后置][" + joinPoint.getSignature().getName() + "]方法最终结束");
            return 0;
        }
    }

    applicationContext.xml:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4        xmlns:context="http://www.springframework.org/schema/context"
     5        xmlns:aop="http://www.springframework.org/schema/aop"
     6        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
     7 
     8     <context:component-scan base-package="com.atguigu"></context:component-scan>
     9     <!--开启基于注解的AOP功能:AOP名称空间-->
    10     <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    11 
    12 </beans>

    AOPTest.java:

    package com.atguigu.test;
    
    import com.atguigu.impl.MyMathCalculator;
    import com.atguigu.inter.Calculator;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * @Title: AOPTest
     * @Description:
     * @Author:
     * @Version: 1.0
     * @create 2020/6/7 23:35
     */
    //@ContextConfiguration(locations = "classpath:applicationContext.xml")
    //@RunWith(SpringJUnit4ClassRunner.class)
    public class AOPTest {
    
        ApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    
        //@Autowired
        //private Calculator calculator;
    
        //1.从ioc容器中拿到目标对象,注意:一定用它的接口类型,不要用它本类
        //细节一:com.atguigu.impl.MyMathCalculator@3c947bc5
        //class com.sun.proxy.$Proxy18
        //AOP的底层就是动态代理,容器中保存的组件是它的代理对象 $Proxy18 当然不是本类的类型
        @Test
        public void test(){
    
            //calculator.add(1,2);
            Calculator bean = ioc.getBean(Calculator.class);
            bean.add(2,1);
            System.out.println(bean);
            System.out.println(bean.getClass());
        }
    
        @Test
        public void test02(){
            Calculator bean = (Calculator) ioc.getBean("myMathCalculator");
            System.out.println(bean.getClass());
        }
    
        @Test
        public void test03(){
            //没有接口就算本类类型
            //cglib帮我们创建好了代理对象
            //  class com.atguigu.impl.MyMathCalculator$$EnhancerBySpringCGLIB$$355554e1
            //总结:有接口就算jdk创建对象,没有接口就算cglib创建对象
            MyMathCalculator bean = ioc.getBean(MyMathCalculator.class);
            bean.add(1,2);
            System.out.println(bean.getClass());
        }
    
        /**
         * 通知方法的执行顺序:
         *
         * try{
         *     @Before
         *     method.invoke(obj,args);
         *     @AfterReturning
         * }catch(Exception c){
         *     @AfterThrowing
         * }finally{
         *     @After
         * }
         *
         * 正常执行:@Before(前置通知)-----> @After(后置通知) -----> @AfterReturning(正常返回)
         * 异常执行:@Before(前置通知)-----> @After(后置通知) -----> @AfterThrowing(异常通知)
         */
        //切面执行顺序:以类名首字母位置排序,排序靠前的先执行
        //@Order(1)//使用Order改变切面顺序,数值越小,优先级越高
        @Test
        public void test04(){
            MyMathCalculator bean = ioc.getBean(MyMathCalculator.class);
            bean.add(1,2);
            System.out.println("============");
    //        bean.div(1,0);
            bean.div(1,1);
        }
    }

     图解:

    总结:

  • 相关阅读:
    Python 中的Lock与RLock
    Python 自定义三方库
    Python 通过RSA实现license验证设备指纹与有效期
    Python 通过wmi获取Window服务器硬件信息
    Java List对象集合按对象属性分组、分组汇总、过滤等操作示例
    BrokenPipeError: [Errno 32] Broken pipe
    Python 通过dmidecode获取Linux服务器硬件信息
    Linux 解决E: Sub-process /usr/bin/dpkg returned an error code (1)错误
    Ubuntu 增加新用户并赋予root权限及免密的方法
    关于smali插桩
  • 原文地址:https://www.cnblogs.com/116970u/p/13067007.html
Copyright © 2020-2023  润新知