aop源码解析二:postProcessBeforeInstantiation

时间:2021-10-17 17:16:52

我们就不再从bean的加载过程开始看了 在aop源码解析一:注册BPP中 介绍到了一个后置处理器 AnnotationAwareAspectJAutoProxyCreator 我们就从这个类入手aop
先看postProcessBeforeInstantiation 很显然 是bean实例化前调用的 里面好像是处理了一些事情

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
Object cacheKey = getCacheKey(beanClass, beanName);

if (beanName == null || !this.targetSourcedBeans.containsKey(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
//这里需要注意的是 shouldSkip方法被子类覆盖了 这个很容易忽略
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}

// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
if (beanName != null) {
//todo 这里返回null!!!
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
this.targetSourcedBeans.put(beanName, Boolean.TRUE);
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
}

return null;
}

这里shouldSkip(beanClass, beanName)方法是一个很容易忽略的方法 可能是方法名字的原因或者复杂的继承关系吧 但在这个方法的实现过程中 已经把所有的切面加载完成了
AspectJAwareAdvisorAutoProxyCreator##shouldSkip

    protected boolean shouldSkip(Class beanClass, String beanName) {
// TODO: Consider optimization by caching the list of the aspect names
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);
}

首先找出所有的advisor 如果要加载的这个bean是一个advisor的话 当然就不会代理了
AnnotationAwareAspectJAutoProxyCreator##findCandidateAdvisors()

    protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
// 寻找bean的类型为Aspect.class的 我们更关心注解的形式 这里不过多的讲解
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
// 处理注解(@Aspect)的形式
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
    public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = null;

synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<Advisor>();
aspectNames = new LinkedList<String>();
// 根据类型取出beanFactory中所有的bean 包括父容器 然后循环判断
// todo 这样就可能造成重复加载
String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
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);
if (beanType == null) {
continue;
}
// 带有@Aspect 注解 并且不是由ajc编译生成 code-style方式的切面spring aop不处理(在前面已经处理过了)
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.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.EMPTY_LIST;
}
List<Advisor> advisors = new LinkedList<Advisor>();
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;
}

对于bean工厂中的所有的bean 判断是否有Aspect注解 并且不是由ajc编译 code-style编程的方式 再前面已经处理过了 这里就不再处理 对于下面部分的代码阅读来说 有些读者可能会觉得困难 看着这么多陌生的名字很长的类就觉得好难 其实这些类只是一些包装util而已 为的就是不会在一个方法里面代码过多显得臃肿 AspectMetadata MetadataAwareAspectInstanceFactory都是起到这样的作用 下面的部分就是本文的重头戏了 寻找advisor

    public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
final String aspectName = maaif.getAspectMetadata().getAspectName();
validate(aspectClass);

// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(maaif);

final List<Advisor> advisors = new LinkedList<Advisor>();
// 循环所有的切面方法
for (Method method : getAdvisorMethods(aspectClass)) {
// 将切面方法转化为advisor
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}

// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}

// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}

return advisors;
}

我们这里先列举一个切面的例子 这样可能更有助于读者阅读 这个例子可以从github-springmvcdemoclone下来

@Component
@Aspect
public class LogTipAspect {

@Around("@annotation(com.sunmingshuai.annotation.LogTip)")
public void aroundEntry(ProceedingJoinPoint pjp){
System.out.print("before aroundEntry");
try {
pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.print("before aroundEntry");
}

@Before("@annotation(com.sunmingshuai.annotation.LogTip)")
public void beforeEntry(){
System.out.print("beforeEntry");
}

@After("@annotation(com.sunmingshuai.annotation.LogTip)")
public void afterEntry(){
System.out.print("afterEntry");
}

@AfterThrowing("@annotation(com.sunmingshuai.annotation.LogTip)")
public void afterThrowing(){
System.out.print("afterEntry");
}

@AfterReturning("@annotation(com.sunmingshuai.annotation.LogTip)")
public void afterReturning(){
System.out.print("afterReturning");
}

}

getAdvisorMethods(aspectClass)方法就是找出切面类中的所有的切面方法 看下这里的实现

    private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new LinkedList<Method>();
// 筛选出没有Pointcut注解的方法 其实就是为了想获取所有的@Before @After @Around等注解标注的方法
// 这里的实现是直接排除Pointcut注解 这样代码更简洁些吧
ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException {
// Exclude pointcuts
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
}
});
/**
* 多个comparator的排序: 多个comparator组成一个排序链 程序执行这个排序链 有一个comparator返回非0数字则返回 或者直到执行完排序链 返回0
* 有点类似与sql中多个列的排序算法
* 最后排序结果 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
*/

Collections.sort(methods, METHOD_COMPARATOR);
return methods;
}

代码里面的注释已经很清晰了 找出被切面所注释的方法 注意在方法返回前对方法进行了排序 这个排序算法阅读起来不太容易 主要是因为有多重排序 读者可以学习一下这个方法 也许在工作中能用的到 最后的排序顺序是
Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 这个顺序是很重要的 影响到后面方法调用过程中执行链的顺序
然后再把找到的方法转化成advisor保存 我们可以想象的出来 这样一个advisor保存了非常多的信息在里面

    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif,
int declarationOrderInAspect, String aspectName) {

validate(aif.getAspectMetadata().getAspectClass());

// 包装类: 拆解出切面的切点表达式
AspectJExpressionPointcut ajexp =
getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
if (ajexp == null) {
return null;
}
//从构造函数中可以看出Advisor的组成结构 pointcut advice
return new InstantiationModelAwarePointcutAdvisorImpl(
this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
}

对于切面来说 肯定要指定切点的 也就是这个方法在什么地点执行? getPointcut方法就比较简单了 首先查看是否有切面方法注解 然后根据切点构造AspectJExpressionPointcut

    private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
return ajexp;
}

然后再看最后一步构造advisor 可以看到是直接调用构造函数构造了一个实现类InstantiationModelAwarePointcutAdvisorImpl

    public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp,
MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) {

this.declaredPointcut = ajexp;
this.method = method;
this.atAspectJAdvisorFactory = af;
this.aspectInstanceFactory = aif;
this.declarationOrder = declarationOrderInAspect;
this.aspectName = aspectName;

if (aif.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut =
Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
// If it's not a dynamic pointcut, it may be optimized out
// by the Spring AOP infrastructure after the first evaluation.
this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif);
this.lazy = true;
}
else {
// A singleton aspect.
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
this.pointcut = declaredPointcut;
this.lazy = false;
}
}

方法的内部就是各种赋值操作 但有一个方法需要看一下 instantiateAdvice(this.declaredPointcut) 我们往下追踪

    public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {

Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);

AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}

// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}

if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}

AbstractAspectJAdvice springAdvice;

switch (aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method " + candidateAdviceMethod);
}

// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrderInAspect);
// argNames
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}

可以看到生成了对应的advice 下面又对方法的参数进行处理
argNames指的是注解的argNames参数提供的值 这里会考虑JoinPoint ProceedingJoinPoint特殊参数 这些参数是框架默认支持提供的 一般约定放到参数的第一位
带了读者绕了这么久 读者可能已经不知道我们是从哪里进来的 进来的目的又是什么了 让我们再回到最开始的代码 我们进行了这么多步的操作其实就是为了要验证是不是应该要跳过这个bean 如果这个bean本身就是切面类的话 那么就不会被代理了
继续往下执行 如果没有beanclass所对应的targetsource的话 也一样不会代理的 targetsource相关的知识不再介绍 创建代理的地方其实是在postProcessAfterInitialization
预知后事如何 且听下回分析…