首页 > 其他分享 >Spring04_Aop

Spring04_Aop

时间:2023-04-16 17:13:17浏览次数:41  
标签:增强 void Spring04 Aop AOP println public out

一、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 将以随机的顺序来织入这两个增强处理。

​ 如果应用需要指定不同切面类里增强处理的优先级,Spring 提供了如下两个解决方案

​ 1、让切面类实现 org.springframework.core.Ordered 接口,该接口声明了一个 int getOrder()方法,该方法的返回值越小,则优先级越高。

​ 2、直接使用@Order 注解来修饰一个切面类,该注解中含有一个 int 类型的 value 属性,该属性值越小,优先级越高。

三、AOP XML开发

​ 除了使用 Annotation 注解方式来定义切面、切入点和增强处理外,Spring AOP 也允许直接使用 XML 配置文件来定义管理它们。使用 XML 形式的开发需要理解切点、切面、连接点这几个概念。下面使用一个简单案例模拟 Aop 的 xml 形式开发。

​ 用到的有 service 接口和他的实现类、aop 增强类、和用于测试的 junit4 生成的

package com.qlu.service;

public interface BookService {
    void add();
    void delete();
    void update();
    void find();
}

package com.qlu.service.impl;

import com.qlu.service.BookService;

public class BookServiceImpl implements BookService {
    @Override
    public void add() {
        System.out.println("增加");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }

    @Override
    public void update() {
        System.out.println("更新");
    }

    @Override
    public void find() {
        System.out.println("查找");
    }
}


//增强类使用hutool工具包来获取的当前时间
package com.qlu.aop;

import cn.hutool.core.date.DateUtil;

/**
 * 增强类
 */
public class BookServiceAop {
    public void doBefore() {
        System.out.println(DateUtil.now());
    }
    
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        System.out.println("Before  " + DateUtil.now());
        Object result = point.proceed(point.getArgs());
        System.out.println("After  " + DateUtil.now());
        return result;
    }
}

​ 重点是下面的 xml 文件配置

​ 首先要使用 spring 框架那肯定得把对象放到 ioc 容器里面,考虑到 aop 用的 xml 开发,索性配置 bean 也直接用 xml了。

​ 然后进行 aop-config ,里面有两个标签,一个是 aop-pointcut,也就是说你让谁成为切点,即想对哪个方法进行加强;

​ 另一个是aop-aspect 也就是切面,标签内使用 ref 指向编写的增强类,这样才能让 spring 找到增强类里面的方法;

​ aop-aspect里面放的是你写的增强方法,具体的 before、after、around 由自己配置,pointcut-ref 就是要把这个增强作用到哪个切点。

    <bean id="bookService" class="com.qlu.service.impl.BookServiceImpl"></bean>
    <bean id="bookServiceAop" class="com.qlu.aop.BookServiceAop"></bean>

    <aop:config>
<!--定义切点-->
       <!-- <aop:before method="doBefore" pointcut-ref="bookServicePointcut"></aop:before>-->
            <aop:around method="doAround" pointcut-ref="bookServicePointcut"></aop:around>
<!--切面-->
        <aop:aspect ref="bookServiceAop">
            <aop:before method="doBefore" pointcut-ref="bookServicePointcut"></aop:before>
        </aop:aspect>
    </aop:config>

image-20230416165642398

image-20230416165720731

​ 自然其他注解支持的 xml 肯定也支持,就不做过多演示了。

标签:增强,void,Spring04,Aop,AOP,println,public,out
From: https://www.cnblogs.com/purearc/p/17323590.html

相关文章

  • Spring AOP demo
    动态代理模式实现,比如可以在Bean的生命周期创建阶段,根据Pointcut判断当前bean是否满足切入条件,如果满足,再根据织入器ProxyFactory织入到JoinPoint,再根据bean创建代理对象名词JoinPoint:可以理解成系统中每一个可以应用aop的点,一般是指方法。spring中只支持方法,Pointcut:根据P......
  • Spring04_Aop
    一、AOP概述(一)AOP简介​ 面向切面编程是一种通过横切关注点(Cross-cuttingConcerns)分离来增强代码模块性的方法,它能够在不修改业务主体代码的情况下,对它添加额外的行为。(二)为何需要AOP​ 面向对象编程OOP可以通过对业务的分析,然后抽象出一系列具有一定属性与行为的类,并通......
  • springboot整合阿里云OSS实现多线程下文件上传(aop限制文件大小和类型)
    内容涉及:springboot整合阿里云oss自定义注解及aop的使用:对上传文件格式(视频格式、图片格式)、不同类型文件进行大小限制(视频和图片各自自定义大小)线程池使用:阿里云OSS多线程上传文件阿里云OSS分片上传大文件 业务需求需求一:前端传递单个或多个小文件(这里......
  • 自定义aop
    自定义aopAspectJ应该算的上是Java生态系统中最完整的AOP框架了。SpringAOP和AspectJAOP有什么区别?SpringAOP属于运行时增强,而AspectJ是编译时增强。SpringAOP基于代理(Proxying),而AspectJ基于字节码操作(BytecodeManipulation)。SpringAOP已经集成......
  • AOP底层原理-Cglib动态代理
      publicclassApp{publicstaticvoidmain(String[]args){UserServiceuserService=UserServiceCglibProxy.createUserServiceCglibProxy(UserServiceImpl.class);userService.save();}}publicclassUserServiceCglibProxy{......
  • AOP底层原理-装饰模式(静态代理)
    原始方法:静态代理的方法:   测试:  ......
  • AOP配置(XML)-通知中获取异常对象
     ......
  • AOP配置(XML)-通知中获取参数
        获取参数方式2: ......
  • AOP配置(XML)-五种通知类型配置
         ......
  • Spring 源码解析 --AOP
        ......