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]...

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

posted @ 2021-07-23 17:08  那些年的事儿  阅读(588)  评论(0编辑  收藏  举报