Spring AOP介绍与使用

1、 AOP 的概述

AOP:Aspect Oriented Programming 面向切面编程

OOP:Object Oriented Programming 面向对象编程

面向切面编程:基于OOP基础之上新的编程思想,OOP面向的主要对象是类,而AOP面向的主要对象是切面,在处理日志、安全管理、事务管理等方面有非常重要的作用。AOP是Spring中重要的核心点,虽然IOC容器没有依赖AOP,但是AOP提供了非常强大的功能,用来对IOC做补充。通俗点说的话就是在程序运行期间。

在不修改原有代码的情况下 增强跟主要业务没有关系的公共功能代码之前写好的方法中的指定位置 这种编程的方式叫AOP。

从通俗易懂且不失风趣的角度来讲:(来自武哥文章谈谈Spring中的IOC和AOP概念

面向切面编程的目标就是分离关注点。什么是关注点呢?就是你要做的事,就是关注点。假如你是个公子哥,没啥人生目标,天天就是衣来伸手,饭来张口,整天只知道玩一件事!那么,每天你一睁眼,就光想着吃完饭就去玩(你必须要做的事),但是在玩之前,你还需要穿衣服、穿鞋子、叠好被子、做饭等等等等事情,这些事情就是你的关注点,但是你只想吃饭然后玩,那么怎么办呢?这些事情通通交给别人去干。在你走到饭桌之前,有一个专门的仆人A帮你穿衣服,仆人B帮你穿鞋子,仆人C帮你叠好被子,仆人C帮你做饭,然后你就开始吃饭、去玩(这就是你一天的正事),你干完你的正事之后,回来,然后一系列仆人又开始帮你干这个干那个,然后一天就结束了!
AOP的好处就是你只需要干你的正事,其它事情别人帮你干。也许有一天,你想裸奔,不想穿衣服,那么你把仆人A解雇就是了!也许有一天,出门之前你还想带点钱,那么你再雇一个仆人D专门帮你干取钱的活!这就是AOP。每个人各司其职,灵活组合,达到一种可配置的、可插拔的程序结构。
从Spring的角度看,AOP最大的用途就在于提供了事务管理的能力。事务管理就是一个关注点,你的正事就是去访问数据库,而你不想管事务(太烦),所以,Spring在你访问数据库之前,自动帮你开启事务,当你访问数据库结束之后,自动帮你提交/回滚事务!

AOP的底层用的代理,代理是一种设计模式。

代理的笔记可以看一下:https://blog.csdn.net/weixin_42128429/article/details/118709159

2、AOP相关概念

2.1、AOP核心概念和术语

  • 切面(Aspect): 指关注点模块化,这个关注点可能会横切多个对象。事务管理是企业级Java应用中有关横切关注点的例子。 在Spring AOP中,切面可以使用通用类基于模式的方式(schema-based approach)或者在普通类中以@Aspect注解(@AspectJ 注解方式)来实现。
  • 连接点(Join point): 在程序执行过程中某个特定的点,例如某个方法调用的时间点或者处理异常的时间点。在Spring AOP中,一个连接点总是代表一个方法的执行。
  • 通知(Advice): 在切面的某个特定的连接点上执行的动作。通知有多种类型,包括“around”, “before” and “after”等等。通知的类型将在后面的章节进行讨论。 许多AOP框架,包括Spring在内,都是以拦截器做通知模型的,并维护着一个以连接点为中心的拦截器链。
  • 切点(Pointcut): 匹配连接点的断言。通知和切点表达式相关联,并在满足这个切点的连接点上运行(例如,当执行某个特定名称的方法时)。切点表达式如何和连接点匹配是AOP的核心:Spring默认使用AspectJ切点语义。
  • 引入(Introduction): 声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被通知的对象上。例如,可以使用引入来使bean实现 IsModified接口, 以便简化缓存机制(在AspectJ社区,引入也被称为内部类型声明(inter))。
  • 目标对象(Target object): 被一个或者多个切面所通知的对象。也被称作被通知(advised)对象。既然Spring AOP是通过运行时代理实现的,那么这个对象永远是一个被代理(proxied)的对象。
  • AOP代理(AOP proxy):AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
  • 织入(Weaving): 把切面连接到其它的应用程序类型或者对象上,并创建一个被被通知的对象的过程。这个过程可以在编译时(例如使用AspectJ编译器)、类加载时或运行时中完成。 Spring和其他纯Java AOP框架一样,是在运行时完成织入的。

Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field.
Target(目标对象):代理的目标对象
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装在期织入
Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合

 

2.2、AOP的通知类型

  • 前置通知(Before advice): 在连接点之前运行但无法阻止执行流程进入连接点的通知(除非它引发异常)。
  • 后置返回通知(After returning advice):在连接点正常完成后执行的通知(例如,当方法没有抛出任何异常并正常返回时)。
  • 后置异常通知(After throwing advice): 在方法抛出异常退出时执行的通知。
  • 后置通知(总会执行)(After (finally) advice): 当连接点退出的时候执行的通知(无论是正常返回还是异常退出)。
  • 环绕通知(Around Advice):环绕连接点的通知,例如方法调用。这是最强大的一种通知类型,。环绕通知可以在方法调用前后完成自定义的行为。它可以选择是否继续执行连接点或直接返回自定义的返回值又或抛出异常将执行结束。

2.3、AOP使用场景


AOP用来封装横切关注点,具体可以在下面的场景中使用:

Authentication 权限

Caching 缓存

Context passing 内容传递

Error handling 错误处理

Lazy loading 懒加载

Debugging 调试

logging, tracing, profiling and monitoring 记录跟踪 优化 校准

Performance optimization 性能优化

Persistence 持久化

Resource pooling 资源池

Synchronization 同步

Transactions 事务

3、Spring AOP的简单配置(基于注解)

        3.1、在ioc的基础上添加pom依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

        3.2、编写目标类并配置

  • 给UserServiceImpl添加@Service注解
  • 添加自动扫描的配置
package com.tuling.service.impl;

import com.tuling.dao.IUserDao;
import com.tuling.entity.User;
import com.tuling.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author fanzitianxing
 * @title: UserServiceImpl
 * @projectName spring_aop
 * @description: TODO
 * @date 2021/6/2613:39
 */
@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    IUserDao userDao;
    public User select(Integer id) throws Exception {
        System.out.println("查询user");
        return userDao.select(id);
    }

    public void add(User user) throws Exception {
        System.out.println("新增user");
        userDao.add(user);
    }

    public void update(User user) throws Exception {
        System.out.println("更新user");

        userDao.update(user);
    }

    public void delete(Integer id) throws Exception {
        System.out.println("删除user");
        userDao.delete(id);
    }
}
<!--扫描注解的包-->
    <context:component-scan base-package="com.tuling"></context:component-scan>

        3.3.编写切面类并配置

  • 在LogAspect 中@Component注解
package com.tuling.aspects;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @author fanzitianxing
 * @title: LogAspect
 * @projectName spring_aop
 * @description: TODO
 * @date 2021/6/2613:51
 */
@Component
public class LogAspect {
    public void before(){
        System.out.println("前置通知");
    }
    //后置通知
    public void after(){
        System.out.println("后置通知");
    }
    //后置异常通知
    public void afterThrowing(){
        System.out.println("后置异常通知");
    }
    //后置返回通知
    public void afterReturning(){
        System.out.println("后置返回通知");
    }

}

        3.4、使用注解的AOP对象目标类进行增强

            1、在配置文件中打开注解的AOP开发

<!--启动aop的注解方式的使用-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

              2、在切面类上使用注解
                在类上使用@Aspect注解代表这是一个切面类
                在方法上注入属性@Before(execution表达式)代表前置增强

package com.tuling.aspects;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @author fanzitianxing
 * @title: LogAspect
 * @projectName spring_aop
 * @description: TODO
 * @date 2021/6/2613:51
 */
@Aspect  //标记为切面
@Component //标记为bean组件,才能切入到ioc当中的bean
public class LogAspect {
    /*
   设置下面方法在什么时候运行
       @Before:在目标方法之前运行:前置通知
       @After:在目标方法之后运行:后置通知
       @AfterReturning:在目标方法正常返回之后:返回通知
       @AfterThrowing:在目标方法抛出异常后开始运行:异常通知
       @Around:环绕:环绕通知

       当编写完注解之后还需要设置在哪些方法上执行,使用表达式
       execution(访问修饰符  返回值类型 方法全称)
    */
    //前置通知
    @Before("execution(* com.tuling.service.impl..*.*(..))")
    public void before(){
        System.out.println("前置通知");
    }
    //后置通知
    @After("execution(* com.tuling.service.impl..*.*(..))")
    public void after(){
        System.out.println("后置通知");
    }
    //后置异常通知
    @AfterThrowing("execution(* com.tuling.service.impl..*.*(..))")
    public void afterThrowing(){
        System.out.println("后置异常通知");
    }
    //后置返回通知
    @AfterReturning("execution(* com.tuling.service.impl..*.*(..))")
    public void afterReturning(){
        System.out.println("后置返回通知");
    }
    //环绕通知

}

         3.5编写测试类

package com.tuling.tests;

import com.tuling.service.IUserService;
import com.tuling.service.impl.UserServiceImpl;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author fanzitianxing
 * @title: SpringTest
 * @projectName spring_aop
 * @description: TODO
 * @date 2021/6/2613:48
 */
public class SpringTest {
    ClassPathXmlApplicationContext ioc;
    @Before
    public void before(){
        ioc = new ClassPathXmlApplicationContext("classpath:spring-aop.xml");
    }
    @Test
    public void test01() throws Exception {
        IUserService bean = ioc.getBean(IUserService.class);
        bean.select(1);
    }

}

可以打印一下ioc得到的bean的class

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值