springAop原理分析-动态代理对象创建过程分析

举报
ksh1998 发表于 2022/09/25 05:12:22 2022/09/25
【摘要】 目录 概念AspectJ支持的切入点指示符 Aop 动态代理工厂类图Aop 动态代理对象创建过程IOC部分AOP部分 实战AOP动态动态代理对象配置类JDKCGL...

概念

AspectJ

  1. Aspect 切面(由多个切点组成,多个点组成面)

启用@AspectJ支持后,Spring 会自动检测出在应用程序上下文中定义的任何 Bean,如下使用@Aspect 定义的一个切面示例。

package org.xyz;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. Pointcut 切点(被切入的方法)

切入点决定了连接兴趣点,从而使我们能够控制建议的运行时间。Spring AOP 仅支持 Spring bean 的方法执行连接点,因此您可以将切入点视为与Spring bean 上的方法执行相匹配。切入点声明由两部分组成:一个由名称和任何参数组成的签名,以及一个切入点表达式,该表达式准确确定我们感兴趣的方法执行。在 AOP 的@AspectJ注释样式中,切入点签名由常规方法定义提供,切入点表达式通过使用注释进行指示(用作切入点签名的方法必须具有返回类型)。@Pointcut``void

一个示例可能有助于明确切入点签名和切入点表达式之间的区别。下面的示例定义了一个名为 的切入点,该切入点与任何名为 的方法来执行相匹配:anyOldTransfer``transfer

支持的切入点指示符

弹簧 AOP 支持以下 AspectJ 切入点指示符 (PCD), 用于切入点表达式:

  • execution:用于匹配方法执行连接点。这是使用弹簧 AOP 时要使用的主要切入点指示符。
  • within:限制匹配以连接特定类型中的点(使用Spring AOP时执行在匹配类型中声明的方法)。
  • this:限制与连接点的匹配(使用弹簧 AOP 时的方法的执行),其中 Bean 引用(弹簧 AOP 代理)是给定类型的实例。
  • target:限制与连接点的匹配(使用Spring AOP时的方法执行),其中目标对象(正在代理的应用程序对象)是给定类型的实例。
  • args:限制匹配以连接点(使用Spring AOP时执行方法),其中参数是给定类型的实例。
  • @target:限制与连接点(使用Spring AOP时执行方法)的匹配,其中执行对象的类具有给定类型的注释。
  • @args:限制与连接点的匹配(使用Spring AOP时的方法执行),其中传递的实际参数的运行时类型具有给定类型的注释。
  • @within:限制在具有给定注释的类型中连接点的匹配(使用Spring AOP时,执行具有给定注释的类型中声明的方法)。
  • @annotation:将匹配限制为连接点的主题(在 Spring AOP 中运行的方法)具有给定注释的连接点。
  1. Advice 通知(切入的时机,被切入的业务逻辑)

  2. Before 方法执行之前

  3. After方法执行之后

    1. AfterThrowing 方法执行之后异常处理
    2. AfterReturning 方法执行之后,返回结果
  4. Around 环绕通知,方法执行前和执行之后

  5. JoinPoint 连接点,用于获取方法的参数(配合Advice 里的具体通知使用)

    任何建议方法都可以将 类型的参数声明为其第一个参数。请注意,在建议周围需要声明类型的第一个参数,它是 的子类org.aspectj.lang.JoinPointProceedingJoinPointJoinPoint。

    • getArgs():返回方法参数。
    • getThis(): 返回代理对象。
    • getTarget(): 返回目标对象。
    • getSignature(): 返回所建议方法的说明。
    • toString(): 打印所建议方法的有用说明。

关于具体的切面,切点,建议,以及链接点请参考下面官方文档和实战部分代码结合理解

核心技术 (spring.io)

AOP动态代理

spring AOP 默认对 AOP 代理使用标准 JDK 动态代理。这使得任何接口(或一组接口)都可以被代理。

这对于代理类,不是必须实现被代理类接口。缺省情况下,如果业务对象没有可以实现的接口,则使用 CGLIB。由于编程到接口而不是类是很好的做法,因此业务类通常实现一个或多个业务接口。在那些(希望是罕见的)情况下,可以强制使用CGLIB,在这些情况下,您需要建议未在接口上声明的方法,或者需要将代理对象作为具体类型传递给方法。

Aop 动态代理工厂类图

image-20220917114946634

Aop 动态代理对象创建过程

IOC部分

这部分只是展示一个正常bean的创建过程(如果启用了切点代理,这部分是一样的)

image-20220917153207204

initalizeBean 方法和applyBeanPostProcessorsAfterInitialization方法是创建aop动态代理的重要方法,下面aop部分主要以后置处理器的方法进行详细剖析。

AOP部分

applyBeanPostProcessorsAfterInitialization(获取所有后置处理器)

image-20220917154812383

annotationAwareAspectJAutoProxyCreator(切面代理后置处理器)

image-20220917155228123

实战

AOP动态动态代理对象

目录结构

image-20220917160012327

配置类

切面类

package com.kang.aop;

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

import java.util.Date;

/**
 * @Description 切面测试
 * @ClassName Aoptest
 * @Author 康世行
 * @Date 10:06 2022/7/29
 * @Version 1.0
 **/
@Aspect
@Component
public class Aoptest {

    @Pointcut("execution(* *(..))")
    private void beforeTest(){}

    @Before("beforeTest()")
    public void testBefore(JoinPoint account){
        Object[] args = account.getArgs();
        for (Object arg : args) {
            System.out.println("方法执行之前参数->"+arg);
        }
    }

    @AfterReturning("beforeTest()")
    public void testAfter(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            System.out.println("方法执行之后参数->"+arg);
        }
    }

//    @Around("beforeTest()")
//    public Object testAroundTest(ProceedingJoinPoint joinPoint){
//          Object result=null;
//        try {
//            Object[] args = joinPoint.getArgs();
//            for (Object arg : args) {
//                System.out.println("方法执行之前参数:->"+arg);
//            }
//             result= joinPoint.proceed();
//
//        } catch (Throwable e) {
//            throw new RuntimeException(e);
//        }
//        System.out.println("方法执行完毕");
//        return result;
//    }


}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

注解容器上下文配置类(用于扫包和开启aop代理注解)

package com.kang.aop.config;

import com.kang.aop.Aoptest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

/**
 * @Description 开启切面配置
 * @ClassName AppConfig
 * @Author 康世行
 * @Date 10:05 2022/7/29
 * @Version 1.0
 **/
@Configuration
@EnableAspectJAutoProxy
@ComponentScan({"com.kang.aop"})
public class AppConfig {
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

JDK

接口

package com.kang.aop.aoptest;

/**
 * @Description 发送消息接口
 * @ClassName SendMessage
 * @Author 康世行
 * @Date 9:39 2022/7/30
 * @Version 1.0
 **/
public interface SendMessage {

    /**
    * @author 康世行
    * @description: 发送消息
    * @date  2022/7/30 9:40
    * @param msg 消息体
    * @return void
    * @Version1.0
    **/
    void sendMessage(String msg);
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

实现类

package com.kang.aop.aoptest;

import org.springframework.stereotype.Service;

/**
 * @Description 发送消息实现类
 * @ClassName SendMessageImpl
 * @Author 康世行
 * @Date 9:40 2022/7/30
 * @Version 1.0
 **/
@Service("sendMessageImpl")
public class SendMessageImpl implements SendMessage {
    @Override
    public void sendMessage(String msg) {
        System.out.println("发送的消息是->"+msg);
    }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
package com.kang.aop.aoptest;

import org.springframework.stereotype.Service;

/**
 * @Description TODO
 * @ClassName temp
 * @Author 康世行
 * @Date 9:46 2022/7/30
 * @Version 1.0
 **/
@Service("temp")
public class temp implements SendMessage{
    @Override
    public void sendMessage(String msg) {
        System.out.println(msg);
    }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

结果

image-20220917160657785

CGLIB

实现类

package com.kang.aop.aoptest;

import org.springframework.stereotype.Service;

/**
 * @Description TODO
 * @ClassName userSavle
 * @Author 康世行
 * @Date 10:12 2022/7/29
 * @Version 1.0
 **/
@Service
public class UserImpl {
    public void save(String msg){
        System.out.println("保存用户信息:"+msg);
    }
    public String print(String msg){
        String result= "userImpl"+msg;
        return result;
    }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

结果

image-20220917160550865

完整分析-流程图

https://www.processon.com/view/link/63257d13f346fb3377e81de7

文章来源: kangshihang.blog.csdn.net,作者:康世行,版权归原作者所有,如需转载,请联系作者。

原文链接:kangshihang.blog.csdn.net/article/details/126907525

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。