Spring AOP使用

简介: Spring AOP使用

一、简介

AOP全程是Aspect—Oriented—Programming,面向切面编程。AOP采用横向抽取机制,将分散在各个方法中的重复代码抽取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。

  使用AOP编程,可以使开发人员专心于核心业务,而不用过多的关注于其他业务逻辑的是实现,不但提高了开发效率,而且增强了代码的可维护性。

  目前最流行的AOP框架有两个,分别是Spring AOP和AspectJ。其中:Spring AOP采用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。AspectJ是一个基于Java语言的AOP框架,从Spring 2.0开始,AspectJ扩展了Java语言,扩展了一个专门的编译器,在编译时提供横向代码的织入。

二、重点解析

1、代理分类

  • Pointcut:全局切入点
  • Before:前置通知
  • After-returning:后置通知
  • Around:环绕通知
  • After-throwing:异常通知
  • After:最终通知
  • Declare-parents:引介通知

2、切入点位置配置(expression)

// 定义切入点表达式
  @Pointcut("execution(* com.lydms.jdk.*.*(..))")
private void myPointCut(){}
  • 第1个*:表示返回类型,*****表示任意返回类型。
  • com.lydms.jdk:表示需要拦截的包名。
  • 第2个*:表示类名,*****表示所有类。
  • 第3个*:表示方法名,*****表示所有方法。
  • (…):表示方法的参数,”…“表示任意参数。
  • 第一个*与包名之间有空格。

3、配置通知

属性名称 描述
pointcut 该属性用于指定一个切入点表达式,Spring将在匹配表达式的连接点时织入该通知。
pointcut-ref 该属性指定一个已经存在的切入点名称。通常pointcut和pointcut-ref两个属性只需要其中之一。
method 该属性指定一个方法名。指定将切面Bean中的该方法转换为增强处理。
throwing 该属性只对<after-throwing>元素有效,它用于指定一个形参名,异常通知方法可以通过该形参访问目标方法所抛出的异常。
returning 该属性只对<after-returning>元素有效。它用于指定一个形参名,后置通知方法可以通过该形参访问目标方法的返回值。

案例一:表示任意方法的任意类任意参数都走切面。

@Pointcut("execution(* com.lydms.jdk.*.*(..))")
  private void myPointCut(){
  }

案例二:public修饰符,在upload类下的。方法走切面。

@Pointcut("execution(public * com.lydms.controller.upload.*(..))")
    public void webLog() {
    }

二、动态代理

  • Sring AOP中的代理。可以是JDK动态代理,也可以是CGLIB代理。
  • JDK动态代理实现很简单,但是有一定局限性—使用动态代理的对象必须实现一个或多个接口。假如没有实现的,则使用CGLIB代理。

1、JDK动态代理

JDK动态代理通过java.lang.reflect.proxy来实现。可以调用Proxy类的newProxyInstance( )方法来创建代理对象。对于使用业务接口的类,Spring 默认使用JDK动态代理来实现AOP。

1)、UserDao接口

public interface UserDao {
  public void addUser();
  public void deleteUser();
}

2)、UserDaoImpl实现类

import org.springframework.stereotype.Repository;
// 目标类
@Repository("userDao")
public class UserDaoImpl implements UserDao {
  public void addUser() {
//    int i = 10/0;
    System.out.println("添加用户");
  }
  public void deleteUser() {
    System.out.println("删除用户");
  }
}

3)、代理实现类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.lydms.aspect.MyAspect;
/**
 * JDK代理类
 */
public class JdkProxy implements InvocationHandler{
  // 声明目标类接口
  private UserDao userDao;
  // 创建代理方法
  public  Object createProxy(UserDao userDao) {
    this.userDao = userDao;
    // 1.类加载器
    ClassLoader classLoader = JdkProxy.class.getClassLoader();
    // 2.被代理对象实现的所有接口
    Class[] clazz = userDao.getClass().getInterfaces();
    // 3.使用代理类,进行增强,返回的是代理后的对象
    return  Proxy.newProxyInstance(classLoader,clazz,this);
  }
  /*
   * 所有动态代理类的方法调用,都会交由invoke()方法去处理
   * proxy 被代理后的对象 
   * method 将要被执行的方法信息(反射) 
   * args 执行方法时需要的参数
   */
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 声明切面
    MyAspect myAspect = new MyAspect();
    // 前增强
    myAspect.check_Permissions();
    // 在目标类上调用方法,并传入参数
    Object obj = method.invoke(userDao, args);
    // 后增强
    myAspect.log();
    return obj;
  }
}

4)、测试类

public class JdkTest{
  public static void main(String[] args) {
    // 创建代理对象
    JdkProxy jdkProxy = new JdkProxy();
         // 创建目标对象
    UserDao userDao= new UserDaoImpl();
    // 从代理对象中获取增强后的目标对象
    UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
    // 执行方法
    userDao1.addUser();
    userDao1.deleteUser();
  }
}

执行结果:

2、CGLIB代理

是一个高性能的开源的代码生成包,采用非常底层字节码技术,对指定的目标类生成子类,并对子类进行加强。

1)、UserDao.java

//目标类
public class UserDao {
  public void addUser() {
    System.out.println("添加用户");
  }
  public void deleteUser() {
    System.out.println("删除用户");
  }
}

2)、代理类

import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import com.lydms.aspect.MyAspect;
// 代理类
public class CglibProxy implements MethodInterceptor{
    // 代理方法
  public  Object createProxy(Object target) {
    // 创建一个动态类对象
    Enhancer enhancer = new Enhancer();
    // 确定需要增强的类,设置其父类
    enhancer.setSuperclass(target.getClass());
    // 添加回调函数
    enhancer.setCallback(this);
    // 返回创建的代理类
    return enhancer.create();
  }
  /**
   * proxy CGlib根据指定父类生成的代理对象
   * method 拦截的方法
   * args 拦截方法的参数数组
   * methodProxy 方法的代理对象,用于执行父类的方法 
   */
  @Override
  public Object intercept(Object proxy, Method method, Object[] args, 
                       MethodProxy methodProxy) throws Throwable {
         // 创建切面类对象
    MyAspect myAspect = new MyAspect();
    // 前增强
    myAspect.check_Permissions();
    // 目标方法执行
    Object obj = methodProxy.invokeSuper(proxy, args);
    // 后增强
    myAspect.log(); 
    return obj;
  }
}

3)、测试类

// 测试类
public class CglibTest {
  public static void main(String[] args) {
    // 创建代理对象
    CglibProxy cglibProxy = new CglibProxy();
          // 创建目标对象
    UserDao userDao = new UserDao();
         // 获取增强后的目标对象
    UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao);
    // 执行方法
    userDao1.addUser();
    userDao1.deleteUser();
  }
}

执行结果:

四、AspectJ开发

1、基于XML的声明式AspectJ

1) 简介

2) 切面类

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
 *切面类,在此类中编写通知
 */
public class MyAspect {
  // 前置通知
  public void myBefore(JoinPoint joinPoint) {
    System.out.print("前置通知 :模拟执行权限检查...,");
    System.out.print("目标类是:"+joinPoint.getTarget() );
    System.out.println(",被织入增强处理的目标方法为:"
                            +joinPoint.getSignature().getName());
  }
  // 后置通知
  public void myAfterReturning(JoinPoint joinPoint) {
    System.out.print("后置通知:模拟记录日志...," );
    System.out.println("被织入增强处理的目标方法为:"
                          + joinPoint.getSignature().getName());
  }
  /**
   * 环绕通知
   * ProceedingJoinPoint 是JoinPoint子接口,表示可以执行目标方法
   * 1.必须是Object类型的返回值
   * 2.必须接收一个参数,类型为ProceedingJoinPoint
   * 3.必须throws Throwable
   */
  public Object myAround(ProceedingJoinPoint proceedingJoinPoint) 
             throws Throwable {
    // 开始
    System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");
    // 执行当前目标方法
    Object obj = proceedingJoinPoint.proceed();
    // 结束
    System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
    return obj;
  }
  // 异常通知
  public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
    System.out.println("异常通知:" + "出错了" + e.getMessage());
  }
  // 最终通知
  public void myAfter() {
    System.out.println("最终通知:模拟方法结束后的释放资源...");
  }
}

3) 注解

<?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:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
  <!-- 1 目标类 -->
  <bean id="userDao" class="com.lydms.jdk.UserDaoImpl" />
  <!-- 2 切面 -->
  <bean id="myAspect" class="com.lydms.aspectj.xml.MyAspect" />
  <!-- 3 aop编程 -->
  <aop:config>
        <!-- 配置切面 -->
    <aop:aspect ref="myAspect">
      <!-- 3.1 配置切入点,通知最后增强哪些方法 -->
      <aop:pointcut expression="execution(* com.lydms.jdk.*.*(..))"
                                              id="myPointCut" />
      <!-- 3.2 关联通知Advice和切入点pointCut -->
      <!-- 3.2.1 前置通知 -->
      <aop:before method="myBefore" pointcut-ref="myPointCut" />
      <!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值
       returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
      <aop:after-returning method="myAfterReturning"
        pointcut-ref="myPointCut" returning="returnVal" />
      <!-- 3.2.3 环绕通知 -->
      <aop:around method="myAround" pointcut-ref="myPointCut" />
      <!-- 3.2.4 抛出通知:用于处理程序发生异常-->
      <!-- * 注意:如果程序没有异常,将不会执行增强 -->
      <!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
      <aop:after-throwing method="myAfterThrowing"
        pointcut-ref="myPointCut" throwing="e" />
      <!-- 3.2.5 最终通知:无论程序发生任何事情,都将执行 -->
      <aop:after method="myAfter" pointcut-ref="myPointCut" />
    </aop:aspect>
  </aop:config>
</beans>

4) 测试类

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.lydms.jdk.UserDao;
// 测试类
public class TestXmlAspectj {
  public static void main(String args[]) {
    String xmlPath = 
                         "com/lydms/aspectj/xml/applicationContext.xml";
    ApplicationContext applicationContext = 
                          new ClassPathXmlApplicationContext(xmlPath);
    // 1 从spring容器获得内容
    UserDao userDao = (UserDao) applicationContext.getBean("userDao");
    // 2 执行方法
    userDao.addUser();
  }
}

2、基于注解的声明式AspectJ

1)注解及其描述:

注解名称 描述
@Aspect 用于定义一个切面。
@Pointcut 用于定义切入点表达式。在使用时,还需要定义方法签名(包含名字和任意参数),来表示切入点名称。
@Before 用于定义前置通知。相当于BeforeAdvice。通常指定一个value值,用来指定切入点表达式。
@AfterReturning 用于定义后置通知。相当于AfterReturningAdvice。
@Around 用于定义环绕通知。相当于MethodInterceptor。
@AfterThrowing 用于定义异常通知来处理程序中未处理的异常。相当于ThrowAdvice。
@After 用于定义最终final通知。无论是否异常,都会执行。
@DeclareParents 用于定义引介通知,相当于IntroductionInterceptor。

2)切面类:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
 * 切面类,在此类中编写通知
 */
@Aspect
@Component
public class MyAspect {
  // 定义切入点表达式
  @Pointcut("execution(* com.lydms.jdk.*.*(..))")
  // 使用一个返回值为void、方法体为空的方法来命名切入点
  private void myPointCut(){}
  // 前置通知
  @Before("myPointCut()")
  public void myBefore(JoinPoint joinPoint) {
    System.out.print("前置通知 :模拟执行权限检查...,");
    System.out.print("目标类是:"+joinPoint.getTarget() );
    System.out.println(",被织入增强处理的目标方法为:"
                   +joinPoint.getSignature().getName());
  }
  // 后置通知
  @AfterReturning(value="myPointCut()")
  public void myAfterReturning(JoinPoint joinPoint) {
    System.out.print("后置通知:模拟记录日志...," );
    System.out.println("被织入增强处理的目标方法为:"
                  + joinPoint.getSignature().getName());
  }
  // 环绕通知 
  @Around("myPointCut()")
  public Object myAround(ProceedingJoinPoint proceedingJoinPoint) 
            throws Throwable {
    // 开始
    System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");
    // 执行当前目标方法
    Object obj = proceedingJoinPoint.proceed();
    // 结束
    System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
    return obj;
  }
  // 异常通知
  @AfterThrowing(value="myPointCut()",throwing="e")
  public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
    System.out.println("异常通知:" + "出错了" + e.getMessage());
  }
  // 最终通知
  @After("myPointCut()")
  public void myAfter() {
    System.out.println("最终通知:模拟方法结束后的释放资源...");
  }
}

3)注解配置要扫描的包:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:aop="http://www.springframework.org/schema/aop"
  http://www.springframework.org/schema/context/spring-context-4.3.xsd">
      <!-- 指定需要扫描的包,使注解生效 -->
      <context:component-scan base-package="com.lydms" />
      <!-- 启动基于注解的声明式AspectJ支持 -->
      <aop:aspectj-autoproxy />
</beans>

4)测试类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.lydms.jdk.UserDao;
// 测试类
public class TestAnnotationAspectj {
  public static void main(String args[]) {
    String xmlPath = 
                  "com/lydms/aspectj/annotation/applicationContext.xml";
    ApplicationContext applicationContext = 
                 new ClassPathXmlApplicationContext(xmlPath);
    // 1 从spring容器获得内容
    UserDao userDao = (UserDao) applicationContext.getBean("userDao");
    // 2 执行方法
    userDao.addUser();
  }
}

五、Spring boot集成AspectJ开发

1、Pom.xml

<!--不指定版本的原因,如不指定版本,会默认下载springboot对应版本的jar -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、Controller.java

import com.lydms.common.Result;
import com.lydms.pojo.English;
import com.lydms.service.WordAddService;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@RestController
@RequestMapping("/add")
public class WordAddController {
    private static final Logger logger = LogManager.getLogger(WordAddController.class);
    @Autowired
    private WordAddService wordAddService;
    /**
     * 添加英语
     * @throws Exception
     */
    @RequestMapping(value = "/english", method = {RequestMethod.POST})
    public ApiResult addEnglish(@RequestBody English en) {
//        1、将单词存入数据库
        English result = wordAddService.addEnglish(en);
        return ApiResult.succ(result);
    }
}

3、切面类(Aspect.java)

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
@Aspect
@Order(5)
@Component
public class ControllerAspect {
    private static final Logger logger = LogManager.getLogger(ControllerAspect.class);
    private ThreadLocal<Long> startTime = new ThreadLocal<Long>();
    @Autowired
    private ObjectMapper mapper;
    public ControllerAspect() {
    }
    @Pointcut("execution(public * com.ldyms.controller.*.*(..))")
    public void webLog() {
    }
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Enumeration<String> enums = request.getParameterNames();
        ArrayList params = new ArrayList();
        while (enums.hasMoreElements()) {
            String paraName = enums.nextElement();
            String param = paraName + ":" + request.getParameter(paraName);
            params.add(param);
        }
        logger.info("request Url:{},Params:{}",(request.getRequestURL().toString()),params);
        logger.info("request Methods:{}", Arrays.toString(joinPoint.getArgs()));
    }
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        logger.info("RESPONSE: {} ", this.mapper.writeValueAsString(ret));
        logger.info("SPEND TIME: {} ms", System.currentTimeMillis() - startTime.get());
    }
}
目录
相关文章
|
2月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
406 0
|
1月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
6月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
1059 13
|
3月前
|
人工智能 监控 安全
Spring AOP切面编程颠覆传统!3大核心注解+5种通知类型,让业务代码纯净如初
本文介绍了AOP(面向切面编程)的基本概念、优势及其在Spring Boot中的使用。AOP作为OOP的补充,通过将横切关注点(如日志、安全、事务等)与业务逻辑分离,实现代码解耦,提升模块化程度、可维护性和灵活性。文章详细讲解了Spring AOP的核心概念,包括切面、切点、通知等,并提供了在Spring Boot中实现AOP的具体步骤和代码示例。此外,还列举了AOP在日志记录、性能监控、事务管理和安全控制等场景中的实际应用。通过本文,开发者可以快速掌握AOP编程思想及其实践技巧。
|
3月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
3月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
10月前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
525 6
|
9月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
639 25
|
9月前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
433 24
|
8月前
|
Java API 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——Spring Boot 中的 AOP 处理
本文详细讲解了Spring Boot中的AOP(面向切面编程)处理方法。首先介绍如何引入AOP依赖,通过添加`spring-boot-starter-aop`实现。接着阐述了如何定义和实现AOP切面,包括常用注解如`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@AfterReturning`和`@AfterThrowing`的使用场景与示例代码。通过这些注解,可以分别在方法执行前、后、返回时或抛出异常时插入自定义逻辑,从而实现功能增强或日志记录等操作。最后总结了AOP在实际项目中的重要作用,并提供了课程源码下载链接供进一步学习。
1012 0

热门文章

最新文章

下一篇
oss云网关配置