Spring AOP 机制源码解读
导读
- AOP是Spring框架面向切面的编程思想,AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中。
- 本文不会讲解Spring切面的使用方法以及Spring切面的定义,但会通过一个示例引入话题。
- 本文设计内容主要说明Spring内部如何帮我们做切面处理,Spring是如何实现切面的,虽然Spring切面的底层实现还是基于动态代理,但是本文并不会讲解动态代理,如果读者不太了解动态代理,还需先连接器原理后再进行本文的解读,本文解读需要一定的基础:如Spring bean的加载机制,Spring注解功能,Spring后置处理器,Spring BeanFactory。
一 示例引入
1.1 目标方法
下面是测试所用的目标方法,使用如下一个打印方法进行测试:
方法很简单,参入两个int类型的输入参数,打印入参,返回两个参数的除;
我们的需求是:在目标方法运行的时候进行日志输出(方法运行前,方法运行结束,或者方法运行出现异常的时候,...)
public class MainPrint {
public int print(int i, int j) {
System.out.println("i = " + i + ", j = " + j);
return i / j;
}
}
1.2 定义切换类
定义切面如下所示:
- 通过注解@Aspect告知Spring 该类是一个切面类。
- 定义前置增加(@Before):logStart:在目标方法(div)运行之前运行
- 定义后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
- 返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
- 异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
- 环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced());
@Aspect
public class LogsAspects {
/**
* 定义切入点
*/
@Pointcut("execution(public int org.mzw.spring.annotation.aop.MainPrint.*(..))")
public void pointCut(){};
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}");
}
@After("org.mzw.spring.annotation.aop.LogsAspects.pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
}
/**
* 获取返回值
* 注意:JoinPoint一定要出现在参数表的第一位
*/
@AfterReturning(value="pointCut()",returning="result")
public void logReturn(JoinPoint joinPoint,Object result){
System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
}
@AfterThrowing(value="pointCut()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception){
System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
}
}
1.3 基于注解配置
- @EnableAspectJAutoProxy 通过添加该注解告知Spring启动AspectJAutoProxy 开启基于注解的aop模式
@EnableAspectJAutoProxy
@Configuration
public class ConfigOfAOP {
//业务逻辑类加入容器中
@Bean
public MainPrint calculator(){
return new MainPrint();
}
//切面类加入到容器中
@Bean
public LogsAspects logsAspects(){
return new LogsAspects();
}
}
1.4 Main 启动容器
private static void starterSpringOfAop(Class<?>... clazz) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(clazz);
MainPrint bean = applicationContext.getBean(MainPrint.class);
int print = bean.print(2, 4);
System.out.println(print);
}
输出结果:
print运行。。。@Before:参数列表是:{[2, 4]}
i = 2, j = 4
print结束。。。@After
print正常返回。。。@AfterReturning:运行结果:{0}
0
实例很简单,接下类看看这简单的背后Spring是如何处理的。
在上面示例中,@Aspect告知Spring这是一个切面逻辑,只有另一处代码我们需要关注,那就是@EnableAspectJAutoProxy 该注解告知Spring 开启切面功能。
所以我们需要重点关注下@EnableAspectJAutoProxy到底做了哪些骚操作
。
二 AOP代理创建源码解读
开启基于注解的aop模式;@EnableAspectJAutoProxy
了解Spring IOC的可以知道,Spring在启动的时候会注册一些Bean,不同的Bean在不同的时机做不同的事情,所以我们也效仿这个模板去分析下Spring的AOP原理(容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?)
2.1 EnableAspectJAutoProxy
2.1.1 @EnableAspectJAutoProxy 是什么?
我们在Spring的配置类上添加了这一注解@EnableAspectJAutoProxy ,所以我们先分析下这个注解到底是做什么的?
下面是EnableAspectJAutoProxy的源码:这个是一个注解组件,除了注解常用的几个用于注解的注解之外我们会发现一个特殊的注解@Import(AspectJAutoProxyRegistrar.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
*/
boolean proxyTargetClass() default false;
/**
* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
* Off by default, i.e. no guarantees that {@code AopContext} access will work.
* @since 4.3.1
*/
boolean exposeProxy() default false;
}
@Import 该组件可以向容器中注册Bean,
- {@link Configuration},
- {@link ImportSelector},
- {@link ImportBeanDefinitionRegistrar} * or regular component classes to import.
我们看到AspectJAutoProxyRegistrar
应该是一个BeanDefinitionRegistrar, 给容器中导入AspectJAutoProxyRegistrar
2.1.2 AspectJAutoProxyRegistrar.class
利用AspectJAutoProxyRegistrar
自定义给容器中注册bean;BeanDefinetion
源码如下:
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) {
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);
}
}
}
}
2.1.3 registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
查看方法最终是给容器中注册一个AnnotationAwareAspectJAutoProxyCreator.class
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
2.1.4 @EnableAspectJAutoProxy 注解小结
- @EnableAspectJAutoProxy :利用
@Import
给容器中导入AspectJAutoProxyRegistrar
; - 利用AspectJAutoProxyRegistrar自定义给容器中注册bean;
AnnotationAwareAspectJAutoProxyCreator
所以@EnableAspectJAutoProxy 注解的功能最终就是给Spring Ioc容器中注册了一个Bean:AnnotationAwareAspectJAutoProxyCreator
2.2 AnnotationAwareAspectJAutoProxyCreator
我们先分析下AnnotationAwareAspectJAutoProxyCreator 这是一个什么组件,看下该类的继承关系:如下图(简化后的:排除一些无关此话题的依赖树):
AnnotationAwareAspectJAutoProxyCreator 2.png上图中关注标红的组件:
- implements SmartInstantiationAwareBeanPostProcessor 后置处理器
- BeanFactoryAware 自动装配BeanFactory
知道了这个组件是一个什么东西后我们就可以分析它具体做什么事情?
- 关注后置处理器,在bean初始化完成前后做事情?
- 装配BeanFactory
这里暂时不说SpringIOC是如何加载AnnotationAwareAspectJAutoProxyCreator的逻辑,这里暂时只分析AnnotationAwareAspectJAutoProxyCreator的作用,后续有机会再针对Spring加载AnnotationAwareAspectJAutoProxyCreator进行说明。
2.2.1 AbstractAutoProxyCreator
在该AnnotationAwareAspectJAutoProxyCreator组件的超类AbstractAutoProxyCreator 中显示的装配了BeanFactory
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
同时在该AbstractAutoProxyCreator组件中实现了后置处理器的逻辑
此处需要特殊说明下:
- 一般我们实现BeanPostProcess 实现的方法是postProcessBeforeInitialization/postProcessAfterInitialization,该方法是Bean在调用初始化方法前后被调用,
- 而此处我们实现InstantiationAwareBeanPostProcessor的方法是postProcessBeforeInstantiation/postProcessAfterInstantiation 该方法是在Bean实例化的前后进行调用;
补充说明:AbstractAutoProxyCreator实现了InstantiationAwareBeanPostProcessor 所以AnnotationAwareAspectJAutoProxyCreator组件的作用是在每个Bean创建之前调用postProcessBeforeInstantiation()
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
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.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
2.2.2 方法 postProcessBeforeInstantiation
该方法一直跳过,所以此处不进行分析
// 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.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
2.2.3 postProcessAfterInitialization
- 尝试从缓存中获取bean名称
- 是否是早期代理引用
- 如果不是则进行增强尝试
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
关注该方法wrapIfNecessary 见名知意: 如果需要则进行增强,
2.2.3 wrapIfNecessary
getAdvicesAndAdvisorsForBean.jpg- 判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
- 判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean, 或者是否是切面(@Aspect)
- 是否需要跳过
- 获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
- 永远返回false
- 创建代理对象
- 放入缓存
- 返回代理对象
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return 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;
}
createProxy 创建代理对象
下面是createProxy 调用链
return proxyFactory.getProxy(getProxyClassLoader());
- getProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
- createAopProxy
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
- getAopProxyFactory().createAopProxy
DefaultAopProxyFactory#createAopProxy
这里判断使用哪种进行代理 - 如果目标对象实现接口,使用JDK动态代理技术 -> new JdkDynamicAopProxy(config);
- 如果目标对象没有实现接口,则使用cglib动态代理技术 -> new ObjenesisCglibAopProxy(config);
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (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);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
然后调用具体实现的代理工厂的getProxy 创建代理,
下面是CglibAopProxy的getProxy方法实现:
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
返回代理对象
createProxy.jpg到此代理对象创建完毕!
AOP 代理方法执行逻辑源码解析
代码中我们获取代理对象,并执行目标方法
bean.print(2, 4);
private static void starterSpringOfAop(Class<?>... clazz) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(clazz);
MainPrint bean = applicationContext.getBean(MainPrint.class);
int print = bean.print(2, 4);
System.out.println(print);
}
这里我们断点打在bean.print(2, 4); 观察下
intercept.jpg
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
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);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 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 = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
2.3.1 核心代码CglibMethodInvocation.proceed
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
Object[] arguments, @Nullable Class<?> targetClass,
List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
// Only use method proxy for public methods not derived from java.lang.Object
this.methodProxy = (Modifier.isPublic(method.getModifiers()) &&
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method) ?
methodProxy : null);
}
2.3.2 proceed
执行步骤:
- 容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
- 根据ProxyFactory对象获取将要执行的目标方法拦截器链;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
- List<Object> interceptorList保存所有拦截器 : 一个默认的ExposeInvocationInterceptor 和 4个增强器;
- 遍历所有的增强器,将其转为Interceptor;registry.getInterceptors(advisor);
- 将增强器转为List<MethodInterceptor>;如果是MethodInterceptor,直接加入到集合中,如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;转换完成返回MethodInterceptor数组;
- 如果没有拦截器链,直接执行目标方法;拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
- 如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个 CglibMethodInvocation 对象,并调用 Object retVal = mi.proceed();
- 拦截器链的触发过程; 如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;拦截器链的机制,保证通知方法与目标方法的执行顺序;
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
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 {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
网友评论