Spring-源码-实例化-AOP原理

Spring-源码-实例化-AOP原理,第1张

学习本文你可以掌握
1、被切面拦截的目标类是如何实例化
2、都知道AOP是基于JDK动态代理或者Cglib,那么原理是怎么实现的

一、使用案例

1、先定义一个service方法

public interface UserService {
    void addUser();
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("新增用户...");
    }
}

2、在定义一个切面

@Aspect
@Component
public class SimpleAspect {

    //定义切入点表达式,拦截前面上面定义的UserService
    @Pointcut("execution(* com.xiaour.spring.boot.service.UserService.*(..))")
    private void pointcut() {
    }

    //环绕通知
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws InterruptedException {
        String methodName = proceedingJoinPoint.getSignature().getName();
        System.out.println("执行" + methodName + "的环绕通知(@Around)...");
        
        Object result = proceedingJoinPoint.proceed();//执行目标方法
    }

    // 前置通知
    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行" + methodName + "的前置通知(@Before)...");
    }

    // 后置通知
    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行" + methodName + "的后置通知(@After)...");
    }

    // 返回通知
    @AfterReturning("pointcut()")
    public void afterReturning(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行" + methodName + "的后置返回通知(@AfterReturning)...");
    }
}

3、开启Aspect注解

@EnableAspectJAutoProxy //开启AOP注解
@SpringBootApplication
public class Application {
	......
}

至此Service的任意方法被调用都会进到切面的通知方法里面去。

二、下面开始介绍下原理

1、先从@EnableAspectJAutoProxy注解开始说

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

该注解2个属性
proxyTargetClass :AOP代理的具体实现方式。英文注释很简单哈,是否开启CGLIB(基于子类)的代理,为true的话使用CGLIB,为false的话使用基于标准 Java 接口的代理,也就是JDK动态代理。如果是true,目标类是接口还是会使用动态代理。
exposeProxy:控制代理的暴露方式。是否暴露当前代理对象为ThreadLocal模式。可以解决内部调用不能使用切面的问题,为true的话可以获取代理对象,通过代理对象调用方法进入切面。

//实例伪代码
T proxy=(T) AopContext.currentProxy();

@Import(AspectJAutoProxyRegistrar.class)先导入AspectJAutoProxyRegistrar类,看下该类会做什么事情


public class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
		}
		if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
			AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
		}
	}
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
	return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

最终向容器中添加一个类AnnotationAwareAspectJAutoProxyCreator,该类一层层实现很多接口,顶层有个BeanPostProcessor,所以AnnotationAwareAspectJAutoProxyCreator也算一个BeanPostProcessor,也会被添加到BeanPostProcessor执行链中去,然后在Bean初始化前后调用BeanPostProcessor的前置后置回调方法,AnnotationAwareAspectJAutoProxyCreator在调用后置回调方法时,会返回一个代理代理对象,从而生成了bean的代理实例。

2、下面就开始创建userService普通实例,然后执行设置属性方法populateBean和初始化方法initializeBean

3、执行前置-初始化-后置方法

4、执行AnnotationAwareAspectJAutoProxyCreator的后置方法,生成userService代理对象,替换传进来的userService的普通实例对象

5、执行AnnotationAwareAspectJAutoProxyCreator父类的postProcessAfterInitialization,里面会执行最为重要的方法-wrapIfNecessary

6、下面介绍下wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	.....
	//判断cacheKey是否属于通知的bean
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
	if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

	// Create proxy if we have advice.//获取通知拦截器
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		Object proxy = createProxy(
				bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}

先获取通知拦截器,判断userService是否是符合通知拦截器定义的规则,也就是是否符合切点表达式定义的规则,如果符合则返回一系列通知方法,下面截了一张specificInterceptors的内容,其实就是我们上面定义切面里面的四个通知

protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
	....

	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this);

	//设置ProxyTargetClass的值,这个和前面提到的注解EnableAspectJAutoProxy的ProxyTargetClass属性是一个东西
	if (!proxyFactory.isProxyTargetClass()) {
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}

	//拿到所有通知,也就是切面里面定义切面里面的四个通知
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	for (Advisor advisor : advisors) {
		proxyFactory.addAdvisor(advisor);
	}

	proxyFactory.setTargetSource(targetSource);//设置被代理对象
	customizeProxyFactory(proxyFactory);

	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
		proxyFactory.setPreFiltered(true);
	}

	return proxyFactory.getProxy(getProxyClassLoader());
}

最终调用proxyFactory.getProxy,下面看ProxyFactory的getProxy方法是做了啥

public Object getProxy(ClassLoader classLoader) {
	return createAopProxy().getProxy(classLoader);
}

调用createAopProxy方法里面会先拿到aopProxyFactory,然后调用aopProxyFactory的createAopProxy

protected final synchronized AopProxy createAopProxy() {
	if (!this.active) {
		activate();
	}
	return getAopProxyFactory().createAopProxy(this);
}

private AopProxyFactory aopProxyFactory;
public AopProxyFactory getAopProxyFactory() {
	return this.aopProxyFactory;
}

下面看aopProxyFactory

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return new JdkDynamicAopProxy(config);
	}
}

更具条件是选择JDK代理还是Cglib代理,最后将生成的代理类一步一步返回

假设上面返回JdkDynamicAopProxy对象,当调用JdkDynamicAopProxy的getProxy方法是如何创建代理对象的

注意接口参数里面的interfaces的值,是一个数组,不是单个接口,而是多个接口

InvocationHandler其实就是JdkDynamicAopProxy对象,里面实现了invoke方法,下面我们看下当我们调用UserService里面的方法时,invoke里面做了什么?

拿到调用链,也就是由目标方法+切面环绕通知+前置通知+后置通知组成的调用链一层一层执行下去

至此所有流程执行完成了。

总结:Spring会先加载AspectJAutoProxyRegistrar类,AspectJAutoProxyRegistrar会像容器注册AnnotationAwareAspectJAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcess类型的Bean,在实例化其他普通Bean的时候,会执行该bean的前置和后置方法,在AnnotationAwareAspectJAutoProxyCreator后置方法里面会创建代理对象,当我们执行目标方法时,会进入JdkDynamicAopProxy的invoke方法,然后拿到调用链,挨个调用。

三、疑问

1、在执行wrapIfNecessary方法的时候,getAdvicesAndAdvisorsForBean方法会返回当前Bean的所有通知方法,也就是下图的specificInterceptors,那么这个数组是怎么得到的

解答:

1、在解答前先说下在启动的时候,我们手动添加的@EnableAspectJAutoProxy注解,里面会注册一个AnnotationAwareAspectJAutoProxyCreator,这是一个BeanPostProcess

注册时设置的值,主要包含设置下面2个属性

  1. order = Integer.MIN_VALUE
  2. beanName = org.springframework.transaction.config.internalTransactionAdvisor

注册AnnotationAwareAspectJAutoProxyCreator必定会实例化它,看下AnnotationAwareAspectJAutoProxyCreator的初始化方法

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
	//这个2个属性非常重要,下面会介绍aspectJAdvisorsBuilder
	private AspectJAdvisorFactory aspectJAdvisorFactory;
	private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder;
	//aspectJAdvisorsBuilder主要完成判断Bean是否被切入点表达式拦截的对象

	//初始化方法的时候会为上面2个属性设置值
	@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);
	}	

}

BeanFactoryAspectJAdvisorsBuilder类,里面有几个Map,存的是带有@Aspect的通知方法集合,就比如类SimpleAspect带有@Aspect注解,那么SimpleAspect就是一个切面,切面里面一定有切点+通知方法,那么这些通知方法都存在aspectJAdvisorsBuilder的Map里面,至于怎么知道SimpleAspect类带有@Aspect注解,以及找出他的通知方法,都是由aspectJAdvisorsBuilder来实现的。
那么什么时候触发去解析的SimpleAspect里面的通知呢?在实例Bean实例化的时候,下面开始讲解

先看创建Bean时会调用一个resolveBeforeInstantiation方法,也就是下图说的

resolveBeforeInstantiation是调用doCreateBean之前调用的,也叫实例化前置方法。

下面看下resolveBeforeInstantiation怎么做的

在一对BeanPostProcess集合中,轮训调用实例化前置方法,当然也包含AnnotationAwareAspectJAutoProxyCreator

1、AnnotationAwareAspectJAutoProxyCreator是InstantiationAwareBeanPostProcessor的子类
2、实现了postProcessBeforeInstantiation方法

下面看下AnnotationAwareAspectJAutoProxyCreator的postProcessBeforeInstantiation方法,shouldSkip方法很重要

@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	for (Advisor advisor : candidateAdvisors) {
		if (advisor instanceof AspectJPointcutAdvisor) {
			if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
				return true;
			}
		}
	}
	return super.shouldSkip(beanClass, beanName);
}

shouldSkip会调用findCandidateAdvisors方法,下面看下findCandidateAdvisors做了什么?

最终是通过前面说到的aspectJAdvisorsBuilder对象,调用buildAspectJAdvisors方法完成解析找到带有@Aspect注解的类,并且解析里面的通知方法。

上图主要流程,拿到所有BeanName判断这个Bean是否带有@Aspect注解,如果有那么解析这个Bean的所有通知方法。看下是如何判断是否带有@Aspect注解的

AnnotationUtils.findAnnotation(clazz, Aspect.class) != null

下面说下处理@Aspect类的逻辑

List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

主要是把里面的方法转成Advisor对象,看下最终包含哪些方法

1、SimpleAspect.around
2、SimpleAspect.before
3、SimpleAspect.after
4、SimpleAspect.afterReturning

注意是不包含切入点表达式的方法,只有通知,再回到上面wrapIfNecessary

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/741028.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存