【Spring】AOP详解

AOP概述

AOP(Aspect-Oriented Programming)面向切面编程,是Spring框架体系结构中重要的功能模块,在代码执行过程中,动态嵌入其他代码,通常使用在如日志、事务处理、异常处理等场景。
AOP 术语

术语 描述
切面 切面泛指交叉业务逻辑。比如事务处理、日志处理就可以理解为切面。常用的切面有通知与顾问。实际就是对主业务逻辑的一种增强
织入 织入是指将切面代码插入到目标对象的过程。
连接点 连接点指切面可以织入的位置。
切入点 切入点指切面具体织入的位置。
通知(Advice) 通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
目标对象 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。
织入 织入把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。

通知的类型

通知 描述
前置通知 在一个方法执行之前,执行通知。
后置通知 在一个方法执行之后,不考虑其结果,执行通知。
返回后通知 在一个方法执行之后,只有在方法成功完成时,才能执行通知
抛出异常后通知 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。
环绕通知 在建议方法调用之前和之后,执行通知。

Spring基于代理类AOP实现

前面介绍了Java中代理模式和JDK提供的动态代理:Java代理模式之-静态代理和动态代理原理解析
Spring 默认使用JDK动态代理实现的AOP编程

首先创建Spring工程:【Spring】Spring入门案例

pom.xml中加入Spring aop相关的包

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>

前置通知

代码结构如下:
在这里插入图片描述

创建目标接口和实现类

public interface UserService {
    public void selectUser();
}
public class UserServiceImpl implements UserService{
    @Override
    public void selectUser() {
        System.out.println("UserServiceImpl -> selectUser");
    }
}

创建切面类

public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    /**
     * method 目标方法
     * args 目标方法参数列表
     * target 目标对象
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("MyMethodBeforeAdvice -> 前置通知的before方法执行...");
    }
}

添加配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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.xsd">
    <!-- 注册目标类 -->
    <bean id="userService" class="com.lucas.UserServiceImpl" ></bean>
    <!-- 注册前置通知 -->
    <bean id="myMethodBeforeAdvice" class="com.lucas.MyMethodBeforeAdvice" ></bean>

    <!-- 注册代理类 -->
    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 指定目标对象 -->
        <property name="target" ref="userService"/>
        <!-- 指定目标类实现的所有接口 -->
        <property name="interfaces" value="com.lucas.UserService"/>
        <!-- 指定切面 -->
        <property name="interceptorNames" >
            <list>
                <value>myMethodBeforeAdvice</value>
            </list>
        </property>
    </bean>
</beans>

添加测试代码

public class App {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new
                ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("proxyFactoryBean");
        userService.selectUser();
    }
}

运行如下:
在这里插入图片描述

返回后通知

添加返回后通知切面类


import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class MyAfterRunningAdvice implements AfterReturningAdvice {
    /**
     * method 目标方法
     * args 目标方法参数列表
     * target 目标对象
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("MyAfterRunningAdvice -> 后置方法执行了...");
    }
}

修改配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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.xsd">
    <!-- 注册目标类 -->
    <bean id="userService" class="com.lucas.UserServiceImpl" ></bean>
    <!-- 注册前置通知 -->
    <bean id="myMethodBeforeAdvice" class="com.lucas.MyMethodBeforeAdvice" ></bean>
    <!-- 注册返回后通知 -->
    <bean id="myMethodAfterAdvice" class="com.lucas.MyAfterRunningAdvice" ></bean>
    <!-- 注册代理类 -->
    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 指定目标对象 -->
        <property name="target" ref="userService"/>
        <!-- 指定目标类实现的所有接口 -->
        <property name="interfaces" value="com.lucas.UserService"/>
        <!-- 指定切面 -->
        <property name="interceptorNames" >
            <list>
                <value>myMethodBeforeAdvice</value>
                <value>myMethodAfterAdvice</value>
            </list>
        </property>
    </bean>
</beans>

运行结果如下:
在这里插入图片描述

环绕通知

创建环绕通知切面类

package com.lucas;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("MyMethodInterceptor -> 环绕通知 --- before");
        Object res = invocation.proceed();
        if(res !=null){
            // 增强返回结果
            res = ((String)res).toUpperCase();
        }
        System.out.println("MyMethodInterceptor 环绕通知---after");
        return res;
    }
}

修改配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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.xsd">
    <!-- 注册目标类 -->
    <bean id="userService" class="com.lucas.UserServiceImpl" ></bean>
    <!-- 注册前置通知 -->
    <bean id="myMethodBeforeAdvice" class="com.lucas.MyMethodBeforeAdvice" ></bean>
    <!-- 注册返回后通知 -->
    <bean id="myMethodAfterAdvice" class="com.lucas.MyAfterRunningAdvice" ></bean>
    <!-- 注册环绕通知 -->
    <bean id="myMethodInterceptor" class="com.lucas.MyMethodInterceptor" ></bean>
    <!-- 注册代理类 -->
    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 指定目标对象 -->
        <property name="target" ref="userService"/>
        <!-- 指定目标类实现的所有接口 -->
        <property name="interfaces" value="com.lucas.UserService"/>
        <!-- 指定切面 -->
        <property name="interceptorNames" >
            <list>
                <value>myMethodBeforeAdvice</value>
                <value>myMethodAfterAdvice</value>
                <value>myMethodInterceptor</value>
            </list>
        </property>
    </bean>
</beans>

运行结果
在这里插入图片描述

异常通知

创建异常通知切面类

import org.springframework.aop.ThrowsAdvice;

public class MyThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(Exception ex){
        System.out.println("MyThrowsAdvice -> afterThrowing 异常通知");
    }
}

UserServiceImpl中制造异常

public class UserServiceImpl implements UserService {
    @Override
    public void selectUser() {
        System.out.println("UserServiceImpl -> selectUser");
        throw new NullPointerException();
    }
}

添加配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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.xsd">
    <!-- 注册目标类 -->
    <bean id="userService" class="com.lucas.UserServiceImpl" ></bean>
    <!-- 注册前置通知 -->
    <bean id="myMethodBeforeAdvice" class="com.lucas.MyMethodBeforeAdvice" ></bean>
    <!-- 注册返回后通知 -->
    <bean id="myMethodAfterAdvice" class="com.lucas.MyAfterRunningAdvice" ></bean>
    <!-- 注册环绕通知 -->
    <bean id="myMethodInterceptor" class="com.lucas.MyMethodInterceptor" ></bean>
    <!-- 异常通知 -->
    <bean id="myThrowsAdvice" class="com.lucas.MyThrowsAdvice" ></bean>
    <!-- 注册代理类 -->
    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 指定目标对象 -->
        <property name="target" ref="userService"/>
        <!-- 指定目标类实现的所有接口 -->
        <property name="interfaces" value="com.lucas.UserService"/>
        <!-- 指定切面 -->
        <property name="interceptorNames" >
            <list>
                <value>myMethodBeforeAdvice</value>
                <value>myMethodAfterAdvice</value>
                <value>myMethodInterceptor</value>
                <value>myThrowsAdvice</value>
            </list>
        </property>
    </bean>
</beans>

运行结果:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/huweiliyi/article/details/107692925