springAOP使用及原理分析

springAOP简介

AOP(Aspect Oriented Program):即面向切面编程

在面向切面编程的思想里面,把功能分为核心业务功能和周边功能

  • 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
  • 所谓的周边功能,比如性能统计,日志,事务管理等等

在SpringAOP中将这些周边功能称为切面,将核心业务逻辑与这些周边功能分别独立开始,然后将它们交织在一起就是AOP需要做的事情

AOP将业务模块锁公共调用的、非业务逻辑的功能封装起来;这样的好处?

一:所以这些非核心业务功能都集中在一起,而不是分散到多处代码中,便于减少系统的重复代码,降低模块间的耦合度,有利于维护

二:服务模块更简洁,因为它们只包含主要关注点或核心功能的代码,次要的非业务功能(周边功能)被转移到切面中

AOP当中的概念

连接点(JoinPoint):在程序执行过程中某个特定的点,比如类初始化前、类初始化后,方法调用前,方法调用后;

切点(Pointcut):所谓切点就是你所切取的类中的方法,比如你横切的这个类中有两个方法,那么这两个方法都是连接点,对这两个方法的定位就称之为切点;

增强(Advice):增强是织入到连接点上的一段程序,另外它还拥有连接点的相关信息;

目标对象(Target):增强逻辑的织入目标类,就是我的增强逻辑植入到什么位置;

引介(Introduction):一种特殊的增强,它可以为类添加一些属性喝方法;

织入(Weaving):织入就是讲增强逻辑添加到目标对象的过程;

代理(Proxy):一个类被AOP织入增强后,就会产生一个结果类,他是融合了原类和增强逻辑的代理类;

切面(Aspect):切面由切点和增强组成,他是横切逻辑定义和连接点定义的组成;

相关注解说明

  • @Apsect: 将当前类标识为一个切面;
  • @Pointcut: 公共切入点:使用方式 eg: @Before(value = “pointCut()”)
  • @Before:前置通知,就是在目标方法执行之前执行;
  • @AfterReturning:后置通知,方法退出时执行;
  • @AfterThrowing: 异常通知,有异常时该方法执行;
  • @After: 最终通知,无论什么情况都会执行;
  • @Afround: 环绕通知;在目标方法执行的前后进行执行

execution表达式说明:

eg: @Pointcut(value = “execution(* com.compass.aop.test…* .*(…))”)

  • 标识符 含义
  • execution() 表达式的主体
  • 第一个“*”符号 表示返回值的类型任意
  • com.smartplg.interview.test AOP所切的服务的包名,即,需要进行横切的业务类
  • 包名后面的“…” 表示当前包及子包
  • 第二个“*” 表示类名,*即所有类
  • .*(…) 表示任何方法名,括号表示参数,两个点表示任何参数类型

annotation: 匹配指定注解

@annotation(anno.RequiredAnnotationClass) 匹配有此注解[@RequiredAnnotationClass]描述的方法

简单易懂的图就是:

假设我要在用户登录后做日志记录,大致的流程图就是:
在这里插入图片描述

AOP基本使用

有了以上的前置知识,我们就可以使用spring的AOP功能进行编程了,相关的依赖自己进行百度导入,使用springBoot的话,引入依赖比较简单。我们使用注解的方式来完成AOP切面,xml太繁琐了,有兴趣的可以自己看下xml版本的。

1.定义一个配置类,定义扫描规则,开启AOP功能

@EnableAspectJAutoProxy
@Configuration // 配置类
@ComponentScan("com.compass.aop.test") // 标识扫描的路径
public class AppConfig {

}

2.定义一个登录接口

public interface UserService {
    /**
     * 登录接口 [模拟,真实的时候是需要连接数据库查询的]
     * @param username 
     * @param password
     * @return
     */
    String login(String username,String password);
}

3.对登录接口实现

@Service
public class UserServiceImpl implements UserService {

    @Log(title = "loginOperation") //此注解是自定义注解,表示是开启记录日志功能
    @Override
    public String login(String username, String password) {
        System.out.println("login 被执行...");
        // 打开此注释即可看到异常通知
	    // int age = 10/0;
        if ("admin".equals(username) && "123".equals(password)){
            return "login success";
        }
        return "login failed";
    }
}

4.自定注解

@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableLog{
    public String value() default "yes";
}

4.定义切面 对login(String username, String password) 方法进行增强

@Aspect // 表示这是一个切面
@Component  // 将这个切面添加到spring容器中
public class LogAspect {

    // 公共切入点 使用方式 @Before(value = "pointCut()")
    @Pointcut(value = "execution(* com.compass.aop.test..*  .*(..))")
    private void pointCut() {
    }

    // 前置通知
    @Before(value = "pointCut()")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("前置通知[execution]");
    }
    
    
    // 后置通知 在指定注解下执行
    @AfterReturning(value = "@annotation(serviceLog)", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, EnableLog serviceLog, String result) {
        System.out.println("后置通知:"+result+" Log-value:"+serviceLog.value());
        // 如果我们注解标注的值为value 我们可以在此处记录用户的日志信息
        if (serviceLog.value()){
            // 获取到方法参数的value 如果是真实环境我们就可以将日志记录到数据库或者文件中
            Object[] args = joinPoint.getArgs();
            System.out.println(args);
        }
    }

    // 最终通知
    @After(value = "pointCut()")
    public void doAfter(JoinPoint joinPoint) throws NoSuchMethodException {
        System.out.println("最终通知: ");

        //当前调用的方法签名
        Signature signature = joinPoint.getSignature();
        MethodSignature msg=(MethodSignature) signature;
        Object target = joinPoint.getTarget();
        //获取注解标注的方法
        Method method = target.getClass().getMethod(msg.getName(), msg.getParameterTypes());
        //通过方法获取注解
        EnableLog annotation = method.getAnnotation(EnableLog.class);
        //获取参数
        Object[] args = joinPoint.getArgs();

    }

    // 异常通知
    @AfterThrowing(value = "pointCut()",throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint ,Throwable e) {
        System.out.println("异常通知 :"+e.getMessage());
    }

    //环绕通知 注意要有ProceedingJoinPoint参数传入
    @Around(value = "pointCut()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        System.out.println("环绕通知..环绕前");
        // 执行目标方法 不写这行代码 目标方法不会被执行
        // 如果有返回值,一定要return出去,否则自己的业务方法得不到返回值
        Object result = point.proceed();
        System.out.println("环绕通知..环绕后");
        return result;
    }

}

测试类

public class MyTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean( "userServiceImpl",UserService.class);
        String result = userService.login("admin", "123");
        System.out.println("登录结果:"+result);
    }
}

注意点:

  • 环绕通知一定要调用 point.proceed() 方法进行执行目标方法,不然我们的目标方法不会被执行
  • 环绕通知如果有返回值,我们也需要获取到返回值,return出去,否则我们调用目标方法的时候得不到结果

AOP通知的执行顺序

我们来看下这些通知的一个指向流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GzMM4Hp7-1647050128564)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220311203545617.png)]

正常的执行逻辑:

  1. 执行环绕通知的前置逻辑
  2. 执行前置通知
  3. 执行目标方法
  4. 执行后置通知
  5. 执行最终通知
  6. 执行环绕通知的后置逻辑

出现异常的执行逻辑:

  1. 执行环绕通知的前置逻辑
  2. 执行前置通知
  3. 执行目标方法
  4. 出现异常,执行异常通知,出现异常返回通知和环绕通知的后置逻辑就不会被执行了
  5. 执行最终通知

多个切面时的执行流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LczeFkeR-1647050128565)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220311205632982.png)]

springAOP在真实开发中可以做什么?

我这里给大家举几个例子: 反正你就可以理解为,你可以在你的业务逻辑的任何方法执行的时候进行干扰,也就是增强,你可以获取到该方法运行时的参数,可以做一些具体的操作,具体怎么操作是由你的决定的,springAOP在执行方法的同时把控制器交给你,这也是很多框架的特点,在执行原有逻辑的基础上,给你留有接口,或者是能让你对执行过程可以进行干扰。

  1. 记录用户的操作记录
  2. 事务管理
  3. 防重复提交
  4. 做数据缓存
  5. 权限验证
  6. 数据校验

进阶-代理模式之JDK动态代理

代理模式有两种实现:

  • jdk动态代理:jdk原生API,jdk动态代理是由java内部的反射机制来实现的,[如果你实现了接口那么就是动态代理]
  • cglib代理:使用第三方jar包,cglib动态代理底层则是借助asm来实现,[ 如果没有实现接口是用的cglib代理 ]

空口无凭,看源码说话,这也就是我们可以实现接口,也可以不实现接口都能完成AOP切入功能的硬核所在

// 创建一个AOP代理
protected final synchronized AopProxy createAopProxy() {
	if (!this.active) {
			activate();
		}
  		  // 先获取到代理工厂,让后再去创建代理对象
		return getAopProxyFactory().createAopProxy(this);
}

@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (!NativeDetector.inNativeImage() &&
				(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
            // 如果你的目标类是一个接口,那么使用动态代理
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                   // 创建一个动态代理对象返回
				return new JdkDynamicAopProxy(config);
			}
           	//如果不是的话,那就使用cglb进行创建代理对象
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

JDK动态代理的执行方式:

public class JDKDynamicProxy {

    /**
     * 使用jdk的反射机制创建出一个代理对象
     * @param target 需要创建代理的对象
     * @return 代理对象
     */
    public  static <T> T  getTransactionProxyInstance(Object target,Class<T> clazz){

        Object proxyInstance = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 三个参数:1、代理对象,2、目标对象的方法,3、目标对象的参数值列表
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("执行目标业务逻辑之前");  //执行核心业务之前执行的内容
                Object result = method.invoke(target, args); //执行目标对象方法,即核心业务
                System.out.println("执行目标业务逻辑之后"); //执行核心业务之后执行的内容
                return result;
            }
        });
        return (T)proxyInstance;
    }

}

调用测试

    public static void main(String[] args) {

        UserServiceImpl userService = new UserServiceImpl();
        UserService userServiceProxy = JDKDynamicProxy.getTransactionProxyInstance(userService, UserService.class);
        System.out.println("结果:"+userServiceProxy.login("admin", "123"));
        // 一个是真实的对象,一个jdk动态创建的代理对象,根本都不是同一个对象
        System.out.println(userService == userServiceProxy );

    }

输出结果:

执行目标业务逻辑之前
login 被执行... [目标方法被执行]
执行目标业务逻辑之后
结果:login success
false

进阶-SpringAOP原理

1.我们先来看下这个@EnableAspectJAutoProxy 这个注解做了什么事情

他往容器中导入了一个 AspectJAutoProxyRegistrar.class 组件

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;
}

AspectJAutoProxyRegistrar.class做了什么事情?

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        // 在此处导入了 AnnotationAwareAspectJAutoProxyCreator.class 
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

}

然后我们的 AppConfig 早在 this() [ 也就是AnnotationConfigApplicationContext构造器的时候 ],的时候就添加到beanDefinitionMap中去了,然后在 invokeBeanFactoryPostProcessors(beanFactory);后置处理器的时候,就会去容器中找到所有配置类,并且解析。

invokeBeanFactoryPostProcessors(beanFactory)做的事情:

1.拿到工厂中的所有Bean定义信息(后置处理器(第一步的那几个)+配置类)

2.找到真正的配置类

3.使用parse进行配置类解析包扫描所有组件进来

4.1 scanner扫描包路径下的所有类,判断是否扫描范围内的,如果是就封装成4.ScannedGenericBeanDefinition添加到需要返回的组件集合中(@Lazy等注解会在这一步解析,保存到当前Bean的定义信息【BeanDefinition】中)

5.除了会处理@ComponentScan注解还能处理【@PropertySource、@lmport 、@lmportResource、】

执行完 invokeBeanFactoryPostProcessors(beanFactory) 我们的beanDefinitionMap中就有了Bean的定义信息了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Axma1KeA-1647050128566)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220311235915497.png)]

等处理完,我们就可以看到 beanDefinitionMap 中了 AnnotationAwareAspectJAutoProxyCreator 的定义

AnnotationAwareAspectJAutoProxyCreator 他的职责就是看看那些类需要添加到 advisedBeans

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hqVDbQ2B-1647050128566)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220311215904477.png)]

在容器刷新的12大步骤中,registerBeanPostProcessors(beanFactory),会注册所有实现了BeanPostProcessors接口的类,然后我们的AnnotationAwareAspectJAutoProxyCreator就会被创建并且添加到容器中。

	public static void registerBeanPostProcessors(
			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

		
		// 先从容器中获取到 所有实现了 BeanPostProcessor 接口的beanName
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

		
		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

		
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
         //  处理实现了 PriorityOrdered接口的BeanPostProcessor
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
				priorityOrderedPostProcessors.add(pp);
				if (pp instanceof MergedBeanDefinitionPostProcessor) {
					internalPostProcessors.add(pp);
				}
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		// 进行排序
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        // 注册到beanPostProcessors中
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

		 // 处理实现了 Ordered 的BeanPostProcessor
         // 我们的AnnotationAwareAspectJAutoProxyCreator是实现了Ordered接口的会在此处进行处理
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
		for (String ppName : orderedPostProcessorNames) {
             // 调用 getBean() 创建我们的AnnotationAwareAspectJAutoProxyCreator对象 经过生命周期
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);

		// 没有实现任何排序接口的BeanPostProcessor
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

		// Finally, re-register all internal BeanPostProcessors.
		sortPostProcessors(internalPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);

		// Re-register post-processor for detecting inner beans as ApplicationListeners,
		// moving it to the end of the processor chain (for picking up proxies etc).
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yz1O9w0l-1647050128567)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220311225244534.png)]

然后接下来所有的bean创建的流程都会经过AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreatorpostProcessBeforeInstantiation() 的处理,会在每个bean的创建过程中会判断那些是切面需要加入到advisedBeans

@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(beanClass, beanName);
 
		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
			//advisedBeans用于存储不可代理的bean,如果包含直接返回
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			//判断当前bean是否可以被代理,然后存入advisedBeans
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}

		//获取封装当前bean的TargetSource对象,如果不存在,则直接退出当前方法,否则从TargetSource
               // 中获取当前bean对象,并且判断是否需要将切面逻辑应用在当前bean上。
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if (targetSource != null) {
			if (StringUtils.hasLength(beanName)) {
				this.targetSourcedBeans.add(beanName);
			}
                        // 获取能够应用当前bean的切面逻辑
			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
 
                        // 对生成的代理对象进行缓存
			this.proxyTypes.put(cacheKey, proxy.getClass());
			//如果最终可以获得代理类,则返回代理类,直接执行实例化后置通知方法
			return proxy;
		}
 
		return null;
	}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RoOGOk8Z-1647050128567)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220311225552782.png)]

AnnotationAwareAspectJAutoProxyCreator 他还实现了 BeanFactoryAware 会拿到整个beanFactory

	@Override
	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		super.initBeanFactory(beanFactory);
		if (this.aspectJAdvisorFactory == null) {
			this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
		}
         
		this.aspectJAdvisorsBuilder =
				new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
	}

在初始化期间使用InitializingBean准备好基本的属性

利用BeanFactoryAware 接口 准备好ReflectiveAspectJAdvisorFactory BeanFactoryAspectJAdvisorsBuilderAdapter

ReflectiveAspectJAdvisorFactory (创建增强器的工厂,他们负责保存增强器)

BeanFactoryAspectJAdvisorsBuilderAdapter(增强器建造者,产生功能增强器)

如何找到切面?

	@Override
	protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		if (this.aspectJAdvisorsBuilder != null) {
            // 第一次运行的时候,不知道谁是切面,把容器中的所有组件获取到挨个进行比对,看看谁是切面,添加到缓存中
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}
	public List<Advisor> buildAspectJAdvisors() {
		List<String> aspectNames = this.aspectBeanNames;

		if (aspectNames == null) {
            // 双重检测锁
			synchronized (this) {
				aspectNames = this.aspectBeanNames;
                 // 如果切面的集合为空
				if (aspectNames == null) {
					List<Advisor> advisors = new ArrayList<>();
					aspectNames = new ArrayList<>();
					String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
                      // 从容器中获取到所有的beanNames
					for (String beanName : beanNames) {
						if (!isEligibleBean(beanName)) {
							continue;
						}
						// We must be careful not to instantiate beans eagerly as in this case they
						// would be cached by the Spring container but would not have been weaved.
						Class<?> beanType = this.beanFactory.getType(beanName, false);
						if (beanType == null) {
							continue;
						}
                        // 判断当前bean是否是切面,如果是添加到aspectNames
						if (this.advisorFactory.isAspect(beanType)) {
							aspectNames.add(beanName);
							AspectMetadata amd = new AspectMetadata(beanType, beanName);
							if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
								MetadataAwareAspectInstanceFactory factory =
										new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                  // 利用反射获取到切面中的所有的方法,找出增强方法 [可以点进去看看是怎么封装的]
								List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
								if (this.beanFactory.isSingleton(beanName)) {
									this.advisorsCache.put(beanName, classAdvisors);
								}
								else {
									this.aspectFactoryCache.put(beanName, factory);
								}
                                 // 将切面中的所有增强方法信息添加到advisors
								advisors.addAll(classAdvisors);
							}
							else {
								// Per target or per this.
								if (this.beanFactory.isSingleton(beanName)) {
									throw new IllegalArgumentException("Bean with name '" + beanName +
								"' is a singleton, but aspect instantiation model is not singleton");
								}
								MetadataAwareAspectInstanceFactory factory =
										new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
								this.aspectFactoryCache.put(beanName, factory);
								advisors.addAll(this.advisorFactory.getAdvisors(factory));
							}
						}
					}
					this.aspectBeanNames = aspectNames;
					return advisors;
				}
			}
		}

		if (aspectNames.isEmpty()) {
			return Collections.emptyList();
		}
		List<Advisor> advisors = new ArrayList<>();
		for (String aspectName : aspectNames) {
			List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
			if (cachedAdvisors != null) {
				advisors.addAll(cachedAdvisors);
			}
			else {
				MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
				advisors.addAll(this.advisorFactory.getAdvisors(factory));
			}
		}
		return advisors;
	}

一个 Advisor 就是一个增强器保存当前切入点表达式和当前方法的详细信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YR1Y2TNz-1647050128568)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220312012352598.png)]

这样我们的切面和切入点信息就已经准备完毕,接下来就是别的bean在创建的过程当中由AnnotationAwareAspectJAutoProxyCreator判断要不要给其创建代理对象,也就是看看切面能不能切入这个bean。

bean属性初始化完毕后开始创建的代理对象,会在此处判断当前对象如果有切面能切切入,那么就创建代理对象。

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                // 检测是否需要创建代理对象,如果需要使用cglib创建代理对象进行返回
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}
	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
        // 使用原生的jdk动态代理创建代理对象
		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
	}

进阶-AOP运行原理

只要我们执行目标方法,就会被 JdkDynamicAopProxy拦截到,JdkDynamicAopProxy实现了InvocationHandler拦截接口

	@Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;
		// 获取到目标对象
		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
			}
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
			}
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				// There is only getDecoratedClass() declared -> dispatch to proxy config.
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				// Service invocations on ProxyConfig with the proxy config...
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;

			if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

			// 通过当前执行的方法和当前class类型,找到该方法的一个拦截器链,也就是我们在切面写的那些通知
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
            // 如果你没有任何通知切入你,那么就直接利用反射调用目标方法,就不再去创建MethodInvocation
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// 此处相当于创建一个调用者,里面会封装很多信息
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
                // 获取到了invocation后,就可以进行AOP拦截的整个流程了
				retVal = invocation.proceed();
			}

			// Massage return value if necessary.
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				// Special case: it returned "this" and the return type of the method
				// is type-compatible. Note that we can't help if the target sets
				// a reference to itself in another returned object.
				retVal = proxy;
			}
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}


通过当前执行的目标方法和当前类的class获取到调用链

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QxfiGyzl-1647050128569)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220312090901767.png)]

我们真正的方法调用者,他里面封装了很多信息,有目标对象,代理对象,目标方法,当前方法的参数,有了这些信息,我们完全可以利用反射去调用目标方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KkkzWggl-1647050128569)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220312091121095.png)]

接下来我们就分析他是如何进行一步一步调用的,他是一个递归调用,子类父类之间相互调用的一个过程。

坐稳了,开始发车了,前方高能,这就是通知执行流程的一个原因所在,什么时候执行什么通知,都是由接下来的逻辑确定的


	@Override
	@Nullable
	public Object proceed() throws Throwable {
		// currentInterceptorIndex 第一次为 -1,
        // interceptorsAndDynamicMethodMatchers:所有可以拦截当前方法执行的通知都在里面,有几个通知size就为几
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
             // 这个是调用连接点,这就是递归的结束条件,当interceptorsAndDynamicMethodMatchers大小为-1的时候
             // 说明调用链已经准备完毕,可以开始逐步进行调用
			return invokeJoinpoint();
		}

        // 他让当前的这个索引+1,那就就是获取到拦截器列表中的第一个拦截器了
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			// 我们的通知还是一个MethodInterceptor,他可以去调用invoke(this)方法
             // 把ReflectiveMethodInvocation实例给传递过去了,里面有封装很多信息,我们上面有提到过
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

可以到,第一次进来,我们有6个通知,可以切入到我们的 login()方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ue5T5KQC-1647050128569)(C:%5CUsers%5C14823%5CDesktop%5Clearn-note%5Ctyproa-img%5Cimage-20220312093049782.png)]

接着调用到 invoke()方法

	@Override
	@Nullable
	public Object invoke(MethodInvocation mi) throws Throwable {
        // 第一次默认是null
		MethodInvocation oldInvocation = invocation.get();
        // 他把传递过来的ReflectiveMethodInvocation使用ThreadLocal对象保存起来,达到一个线程之间隔离的效果
		invocation.set(mi);
		try {
            // mi就是我们的ReflectiveMethodInvocation,一调用就调用到ReflectiveMethodInvocation.proceed()方法去了
			return mi.proceed();
		}
		finally {
            // 在执行完毕后,设置为null,避免内存泄漏
			invocation.set(oldInvocation);
		}
	}

我们让其递归调用结束,也就是我们的currentInterceptorIndex==我们通知列表的大小的时候,说明该退出递归,去执行对应的调用链了。这个递归调用过程有点难说清楚,还是个人debug看嘛,你看看对象的比变化过程,那些通知分别是在说明位置执行的就行,最后使用的是jdk底层的invoke()方法执行的目标方法。我个大家一张参考图,大家自行进行debug,自己debug看源码才是精髓。

可以到,第一次进来,我们有6个通知,可以切入到我们的 login()方法

接着调用到 invoke()方法

	@Override
	@Nullable
	public Object invoke(MethodInvocation mi) throws Throwable {
        // 第一次默认是null
		MethodInvocation oldInvocation = invocation.get();
        // 他把传递过来的ReflectiveMethodInvocation使用ThreadLocal对象保存起来,达到一个线程之间隔离的效果
		invocation.set(mi);
		try {
            // mi就是我们的ReflectiveMethodInvocation,一调用就调用到ReflectiveMethodInvocation.proceed()方法去了
			return mi.proceed();
		}
		finally {
            // 在执行完毕后,设置为null,避免内存泄漏
			invocation.set(oldInvocation);
		}
	}

我们让其递归调用结束,也就是我们的currentInterceptorIndex==我们通知列表的大小的时候,说明该退出递归,去执行对应的调用链了。这个递归调用过程有点难说清楚,还是个人debug看嘛,你看看对象的比变化过程,那些通知分别是在说明位置执行的就行,最后使用的是jdk底层的invoke()方法执行的目标方法。我个大家一张参考图,大家自行进行debug,自己debug看源码才是精髓。

在这里插入图片描述

这张大致图描述了SpringAOP的一个流程,可以配合debug进行调试,图有点大,可以放大仔细看,因为框架的源代码逻辑比较复杂,难免会导致分析图也有点复杂,想要理解,就得认真反复看,看几次就属性了,到了面试的时候,你就能跟面试官两个扯,毫无压力。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值