首页技术栈归档照片墙音乐日记随想收藏夹友链留言关于

9.AOP(面向切面编程)

写作时间:2026-07-08

9.AOP(面向切面编程)

什么是aop

AOP:Aspect oriented programming 面向切面编程,AOP 是 OOP(面向对象编程)的一种延续。

AOP 为什么叫面向切面编程

切 :指的是横切逻辑,原有业务逻辑代码不动,只能操作横切逻辑代码,所以面向横切逻辑

面 :横切逻辑代码往往要影响的是很多个方法,每个方法如同一个点,多个点构成一个面。这里有一个面的概念

AOP在Spring 的作用

AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,和 OOP(面向对象编程)类似,也是一种编程思想。

AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。AOP 就是代理模式的典型应用。

目前最流行的 AOP 框架有两个,分别为 Spring AOP 和 AspectJ。

Spring AOP 是基于 AOP 编程模式的一个框架,它能够有效的减少系统间的重复代码,达到松耦合的目的。Spring AOP 使用纯 Java 实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。有两种实现方式:基于接口的 JDK 动态代理和基于继承的 CGLIB 动态代理。

AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。

提供声明式事务;允许用户自定义切面

·横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等....

·切面(ASPECT):横切关注点被模块伐的特殊对象。即,它是一个类。

·通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

·目标(Target)︰被通知对象。

·代理(Proxy)︰向目标对象应用通知之后创建的对象。

·切入点(PointCut)︰切面通知执行的“地点"的定义。

·连接点(JointPoint) :与切入点匹配的执行点。

导入依赖

      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.4</version>
      </dependency>

AOP术语

名称 说明
Joinpoint(连接点) 指那些被拦截到的点,在 Spring 中,指可以被动态代理拦截目标类的方法。
Pointcut(切入点) 指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。
Advice(通知) 指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。
Target(目标) 指代理的目标对象。
Weaving(植入) 指把增强代码应用到目标上,生成代理对象的过程。
Proxy(代理) 指生成的代理对象。
Aspect(切面) 切入点和通知的结合。

Advice 直译为通知,也有的资料翻译为“增强处理”,共有 5 种类型,如下表所示。

通知 说明
before(前置通知) 通知方法在目标方法调用之前执行
after(后置通知) 通知方法在目标方法返回或异常后调用
after-returning(返回后通知) 通知方法会在目标方法返回后调用
after-throwing(抛出异常通知) 通知方法会在目标方法抛出异常后调用
around(环绕通知) 通知方法会将目标方法封装起来

aop元素

AOP 是 Spring 的核心之一,在 Spring 中经常会使用 AOP 来简化编程。在 Spring 框架中使用 AOP 主要有以下优势。

  • 提供声明式企业服务,特别是作为 EJB 声明式服务的替代品。最重要的是,这种服务是声明式事务管理。
  • 允许用户实现自定义切面。在某些不适合用 OOP 编程的场景中,采用 AOP 来补充。
  • 可以对业务逻辑的各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时也提高了开发效率。

方式一:使用Spring API接口【主要Spring API接口实现】

applicationContext.xml

引入AOP

      xmlns:aop="http://www.springframework.org/schema/aop"
      http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd

实现

  <!--注册bean-->
      <bean id="afterLog" class="com.yuan.log.AfterLog"/>
      <bean id="log" class="com.yuan.log.Log"/>
      <bean id="userSericeimpl" class="com.yuan.service.UserSericeimpl"/>

    <!--配置AOP,需要导入aop约束-->
    <!--方式一使用原生的apl-->
     <app:config>
         <!--切入点: expression:表达式,execution(要执行的位置!* 代表所有 ..代表任意参数) -->
          <app:pointcut id="pointcut" expression="execution(* com.yuan.service.UserSericeimpl.*(..))"/>

         <!--执行环绕增加-->
         <app:advisor advice-ref="log" pointcut-ref="pointcut"/>
         <app:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
     </app:config>

log包下两个类:AfterLog,Log

public class AfterLog implements AfterReturningAdvice {
    //returnValue返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName() + "方法,返回的结果为:"+returnValue);
    }
}
//method:要执行的目标对象的方法
//args:参数
//target:目标对象
public class Log implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName() + "被执行了");
    }
}

service:接口,实现类

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}
public class UserSericeimpl implements UserService{
    public void add() {
        System.out.println("实现了一个新增方法");
    }
    public void delete() {
        System.out.println("实现了一个删除方法");
    }
    public void update() {
        System.out.println("实现了一个修改方法");
    }
    public void select() {
        System.out.println("实现了一个查询方法");
    }
}

测试类

public class Test {
    public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
       //动态代理的是接口
       UserService bean = context.getBean("userSericeimpl", UserService.class);
       bean.delete();
    }
}

方式二:使用使用自定义类【主要是切片定义】

diy包类:DiyPointCut

public class DiyPointCut {
    public void before(){
        System.out.println("====方法执行前====");
    }
    public void after() {
        System.out.println("====方法执行后====");
    }
}

applicationContext.xml

    <!--方式二,自定义类-->
    <bean id="diy" class="com.yuan.diy.DiyPointCut"/>
    <aop:config>
        <!--自定义切片ref要引入的类-->
        <app:aspect ref="diy">
            <!--切入点-->
            <app:pointcut id="poin" expression="execution(* com.yuan.service.UserSericeimpl.*(..))"/>
            <!--method:方法名称
            pointcut-ref:自定义的id名称-->
            <app:before method="before" pointcut-ref="poin"/>
            <app:after method="after" pointcut-ref="poin"/>
        </app:aspect>
    </aop:config>

测试类

public class Test {
    public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
       //动态代理的是接口
       UserService bean = context.getBean("userSericeimpl", UserService.class);
       bean.delete();
    }
}

方式三:使用注解实现

Annotation 注解介绍

名称 说明
@Aspect 用于定义一个切面。
@Pointcut 用于定义一个切入点。
@Before 用于定义前置通知,相当于 BeforeAdvice。
@AfterReturning 用于定义后置通知,相当于 AfterReturningAdvice。
@Around 用于定义环绕通知,相当于MethodInterceptor。
@AfterThrowing 用于定义抛出通知,相当于ThrowAdvice。
@After 用于定义最终final通知,不管是否异常,该通知都会执行。
@DeclareParents 用于定义引介通知,相当于IntroductionInterceptor(不要求掌握)。

自定义diy包的类:AnnotationPointcut

/*
   方式三,使用注解实现
 */
@Aspect //标注这个类是一个切片
public class AnnotationPointcut {
    @Before("execution(* com.yuan.service.UserSericeimpl.*(..))")
    public void before(){
        System.out.println("=====方法执行前=====");
    }
    @After("execution( * com.yuan.service.UserSericeimpl.*(..))")
    public void after() {
        System.out.println("=====方法执行后=====");
    }

第二种

package com.yuan.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
/*
使用注解实现
 */
@Aspect //标注这个类是一个切片
public class AnnotationPointcut {
    @Pointcut("execution(* com.yuan.service.UserService..*(..))")
    public void pointcut(){

    }
    @Before("pointcut()")
    public void before(){
        System.out.println("=====方法执行前=====");
    }
    @After("pointcut()")
    public void after() {
        System.out.println("=====方法执行后=====");
    }

实现环绕增强

    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.yuan.service.UserSericeimpl.*(..))")
    public void around(ProceedingJoinPoint pj) throws Throwable {
        System.out.println("环绕前");
        Object object=pj.proceed();        //执行方法
        System.out.println("环绕后");
        System.out.println(object);
        Signature signature = pj.getSignature();//获得签名
        System.out.println("signature"+signature);
    }
}

applicationContext.xml

    <!--方式三-->
    <bean id="annotationPointcut" class="com.yuan.diy.AnnotationPointcut"/>
    <!--开启注解支持 JDK(默认 proxy-target-cLass="false") cgLib.-->
    <app:aspectj-autoproxy/>

测试类

public class Test {
    public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
       //动态代理的是接口
       UserService bean = context.getBean("userSericeimpl", UserService.class);
       bean.delete();
    }
}
avatar

yuanyourdomain

写代码,做研究,记录生活。

RECOMMENDED

MyBatis 动态 SQL

2026-07-08

Nginx 基础入门

2026-07-08

Maven 多模块与私服

2026-07-08