我们知道,使用面对对象编程的时候有一些弊端,当需要为多个不具有继承关系的对象引入同一个公共行为时,例如日志、安全检测等,所以就有了一个对面对对象编程的补充,即面对切面编程(AOP),AOP所关注的方向是横向的,不同于OOP的纵向。

Spring2.0采用@AspectJ注解来定义一个包含切点信息和增强横切逻辑的切面,这样的方案比采用配置的方案要来得更方便。

那要支持注解方式的AOP,首先需要在配置文件中添加一行配置

<aop:aspectj-autoproxy/>

我们的分析首先就从这里开始。之前自定义标签的随笔中提到了如何自定义标签的方法,这里的aop也是一个自定义配置,我们可以根据scheme文件定位到这个注解的具体解析器。

public class AopNamespaceHandler extends NamespaceHandlerSupport {
public AopNamespaceHandler() {
} public void init() {
this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}

可以看出,对于aspect-aotoproxy的解析工作在AspectJAutoProxyBeanDefinitionParser中

解析方法是这个类的parse方法

public BeanDefinition parse(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
this.extendBeanDefinition(element, parserContext);
return null;
}

继续进入到registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法中

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}

实际上这个方法主要做了三件事情:

1、注册或者升级AnnotationAwareAspectJAutoProxyCreator

2、处理proxy-target-class以及expose-proxy属性

3、最后注册组件并通知,便于监听器做进一步处理

先来看第一条,对于AOP的实现基本都是靠AnnotationAwareAspectJAutoProxyCreator去完成的

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if(registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
BeanDefinition beanDefinition1 = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
if(!cls.getName().equals(beanDefinition1.getBeanClassName())) {
int currentPriority = findPriorityForClass(beanDefinition1.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if(currentPriority < requiredPriority) {
beanDefinition1.setBeanClassName(cls.getName());
}
} return null;
} else {
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Integer.valueOf(-2147483648));
beanDefinition.setRole(2);
registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
return beanDefinition;
}
}

这段代码里首先先判断容器中是否已经存在了代理创建器,且与当前的不一致,如果存在,那么spring会根据代理创建器的优先级,确定使用哪一个代理创建器。

如果容器中不存在已有的代理创建器,那么spring就会生成一个默认的,且优先级最低的代理创建器。

接下来到了第二步

    private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
if(sourceElement != null) {
boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute("proxy-target-class")).booleanValue();
if(proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
} boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute("expose-proxy")).booleanValue();
if(exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
} }

这里对proxy-target-class属性和expose-proxy属性进行了设置。

proxy-target-class:AOP生成代理的默认策略是:如果被代理的目标对象实现了至少一个接口,那么就会使用JDK动态代理,所有该目标类型实现的接口都会被代理,如果该目标类型没有实现任何接口,那么spring就会生成一个CGLIB代理

proxy-target-class属性可以强制spring对目标类生成CGLIB代理。

expose-proxy:有时候目标对象内部的自我调用将无法实施切面中的增强,这时候就可以设置expose-proxy为true暴露代理,然后在自我调用的时候使用AopContext.currentProxy()获取当前的代理。

之前的内容讲解了通过自定义配置完成了对AnnotationAwareAspectJAutoProxyCreator的自动注册,那么这个类到底做了什么工作来完成AOP的工作呢?

我们看一下这个类的UML图

aaarticlea/png;base64,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" alt="" />

我们可以看到这个类实现了BeanPostProcessor这个接口,每当Spring加载这个Bean时会在实例化前调用其方法,说明对于aop的实现就在这里。

我们走进它实现的postprocessAfterInitialization这个方法。

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
} return bean;
}

这里的wrapIfNecessary方法根据字面意思可以看出就是对当前bean做了包装,我们进入到这个方法中

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
} else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
} else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}

首先在第一段if逻辑中,Spring检查了当前bean是否已经被代理过,在targetSourcedBeans中维护了所有已经被代理过的beanName,如果已经代理过,那么就只需直接返回即可。

第二段if逻辑中,Spring会判断当前bean是否在不需要代理的bean列表中,如果在话,那么就直接返回当前bean即可。

第三段if逻辑中,Spring判断了当前这个bean是否是基础设施类,如果是的话就无需对其做代理了。所谓的基础设施类根据源码来看就是一下几类

Advice、Pointcut、Advisor和AopInfrastructureBean

第四段那就说明当前bean无需做代理,那么在advisedBeans中当前的bean就被设置为无需代理的类

重要的逻辑显然在第三段if逻辑中,看一下这段

首先getAdvicesAndAdvisorsForBean这个方法是对增强方法生成代理,具体逻辑在findEligibleAdvisors中

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
this.extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
} return eligibleAdvisors;
}

首先是找到候选的增强器

protected List<Advisor> findCandidateAdvisors() {
List<Advisor> advisors = super.findCandidateAdvisors();
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
} return advisors;
}

这个方法中可以看到首先会调用父类的findCandidateAdvisors方法获取配置中的增强。

获取注解中的增强就是在buildAspectJAdvisors方法中

public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized(this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList();
List<String> aspectNames = new LinkedList();
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
String[] var18 = beanNames;
int var19 = beanNames.length; for(int var7 = 0; var7 < var19; ++var7) {
String beanName = var18[var7];
if (this.isEligibleBean(beanName)) {
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType != null && 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 {
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;
}
}
}

这段代码里

1)首先会获取容器中的所有bean,BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false),这个方法就是获取所有Object类的bean,实际上就是所有bean

2)如果当前bean是Aspect类型,那么根据aspect是否是单例,通过this.advisorFactory.getAdvisors(factory)来获取增强

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
this.validate(aspectClass);
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new LinkedList();
Iterator var6 = this.getAdvisorMethods(aspectClass).iterator(); while(var6.hasNext()) {
Method method = (Method)var6.next();
Advisor advisor = this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
} if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new ReflectiveAspectJAdvisorFactory.SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
} Field[] var12 = aspectClass.getDeclaredFields();
int var13 = var12.length; for(int var14 = 0; var14 < var13; ++var14) {
Field field = var12[var14];
Advisor advisor = this.getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
} return advisors;
}

首先在this.getAdvisorMethods方法中,会在当前aspect中找到所有非Pointcut注解的所有方法,并对方法进行排序。

拿到了所有方法后,会遍历所有方法并获取增强

接下来考虑到配置中可能会将增强配置成延迟初始化,那么需要在首位加入同步实例化增强器以保证增强使用之前的实例化

最后是是对DeclareParents注解的获取

我们首先看一下获取增强的方法

1)获取切点信息

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

这里会拿到所有的Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class注解,接下来会获取注解中的表达式,例如@Pointcut(execution(**.test*(..)))中的execution(**.test*(..))

拿到这些内容后将解析的结果封装到 AspectJExpressionPointcut类中

2)根据切点信息生成增强

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
this.declaredPointcut = declaredPointcut;
this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
this.methodName = aspectJAdviceMethod.getName();
this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
this.aspectJAdviceMethod = aspectJAdviceMethod;
this.aspectJAdvisorFactory = aspectJAdvisorFactory;
this.aspectInstanceFactory = aspectInstanceFactory;
this.declarationOrder = declarationOrder;
this.aspectName = aspectName;
if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
this.pointcut = new InstantiationModelAwarePointcutAdvisorImpl.PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory, null);
this.lazy = true;
} else {
this.pointcut = this.declaredPointcut;
this.lazy = false;
this.instantiatedAdvice = this.instantiateAdvice(this.declaredPointcut);
} }

这里就是讲增强的信息封装成InstantiationModelAwarePointcutAdvisorImpl类,同时针对不同的增强会通过不同的逻辑实现

@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
this.validate(candidateAspectClass);
AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
} else if (!this.isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]");
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Found AspectJ method: " + candidateAdviceMethod);
} Object springAdvice;
switch(aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning)aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
((AbstractAspectJAdvice)springAdvice).setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing)aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
((AbstractAspectJAdvice)springAdvice).setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtPointcut:
if (this.logger.isDebugEnabled()) {
this.logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
} return null;
default:
throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);
} ((AbstractAspectJAdvice)springAdvice).setAspectName(aspectName);
((AbstractAspectJAdvice)springAdvice).setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
((AbstractAspectJAdvice)springAdvice).setArgumentNamesFromStringArray(argNames);
} ((AbstractAspectJAdvice)springAdvice).calculateArgumentBindings();
return (Advice)springAdvice;
}
}

这里针对不同的增强类型生成了不同的增强器。增强器中实现了对于的接口方法,例如AtBefore实现了MethodBeforeAdvice接口,AtAfter则是实现了MethodInterceptor的invoke方法,在方法里使用了finilly代码块保证在所代理的方法执行后调用切面方法。

完成了所有增强器的解析后,接下来就需要从中挑选出使用于当前Bean的增强器, 这段逻辑在this.findAdvisorsThatCanApply方法中

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if(candidateAdvisors.isEmpty()) {
return candidateAdvisors;
} else {
LinkedList eligibleAdvisors = new LinkedList();
Iterator hasIntroductions = candidateAdvisors.iterator(); while(hasIntroductions.hasNext()) {
Advisor candidate = (Advisor)hasIntroductions.next();
if(candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
} boolean hasIntroductions1 = !eligibleAdvisors.isEmpty();
Iterator candidate2 = candidateAdvisors.iterator(); while(candidate2.hasNext()) {
Advisor candidate1 = (Advisor)candidate2.next();
if(!(candidate1 instanceof IntroductionAdvisor) && canApply(candidate1, clazz, hasIntroductions1)) {
eligibleAdvisors.add(candidate1);
}
} return eligibleAdvisors;
}
}

这部分代码并没有理解得特别清楚,待后续补充

在获取了所有对应bean的增强后,便可以进行代理的创建了

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if(!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
return new JdkDynamicAopProxy(config);
} else {
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.");
} else {
return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass)?new ObjenesisCglibAopProxy(config):new JdkDynamicAopProxy(config));
}
}
}

首先会根据策略判断是使用JDK代理还是CGLIB代理,判断的依据是:

1、optimize:是否使用激进的优化策略,除非完全了解AOP代理如何处理优化,否则不推荐使用这个配置

2、proxyTargetClass:这个属性为true时,会统一使用CGLIB生产代理

3、根据spring默认的代理策略,如果这个类实现了接口,那么就使用JDK代理,反之,使用CGLIB代理

确定了使用哪种代理方式后,就可以进行代理的创建了

1)JDK代理

JDK代理的核心实际上就是要创建InvocationHandler,Spring用的是继承InvocationHandler的JdkDynamicAopProxy

这里面有两个方法尤为关键

1、getProxy

    public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
} Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
this.findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

在这段代码中,首先获取了当前bean的所有接口,并通过Proxy.newProxyInstance方法,将所有接口代理成自己(也就是InvocationHandler)

2、invoke

invoke是InvocationHandler接口的一个接口方法,当代理的方法被调用时,实际上调用的就是这个方法

@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; Object var13;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
Boolean var19 = this.equals(args[0]);
return var19;
} if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
Integer var18 = this.hashCode();
return var18;
} if (method.getDeclaringClass() == DecoratingProxy.class) {
Class var17 = AopProxyUtils.ultimateTargetClass(this.advised);
return var17;
} Object retVal;
if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
return retVal;
} if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
} target = targetSource.getTarget();
Class<?> targetClass = target != null ? target.getClass() : null;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
} Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
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);
} var13 = retVal;
} finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
} if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
} } return var13;
}

首先,如果调用的是equals方法和hashcode方法,spring会做特殊的处理,经过一系列处理之后,接下来spring会拿到当前方法的拦截器链

如果拦截器链为空,那么就直接调用切点方法

如果有,那么会通过ReflectiveMethodInvocation对拦截器链进行封装

最后调用拦截器链

我们来看具体的调用方法

    public Object proceed() throws Throwable {
if(this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return this.invokeJoinpoint();
} else {
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if(interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)?dm.interceptor.invoke(this):this.proceed();
} else {
return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
}
}
}

这里逻辑比较简单,就是遍历每一个拦截器,并按顺序调用拦截器的invoke方法,如果是动态的,就需要匹配一下,如果是普通的拦截器,直接调用即可。

2)CGLIB代理

CGLIB是一个强大的高性能的代码生成包,底层通过使用一个小而快的字节码处理器框架ASM,来转换字节码并生成新的类

public Object getProxy(ClassLoader classLoader) {
if(logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
} try {
Class ex = this.advised.getTargetClass();
Assert.state(ex != null, "Target class must be available for creating a CGLIB proxy");
Class proxySuperClass = ex;
int x;
if(ClassUtils.isCglibProxyClass(ex)) {
proxySuperClass = ex.getSuperclass();
Class[] enhancer = ex.getInterfaces();
Class[] callbacks = enhancer;
int types = enhancer.length; for(x = 0; x < types; ++x) {
Class additionalInterface = callbacks[x];
this.advised.addInterface(additionalInterface);
}
} this.validateClassIfNecessary(proxySuperClass, classLoader);
Enhancer var12 = this.createEnhancer();
if(classLoader != null) {
var12.setClassLoader(classLoader);
if(classLoader instanceof SmartClassLoader && ((SmartClassLoader)classLoader).isClassReloadable(proxySuperClass)) {
var12.setUseCache(false);
}
} var12.setSuperclass(proxySuperClass);
var12.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
var12.setNamingPolicy(SpringNamingPolicy.INSTANCE);
var12.setStrategy(new CglibAopProxy.ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
Callback[] var13 = this.getCallbacks(ex);
Class[] var14 = new Class[var13.length]; for(x = 0; x < var14.length; ++x) {
var14[x] = var13[x].getClass();
} var12.setCallbackFilter(new CglibAopProxy.ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
var12.setCallbackTypes(var14);
return this.createProxyClassAndInstance(var12, var13);
} catch (CodeGenerationException var9) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass() + "]: Common causes of this problem include using a final class or a non-visible class", var9);
} catch (IllegalArgumentException var10) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass() + "]: Common causes of this problem include using a final class or a non-visible class", var10);
} catch (Exception var11) {
throw new AopConfigException("Unexpected AOP exception", var11);
}
}

CGLIB代理简单来说就是生成一个Enhancer类,然后设置它的代理对象,以及callback方法,

代理的逻辑就做callback里面

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
CglibAopProxy.DynamicAdvisedInterceptor aopInterceptor = new CglibAopProxy.DynamicAdvisedInterceptor(this.advised);
Object targetInterceptor;
if(exposeProxy) {
targetInterceptor = isStatic?new CglibAopProxy.StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()):new CglibAopProxy.DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
} else {
targetInterceptor = isStatic?new CglibAopProxy.StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()):new CglibAopProxy.DynamicUnadvisedInterceptor(this.advised.getTargetSource());
} Callback targetDispatcher = (Callback)(isStatic?new CglibAopProxy.StaticDispatcher(this.advised.getTargetSource().getTarget()):new CglibAopProxy.SerializableNoOp());
Callback[] mainCallbacks = new Callback[]{aopInterceptor, (Callback)targetInterceptor, new CglibAopProxy.SerializableNoOp(), targetDispatcher, this.advisedDispatcher, new CglibAopProxy.EqualsInterceptor(this.advised), new CglibAopProxy.HashCodeInterceptor(this.advised)};
Callback[] callbacks;
if(isStatic && isFrozen) {
Method[] methods = rootClass.getMethods();
Callback[] fixedCallbacks = new Callback[methods.length];
this.fixedInterceptorMap = new HashMap(methods.length); for(int x = 0; x < methods.length; ++x) {
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
fixedCallbacks[x] = new CglibAopProxy.FixedChainStaticTargetInterceptor(chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
this.fixedInterceptorMap.put(methods[x].toString(), Integer.valueOf(x));
} callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
this.fixedInterceptorOffset = mainCallbacks.length;
} else {
callbacks = mainCallbacks;
} return callbacks;
}

在这里spring会将advised封装至DynamicAdvisedInterceptor中,DynamicAdvisedInterceptor继承了methodInterceptor的接口,在实际调用的时候,会执行这个接口的invoke方法。

实际上JDK代理和CGLIB的实现方式大同小异,首先都是构造链,然后进行链的调用。

Spring源码学习(7)——AOP的更多相关文章

  1. spring源码学习之AOP(一)

    继续源码学习,看了spring中基础的容器和AOP感觉自己也没有什么长进,哈哈,我也不知道到底有用没有,这可能是培养自己的一种精神吧,不管那么多,继续学习!AOP中 AOP中几个重要的概念:(1)Ad ...

  2. spring源码学习之AOP(二)

    接着上一篇中的内容! 3.创建代理 在获取了所有的bean对应的增强器之后,便可以进行代理的创建了org.springframework.aop.framework.autoproxy包下的Abstr ...

  3. spring源码学习之路---深入AOP(终)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章和各位一起看了一下sp ...

  4. Spring 源码学习——Aop

    Spring 源码学习--Aop 什么是 AOP 以下是百度百科的解释:AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程通过预编译的方式和运行期动态代理实 ...

  5. Spring 源码学习笔记10——Spring AOP

    Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...

  6. spring源码学习之路---IOC初探(二)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...

  7. Spring源码学习

    Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...

  8. Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件

    写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...

  9. 【目录】Spring 源码学习

    [目录]Spring 源码学习 jwfy 关注 2018.01.31 19:57* 字数 896 阅读 152评论 0喜欢 9 用来记录自己学习spring源码的一些心得和体会以及相关功能的实现原理, ...

  10. Spring 源码学习笔记11——Spring事务

    Spring 源码学习笔记11--Spring事务 Spring事务是基于Spring Aop的扩展 AOP的知识参见<Spring 源码学习笔记10--Spring AOP> 图片参考了 ...

随机推荐

  1. jenkin服务关闭和重启

    1.关闭Jenkins http://localhost:8080/exit 2.重启Jenkies http://localhost:8080/restart 3.重新加载配置信息 http://l ...

  2. [Linux]最新sublime text 3显示图标

    sublime text 3显示图标 执行命令 sudo vim /usr/share/applications/sublime_text_3.desktop 添加相应信息 [Desktop Entr ...

  3. java程序可以跨平台运行的原因

    java有虚拟机(JVM),JAVA程序不是直接在电脑上运行的,是在虚拟机上进行的,每个系统平台都是有自己的虚拟机(JVM),所以JAVA语言能跨平台. 1, java代码不是直接运行在CPU上,而是 ...

  4. zoj2930

      各点向S连推迟的花费,向T连提前的花费,S表示提前,T表示推迟.a推迟b也推迟b往a连INF.最小割后从各点出发,能直接或间接到T的就是必须推迟的,剩下的就是能提前的. #include < ...

  5. android -------- Hawk数据库

    Hawk 是一个非常便捷的数据库  . 操作数据库只需一行代码 , 能存任何数据类型 . github 地址: https://github.com/orhanobut/hawk 一.概念 Share ...

  6. 虚函数 error LNK2001: 无法解析的外部符号 "public: virtual void __cdecl

    在虚函数后面加一对大括号 #ifndef CAFFE_CONV_DW_LAYER_HPP_ #define CAFFE_CONV_DW_LAYER_HPP_ #include <vector&g ...

  7. Shovel Sale CodeForces - 899D (数位dp)

    大意: n把铲子, 价格1,2,3,...n, 求有多少个二元组(x,y), 满足x+y末尾数字9的个数最多. 枚举最高位, 转化为从[1,n]中选出多少个二元组和为$x$, 枚举较小的数 若$n\g ...

  8. github项目上传与克隆

    1. 先去git官网https://git-scm.com/下载git: 2. 桌面新建文件夹,例如project,文件夹中新建任意文件例如index.html: 3. 打开文件夹,按住shift+右 ...

  9. C#数组--(Array类的属性和方法)

    Array 类是 C# 中所有数组的基类,它是在 System 命名空间中定义.Array 类提供了各种用于数组的属性和方法,可看作扩充了功能的数组(但不等同数组),可以使用Array类的属性来对数组 ...

  10. RabbitMQ安装笔记

    前言 项目中某些场景考虑到高并发情况,调研后决定使用RabbitMQ,本来以为很简单,没想到配置环境花费了好多时间,按照网上的方法来,总是有其他问题需要继续查找,特记录此笔记,方便下次部署安装. 本笔 ...