zoukankan      html  css  js  c++  java
  • Spring,AOP,五大通知类型,通知的常用属性及其描述

    本文主要部分转载自:https://www.cnblogs.com/chuijingjing/p/9806651.html,略作修改和添加。

    一、前置通知
    在目标方法执行之前的通知。

    前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象和目标方法相关的信息。注意:如果接收JoinPoint,必须保证其为方法的第一个参数,否则报错。

    1. 创建接口

    package com.clzhang.spring.aspectj.service;
    
    public interface UserService {
        public void addUser(String name);
    
        public void updateUser();
    
        public void deleteUser();
    
        public void query();
    }

    2. 创建实现类

    package com.clzhang.spring.aspectj.service;
    
    import org.springframework.stereotype.Service;
    
    /**
     * UserServiceImple:目标对象
     */
    @Service("userService")
    public class UserServiceImpl implements UserService {
    
        @Override
        public void addUser(String name) {
            System.out.println("增加用户……");
        }
    
        @Override
        public void updateUser() {
            System.out.println("修改用户……");
        }
    
        @Override
        public void deleteUser() {
            System.out.println("删除用户……");
        }
    
        @Override
        public void query() {
            System.out.println("查询用户……");
        }
    }

    3. 创建切面类

    package com.clzhang.spring.aspectj.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.Signature;
    import org.springframework.stereotype.Component;
    
    /**
     * FirstAspect:切面代码
     */
    @Component
    public class FirstAspect {
        public void before(JoinPoint jp) { // 可以选择额外的传入一个JoinPoint连接点对象,必须用方法的第一个参数接收。
            Class clz = jp.getTarget().getClass();
            Signature signature = jp.getSignature(); // 通过JoinPoint对象获取更多信息
            String name = signature.getName();
            System.out.println("1 -- before...[" + clz + "]...[" + name + "]...");
        }
    }

    4. 创建配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" 
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
        
        <context:annotation-config></context:annotation-config>
        <context:component-scan base-package="com.clzhang.spring.aspectj.service,com.clzhang.spring.aspectj.aop"></context:component-scan>
        
        <aop:config proxy-target-class="true">
            <!-- 配置切入点  -->
            <aop:pointcut 
                expression="execution(* com.clzhang.spring.aspectj.service.UserServiceImpl.addUser(..))" 
                id="pc01"/>
                
            <!-- 配置切面 -->
            <aop:aspect ref="firstAspect">
                <!-- 前置通知 -->
                <aop:before method="before" pointcut-ref="pc01"/>
    
            </aop:aspect>
        </aop:config>
    </beans>

    5. 测试代码

    package com.clzhang.spring.aspectj.test;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.clzhang.spring.aspectj.service.UserService;
    
    /**
     * AOPTest:测试代码
     */
    public class AOPTest {
        @Test
        public void test01(){
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.addUser("cjj"); // 一个连接点
        }
    }

    6. 运行结果

    1 -- before...[class com.clzhang.spring.aspectj.service.UserServiceImpl]...[addUser]...
    增加用户……

    二、环绕通知

    在目标方法执行之前和之后都可以执行额外代码的通知。

    在环绕通知中必须显式的调用目标方法,目标方法才会执行,这个显式调用时通过ProceedingJoinPoint来实现的,可以在环绕通知中接收一个此类型的形参,Spring容器会自动将该对象传入,注意这个参数必须处在环绕通知的第一个形参位置。

    注意:只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。

    环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个null。

    环绕通知有控制目标方法是否执行、有控制是否返回值、有改变返回值的能力。

    环绕通知虽然有这样的能力,但一定要慎用,不是技术上不可行,而是要小心不要破坏了软件分层的“高内聚 低耦合”的目标。

    1. 修改切面类,增加around方法

    package com.clzhang.spring.aspectj.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.springframework.stereotype.Component;
    
    /**
     * FirstAspect:切面代码
     */
    @Component
    public class FirstAspect {
        public void before(JoinPoint jp) { // 可以选择额外的传入一个JoinPoint连接点对象,必须用方法的第一个参数接收。
            Class clz = jp.getTarget().getClass();
            Signature signature = jp.getSignature(); // 通过JoinPoint对象获取更多信息
            String name = signature.getName();
            System.out.println("1 -- before...[" + clz + "]...[" + name + "]...");
        }
        
        public Object around(ProceedingJoinPoint jp) throws Throwable{
            System.out.println("1 -- around before...");
            Object obj = jp.proceed(); //--显式的调用目标方法
            System.out.println("1 -- around after...");
            return obj;
        }    
    }

    2. 修改配置文件,将method改为around

                <!-- 环绕通知 -->
                <aop:around method="around" pointcut-ref="pc01" />

    3. 运行结果

    1 -- around before...
    增加用户……
    1 -- around after...

    三、后置通知

    在目标方法执行之后执行的通知。

    在后置通知中也可以选择性的接收一个JoinPoint来获取连接点的额外信息,但是这个参数必须处在参数列表的第一个。

    1. 修改切面类,增加afterReturn方法

    package com.clzhang.spring.aspectj.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.springframework.stereotype.Component;
    
    /**
     * FirstAspect:切面代码
     */
    @Component
    public class FirstAspect {
        public void before(JoinPoint jp) { // 可以选择额外的传入一个JoinPoint连接点对象,必须用方法的第一个参数接收。
            Class clz = jp.getTarget().getClass();
            Signature signature = jp.getSignature(); // 通过JoinPoint对象获取更多信息
            String name = signature.getName();
            System.out.println("1 -- before...[" + clz + "]...[" + name + "]...");
        }
    
        public Object around(ProceedingJoinPoint jp) throws Throwable {
            System.out.println("1 -- around before...");
            Object obj = jp.proceed(); // --显式的调用目标方法
            System.out.println("1 -- around after...");
            return obj;
        }
    
        public void afterReturn(JoinPoint jp) {
            Class clz = jp.getTarget().getClass();
            Signature signature = jp.getSignature();
            String name = signature.getName();
            System.out.println("1 -- afterReturn...[" + clz + "]...[" + name + "]...");
        }
    }

    2. 修改配置文件,将method改为afterReturn

                <!-- 后置通知 -->
                <aop:after-returning method="afterReturn" pointcut-ref="pc01" />

    3. 运行结果

    增加用户……
    1 -- afterReturn...[class com.clzhang.spring.aspectj.service.UserServiceImpl]...[addUser]...

    四、异常通知
    在目标方法抛出异常时执行的通知。

    可以配置传入JoinPoint获取目标对象和目标方法相关信息,但必须处在参数列表第一位。另外,还可以配置参数,让异常通知可以接收到目标方法抛出的异常对象。

    1. 修改切面类,增加afterThrow方法

    package com.clzhang.spring.aspectj.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.springframework.stereotype.Component;
    
    /**
     * FirstAspect:切面代码
     */
    @Component
    public class FirstAspect {
        public void before(JoinPoint jp) { // 可以选择额外的传入一个JoinPoint连接点对象,必须用方法的第一个参数接收。
            Class clz = jp.getTarget().getClass();
            Signature signature = jp.getSignature(); // 通过JoinPoint对象获取更多信息
            String name = signature.getName();
            System.out.println("1 -- before...[" + clz + "]...[" + name + "]...");
        }
    
        public Object around(ProceedingJoinPoint jp) throws Throwable {
            System.out.println("1 -- around before...");
            Object obj = jp.proceed(); // --显式的调用目标方法
            System.out.println("1 -- around after...");
            return obj;
        }
    
        public void afterReturn(JoinPoint jp){
            Class clz = jp.getTarget().getClass();
            Signature signature = jp.getSignature(); 
            String name = signature.getName();
            System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...");
        }
    
        public void afterThrow(JoinPoint jp,Throwable e){
            Class clz = jp.getTarget().getClass();
            String name = jp.getSignature().getName();
            System.out.println("1afterThrow..["+clz+"]..["+name+"].."+e.getMessage());
        }
    }

    2. 修改配置文件,将method改为afterThrow

                <!-- 异常通知 -->
                <aop:after-throwing method="afterThrow" pointcut-ref="pc01" throwing="e" />

    3. 修改实现类,扔出一个异常

        @Override
        public void addUser(String name) {
            System.out.println("增加用户……");
            int i = 10/0; // 扔出异常
        }

    4. 处理结果

    增加用户……
    1afterThrow..[class com.clzhang.spring.aspectj.service.UserServiceImpl]..[addUser]../ by zero

    五、最终通知

    是在目标方法执行之后执行的通知。

    和后置通知不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返-例如抛出异常,则后置通知不会执行。

    而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。

    另外,后置通知可以通过配置得到返回值,而最终通知无法得到。最终通知也可以额外接收一个JoinPoint参数,来获取目标对象和目标方法相关信息,但一定要保证必须是第一个参数。

    1. 修改切面类,增加after方法

    package com.clzhang.spring.aspectj.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.springframework.stereotype.Component;
    
    /**
     * FirstAspect:切面代码
     */
    @Component
    public class FirstAspect {
        public void before(JoinPoint jp) { // 可以选择额外的传入一个JoinPoint连接点对象,必须用方法的第一个参数接收。
            Class clz = jp.getTarget().getClass();
            Signature signature = jp.getSignature(); // 通过JoinPoint对象获取更多信息
            String name = signature.getName();
            System.out.println("1 -- before...[" + clz + "]...[" + name + "]...");
        }
    
        public Object around(ProceedingJoinPoint jp) throws Throwable {
            System.out.println("1 -- around before...");
            Object obj = jp.proceed(); // --显式的调用目标方法
            System.out.println("1 -- around after...");
            return obj;
        }
    
        public void afterReturn(JoinPoint jp){
            Class clz = jp.getTarget().getClass();
            Signature signature = jp.getSignature(); 
            String name = signature.getName();
            System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...");
        }
    
        public void afterThrow(JoinPoint jp,Throwable e){
            Class clz = jp.getTarget().getClass();
            String name = jp.getSignature().getName();
            System.out.println("1afterThrow..["+clz+"]..["+name+"].."+e.getMessage());
        }
        
        public void after(JoinPoint jp){
            Class clz = jp.getTarget().getClass();
            String name = jp.getSignature().getName();
            System.out.println("1 -- after..["+clz+"]..["+name+"]...");
        }
    }

    2. 修改配置文件,将method改为after

                <!-- 最终通知 -->
                <aop:after method="after" pointcut-ref="pc01" />

    3. 记住,把实现类的异常删除

        @Override
        public void addUser(String name) {
            System.out.println("增加用户……");
        }

    4. 运行结果

    增加用户……

    1 -- after..[class com.clzhang.spring.aspectj.service.UserServiceImpl]..[addUser]...

    六、通知的常用属性及其描述

  • 相关阅读:
    django学习第85天Django的Ajax
    django学习第84天Django常用字段和参数
    django学习第83天Django聚合查询.分组查询.FQ查询
    django学习第82天Django多表查询
    django学习第81天Django模板层2(单表查询.模块的导入和继承.静态文件配置)
    django学习第80天Django模板层1
    django学习第79天Django视图层
    Linux 内核文档翻译
    Linux设备模型——设备驱动模型和sysfs文件系统解读
    内核空间内存申请函数kmalloc kzalloc vmalloc的区别
  • 原文地址:https://www.cnblogs.com/nayitian/p/15049725.html
Copyright © 2011-2022 走看看