【AOP】Spring AOP基础 + 实践 完整记录

简介: Spring AOP的基础概念 ============================================================= AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角.在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)。

 

Spring AOP的基础概念

 

=============================================================

 AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角.
在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)。

==============================================================

基础概念图:

 

有了上面这张原理图,那么关于AOP面向切面编程的核心几个概念,就可以顺利铺开了:

1》join point【连接点】

在spring aop中,认为原有代码中所有的方法都是join point。

2》point cut【切点】

在不改变原有代码的情况下,想多干点事情,那就需要定义point cut,切点。切点的任务是通过一组表达式来匹配要在哪个join point切入,并且匹配要在这个join point的什么位置切入。

3》advice【增强逻辑】

根据1,2切入了原有代码后,要做些什么事情?这多做的事情就是advice,也就是多处理的一些逻辑。比如,你的原有方法是对数据的保存方法,项目交付后,新需求是需要将这些保存操作在日志中记录下来,并且不能更改原有代码,这就是增强逻辑。

advice增强逻辑你是准备放在原有代码之前还是之后,有以下几种:

  • before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)

  • after return advice, 在一个 join point 正常返回后执行的 advice

  • after throwing advice, 当一个 join point 抛出异常后执行的 advice

  • after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.

  • around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.

4》weaving【织入】

现在有了原有代码,有了新的增强逻辑。将这两部分代码连接在一起的过程,就是织入weaving。

根据不同的实现技术, AOP织入有三种方式:

    • 编译器织入, 这要求有特殊的Java编译器.

    • 类装载期织入, 这需要有特殊的类装载器.

    • 动态代理织入, 在运行期为目标类添加增强(Advice)生成子类的方式.
      Spring 采用动态代理织入, 而AspectJ采用编译器织入和类装载期织入.

5》Target【目标对象】

原有代码和增强逻辑织入在一起,重新生成的就是目标对象Target,也叫adviced object.

Spring Aop使用运行时代理的方式实现Aspect,因此adviced object是一个代理对象。

在 Spring AOP 中, 一个 AOP 代理是一个 JDK 动态代理对象或 CGLIB 代理对象。】

注意, adviced object 指的不是原来的类, 而是织入 advice 后所产生的代理类.

 【关于java中代理的概念,类型,区别和理解:

  https://www.cnblogs.com/hongcong/p/5806024.html

  https://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

  可以去看上面两篇文章,我自己还没有心思去研究。

  但我记住了一句话:CGLIB方式不能代理final类。也就是说Spring AOP的切口不能切在final类上了

  】

6》aspect【切面】

aspect切面,是由point cut和advice组合而成的。既包含了连接点也就是切点的定义,也有增强逻辑的具体实现。

 

===========================================

到这里,一个概念就顺利的出来了:Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中.

===========================================

 

Spring Aop的实现和使用的各种情况

 

=======================================================

要在Spring中通过注解方式使用AOP,需要下面两步:

1》在配置文件中配置

   <!-- 自动扫描注解 -->
    <context:component-scan base-package="com.sxd" />
    <!--Spring aop 使用注解的方式-->
    <aop:aspectj-autoproxy />

 

2》定义切面【aspect】

  切面包括 切点【point cut】 和  增强逻辑【advice】

  【下面aspect中已经显示了point cut的各种定义表达式   和  各种类的advice】

  【具体使用,应该按照实际使用逻辑选择性使用即可!!!】

package com.sxd.aop;

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

import java.util.Objects;


/**
 * 切面定义
 * ①在类上定义@Aspect和@Component
 * ②@Pointcut()定义切点
 * ③adive方法上,声明在哪一个切点上切入 加入增强逻辑
 */
@Aspect
@Component
public class Aspects {

    /**
     * execution()    表达式:用来匹配执行方法的连接点
     * 【..(两个点)代表零个或多个】
     * 【本例子中  第一个*代表方法的返回值类型可以为任何类型 如果本方法为void,也符合切入条件】
     * 【本例子中  第一个..代表controller包以及子包下】
     * 【本例子中  第二个*代表这个包下的任意类】
     * 【本例子中  第三个*代表这个包下的任意类中的任意方法】
     * 【本例子中  第二个..代表有无参数都可以被切入】
     *
     * args     如果想要切入的方法的参数要符合什么类型的话
     * 【本例子代表入参中,第一个参数类型需要为String类型的才会被切入,之后有零个入参或多个入参】
     *
     */
    @Pointcut(value = "execution(* com.sxd.controller..*.*(..)) && args(String,..)")
    public void pointCut1(){}


    /**
     * within()     表达式用于匹配这个包下的任意类
     */
    @Pointcut(value = "within(com.sxd.controller.*)")
    public void pointCut2(){}

    /**
     * this()       表达式限定了匹配这个类的实例下的方法
     */
    @Pointcut(value = "this(com.sxd.controller.MainController)")
    public void pointCut3(){}

    /**
     * bean()       匹配IOC容器中的bean的名称
     */
    @Pointcut(value = "bean(memberService)")
    public void pointCut4(){}












    /**
     * 下面是各种advice的展示
     *
     * 1》advice的执行优先级:  around方法执行前》before》【方法自己】》around方法执行后》after》afterReturning
     * 2》若有异常
     * advice的执行优先级:around方法执行前》before》【方法自己】》around方法执行后》after》afterReturning
     * 很奇怪为什么两次都是一样的执行优先级,为什么没有进afterThrowing().因为使用了around。
     * 3》注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用!
     *
     * 4》注意:在切面的advice里面,一定不要让异常抛出去,影响原方法的执行和返回。
     * 5》JoinPoint 即原方法的入参
     * 6》returning即原方法的返回值
     * 7》如果原方法没有返回值,而这里的advice定义了returning,即使pointCut可以匹配上切点,也不会切入原方法
     */


    @Before("pointCut1()")
    public void justBefore(JoinPoint joinPoint){
        System.out.println("切入方法前");
    }


    @After("pointCut1()")
    public void justAfter(JoinPoint joinPoint){
       Object[] arr = joinPoint.getArgs();
       if(Objects.nonNull(arr) && arr.length >0){
           System.out.println((String)arr[0]);
           System.out.println((Integer)arr[1]);
       }
        System.out.println("切入方法后");
    }

    @AfterReturning(pointcut = "pointCut1()",returning = "returnVal")
    public  void justAfterReturn(JoinPoint joinPoint,Object returnVal){
        System.out.println(returnVal.toString());
        System.out.println("在方法执行完,并未抛异常,能正确返回值的情况下,在返回值之前切入");
    }

    @AfterThrowing(pointcut = "pointCut1()",throwing = "err")
    public  void justAfterThrow(JoinPoint joinPoint,Throwable err){
        System.out.println("在方法执行,抛异常的情况下,切入");
    }

    @Around("pointCut1()")
    public Object justAround(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("环绕型切入,方法执行前");
        Object a = null;
        try {
             a =  proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕型切入,方法执行后");
        return  a;
    }

}

 

 

3》上面两步 就把aop写完了 ,最后要测试一下各种情况

package com.sxd.controller;

        import org.springframework.stereotype.Controller;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MainController {

    @ResponseBody
    @RequestMapping("do")
    public String doSomething(){
        return "无参";
    }

    @ResponseBody
    @RequestMapping("do2")
    public String doSomething2(String a){
        return "有参"+a;
    }

    @ResponseBody
    @RequestMapping("do3")
    public String doSomething3(Integer a,String b){ return "整数"+a+"---字符串"+b; }

    @ResponseBody
    @RequestMapping("do4")
    public String doSomething4(String a,Integer b){
        return "字符串"+a+"---整数"+b;
    }

    @ResponseBody
    @RequestMapping("do5")
    public String doSomething5(String a,Integer b) throws Throwable{
        return "字符串转成数字"+Integer.parseInt(a)+"---整数"+b;
    }

    @ResponseBody
    @RequestMapping("do6")
    public void doSomething6(){
        System.out.println("无返回值的");
    }
}
View Code

发请求  测试即可。

 

相关文章
|
1月前
|
监控 Java 开发者
Spring AOP动态代理
Spring AOP动态代理
43 1
|
1月前
|
缓存 Java API
【云原生】Spring Cloud Gateway的底层原理与实践方法探究
【云原生】Spring Cloud Gateway的底层原理与实践方法探究
|
1月前
|
Java Spring 容器
Spring的AOP失效场景详解
Spring的AOP失效场景详解
112 0
|
30天前
|
设计模式 Java Maven
Spring Aop 底层责任链思路实现-springaopdi-ceng-ze-ren-lian-si-lu-shi-xian
Spring Aop 底层责任链思路实现-springaopdi-ceng-ze-ren-lian-si-lu-shi-xian
35 1
|
2月前
|
XML Java 数据格式
5个点轻松搞定Spring AOP底层实现原理
AOP 也是 Spring 中一个较为重要的内容,相对于传统的 OOP 模式,AOP 有很多让人难以理解的地方,本篇文章将向大家介绍 AOP 的实现方法及其底层实现,内容包括:
47 1
|
13天前
|
监控 Java 数据库连接
Spring高手之路17——动态代理的艺术与实践
本文深入分析了JDK和CGLIB两种动态代理技术在Spring框架中的应用。讨论了动态代理的基础概念,通过实例展示了如何实现和应用这两种方法,并比较了它们的性能差异及适用场景。进一步,探讨了在动态代理中实现熔断限流和日志监控的策略,以及如何利用动态代理优化Spring应用的设计和功能。
28 6
Spring高手之路17——动态代理的艺术与实践
|
23天前
|
XML Java Maven
Spring之Aop的注解使用
Spring之Aop的注解使用
|
29天前
|
Java Spring
Spring 如何实现 AOP
Spring 如何实现 AOP
17 0
|
1月前
|
Java 编译器 程序员
Spring AOP 和 AspectJ 的比较
Spring AOP 和 AspectJ 的比较
37 0
|
1月前
|
Java Spring
【spring(三)】AOP总结
【spring(三)】AOP总结