首页 > 其他分享 >Spring04_Aop

Spring04_Aop

时间:2023-04-15 20:11:16浏览次数:40  
标签:增强 UserServiceImpl .. 处理 Spring04 Aop AOP public

一、AOP 概述

(一)AOP简介

​ 面向切面编程是一种通过横切关注点(Cross-cutting Concerns)分离来增强代码模块性 的方法,它能够在不修改业务主体代码的情况下,对它添加额外的行为。

(二)为何需要AOP

​ 面向对象编程 OOP 可以通过对业务的分析,然后抽象出一系列具有一定属性与行为的类,并通过这些类之间的 协作来形成一个完整的软件功能。由于类可以继承,因此可以把具有相同功能或相同特性的属性抽象到一个层次分明 的类结构体系中。但是随着功能越来越复杂,难免暴露出一些问题。

​ 假设下面三个代码块A、B、C 中都有相同的部分 a,这个 a 通常是以类似复制粘贴的形式出现在这三个部分之中,但是一旦我们需要修改 a 的功能,就必须得把所有用到 a 的地方都修改一遍。

image-20230413203106986

​ 如果我们把 a 这部分设计成类似于 “方法” 的东西,再用得到他的地方进行 “调用” ,一旦需要修改我们只需要修改 a 这一个部分就可以,显然是更利于后期维护的。

但是在开发过程中甲方可能会不断的提要求,然后就得不断在 A、B、C 中增加新的逻辑,照上面说的那样我们就得写一个方法放在 “外面” ,然后再去 A、B、C 中去添加调用。

而 AOP 的设计思想就是只要我要增加新的方法,不需要在 A、B、C 中进行显示的调用,系统会自动在 A、B、C 中进行调用这个新的方法。

(三)AOP 名词

​ 切面(Aspect):被加入要执行的额外的新代码块。即切点和增强的组合

​ 连接点(Joinpoint):程序执行过程中明确的接入点,比如方法的调用,或者异常的抛出。在 Spring AOP 中,一个可以访问的方法就代表一个连接点。即可以进行增强的方法

​ 增强处理(Advice):AOP 框架在特定的切入点上执行的增强处理。增强处理有“around”、“before”、 “after”等类型。所谓的增强处理:就是在原代码不改动的情况下,额外多执行的那些代码。 即增加的额外功能

​ 切入点(Pointcut):可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。

​ 引入:将方法或字段添加到被处理的类中。也就是说将增强作用于切点的过程

​ 目标对象:被 AOP 框架进行增强处理的对象,也被称为增强对象。如果 AOP 框架采用的是动态 AOP 实现, 那么该对象就是一个被代理的对象。即真实对象

​ AOP 代理:AOP 框架创建的对象,简单地说,代理就是对目标对象的增强。即代理对象

​ 织入(Weaving):将增强处理添加到目标对象中,并创建一个被增强的对象的过程就是织入。织入有两种方式:编译时增强和运行时增强。(对应两种代理方式)

二、AOP 注解开发案例

​ 注解开发需要引入 AspectJ 的支持,需要将这个@Aspect 注解加入到 Spring 容器的过滤器中,那么扫描 包时才会将使用@Aspect 的 Bean 加入到 Spring 的容器中进行管理。

​ Spring1.X 版本采用自身提供的 AOP API 来定义切入点和增强处理,但目前这种方式已经过时。现在通常建议使用 AspectJ 方法来定义切入点和增强处理,也就是让 Spring 框架整合 AspectJ 框架,在这种方式下,Spring 依然有 “零配置” 和 “基于XML配置” 两种。

xmlns:aop="http://www.springframework.org/schema/aop"

http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd" >
<!-- 启动对 AspectJ 的支持 -->
<aop:aspectj-autoproxy />

<!--扫描包-->
<context:component-scan base-package="">
<!-- 使用 Spring 在自动扫描组件,需要让 Spring 容器识别 AspectJ 的注解,因此将 AspectJ 框架
的 org.aspectj.lang.annotation.Aspect 注解类加入到 Spring 的过滤器中
-->
<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>

(一)Before After 增强

​ 使用两个类来模拟用户注册时进行增强处理

public interface UserService {

    public void regist();
}

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void regist() {
        System.out.println("用户注册");
    }
}

@Aspect
public class UserServiceAspect {
    /**
     * 增强方法
     */
    @Before("execution(* com.qlu.service.impl.UserServiceImpl.*(..))")
    public void doBefore() {
        System.out.println("doBefore");
    }
}

​ MD,不要在这时候用实现类来创建接口,不然会报错 org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ' xxx.xxx.xx.xxx ' available。可以看这边解释,简单来说就是 Sping 在进行默认代理的时候是用的 JDK 代理,对实现类对象做增强得到的增强类和实现类是兄弟关系,所以不能用实现类接收增强类对象。当让你可以强制使用 CGLIB 就没事了。

@Test
public void testAop() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-beans.xml");
    //UserServiceImpl userService = applicationContext.getBean(UserServiceImpl.class);
    UserService userService = applicationContext.getBean(UserService.class);
    userService.regist();
}

image-20230415165055254

​ 可以更改注解的参数值来达成对什么方法进行增强,以及之前还是之后增强即前置增强和后置增强。

标识符 含义
execution() 表达式的主体
第一个 " * " 号 表示返回值是任意类型
com.qlu.service.impl.UserServiceImpl AOP所切的服务的包名,即需要进行横切的业务类
包名后面的“..” 表示当前包及子包
第二个 " * " 表示类名,*即所有类
.*(..) 表示任何方法名,括号表示参数,两个点表示任何参数类型
//作用于 com.qlu.service.impl.UserServiceImpl 类
@Before("execution(* com.qlu.service.impl.UserServiceImpl.*(..))")
//作用于 com.qlu.service.impl 包及其子包的所有类型方法
@After("execution(* com.qlu.service.impl..*(..))")

(二)AfterReturning 和 AfterThrowing

​ 使用 @After 进行增强即使执行切点的原来方法有异常抛出,后置增强依旧会执行,如果使用 @AfterReturning 则会被异常阻止;

image-20230415195013000

​ 如果程序不出意外的话这两个注解的效果是一样的;

image-20230415195247800

​ 另外如果我们想把这个异常捕获处理的话可以使用 @AfterThrowing 注解。

/** 对异常的处理 */
@AfterThrowing(value = "execution(* com.qlu.service.impl.UserServiceImpl.*(..))",throwing="ex")
public void handleException(Throwable ex) {
    System.out.println("异常信息:"+ex.getMessage());
    System.out.println("---------------将异常信息记录到数据库或日志文件中---------------");
}

image-20230415195658649

(三)Around 增强

​ 可以使用 @Around 进行环绕增强,顾名思义就是在切点的前后都进行增强。需要借助于 ProceedingJoinPoint 这个类,方法的参数是一个切点,通过 point.getArgs() 获得切点的参数,point.proceed(args) 执行切点的原本方法。

@Around("execution(* com.qlu.service.impl.UserServiceImpl.*(..))")
    public void doAround(ProceedingJoinPoint point) throws Throwable {
        System.out.println("环绕增强执行之前");
        Object[] args = point.getArgs();
        Object result = point.proceed(args);
        System.out.println(result);
        System.out.println("环绕增强执行之后");
    }

image-20230415194019935

(四)执行的先后顺序

​ Spring AOP 采用和 AspectJ 一样的优先顺序来织入增强处理:在“进入”连接点时,具有最高优先级的增强处理将先被织入,在“退出”连接点时,具有最高优先级的增强处理会最后被织入。

​ 当不同切面里的两个相同增强处理需要在同一个接入点被织入时,Spring AOP 将以随机的顺序来织入这两个增强处理。

标签:增强,UserServiceImpl,..,处理,Spring04,Aop,AOP,public
From: https://www.cnblogs.com/purearc/p/17321758.html

相关文章