前言
博客有段时间没更新了,短短几个月好像发生了不少事情,心情受到一些影响,花了点时间进行复盘,重新调整情绪。这段时间 AI 进步很快,尝试 Vibe Coding 了一段时间,确实好用,开发效率提高了不少,但写代码和文章的成就感降低了很多,加上本来也懒,好像干什么事情都没有动力
在上一篇文章中,问题拆解和整体思路主要是自己整理,具体的实现方案主要是通过问 ChatGPT 和查阅资料选择的,如何实现已经有清晰的规划步骤了,但现在的一些智能 IDE 例如 trae 等已经比较重视方法论,可以拆解问题和实现步骤,形成执行计划,而且拆分得比较详细,不得不感叹现在 Vibe Coding 的门槛变得更低了,不懂代码的人也能做出一个 demo 级别的产品,但也说明了之前使用 AI 的思路没有问题,只是未来可能要往更高层次的思维和方法论去使用了,想法和思路依然重要,需要清楚得知道需求和边界是什么
继续深入研究源码依然有用,而且源码中的实现思路和设计模式,性能优化等内容依然值得我们学习和模仿,可以开阔实现功能的思路,只是使用 AI 辅助学习效率会更高。现在初中级程序员因为有 AI 的辅助,除非刻意去学习,否则会越来越难掌握编程经验,至少能够看懂 AI 是不是在瞎说瞎写,知道代码哪里出了问题,修改的方向,AI 改得是否正确,才能更高效得使用 AI
现阶段来看,对原本行业和工程经验丰富的人来说,懂整体流程和运作逻辑,熟悉业务,有想法,使用 AI 会如虎添翼,例如架构师,技术/产品经理等
也要好好琢磨一下以后的学习方向和博客内容了,想做的事情挺多,但执行力不够,越来越迷茫,哎
ps:年初看了辉夜姬,真是太赞了!好久没有因为看动画这么兴奋了。杀戮尖塔2终于出了,几年前在老朋友的推荐下玩过第一部,后来因为一些事情弃坑,期间朋友约过好多次一起玩游戏,但那些游戏我都不是很感兴趣,当时个人时间不是很充裕,更想做自己的事情,只能约定出第二部的时候再一起玩,如今也不能食言
@EnableAspectJAutoProxy
Springboot 在启动的过程中,通过约定大于配置的方式,扫描 spring.factories 文件加载需要的 Bean,其中 AOP 是在 AopAutoConfiguration 自动配置类中声明了 @EnableAspectJAutoProxy 注解

需要配置 spring.aop.auto,默认为 true,还需要一些 Advice 等类存在才会生效
重点看 @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 实现了 ImportBeanDefinitionRegistrar 扩展注册了 AnnotationAwareAspectJAutoProxyCreator.class
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { 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); } }
}}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);}AspectJAutoProxyRegistrar
查看该类的继承和实现情况,主要是实现了 InstantiationAwareBeanPostProcessor 接口进行扩展,在 Bean 的生命周期中处理动态代理

需要区分一下
BeanPostProcessor 有两个方法:
- postProcessBeforeInitialization,在 Bean 初始化之前执行
- postProcessAfterInitialization,在 Bean 初始化之后执行
InstantiationAwareBeanPostProcessor 继承了 BeanPostProcessor,提供了
- postProcessBeforeInstantiation,在 Bean 实例化之前执行
- postProcessAfterInstantiation,在 Bean 实例化之后执行(还未初始化)
这里调用的是 postProcessBeforeInstantiation方法,在 Bean 实例化之前执行
postProcessBeforeInitialization
这里主要是处理 targetSource 的逻辑,如果这个 Bean 实现了 targetSource 接口,会在这里先生成动态代理,不创建 Bean 实例了
@Overridepublic 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)) { // 缓存不需要创建动态代理的 Bean 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;}
@Overrideprotected 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 && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) { return true; } } return super.shouldSkip(beanClass, beanName);}
protected List<Advisor> findCandidateAdvisors() { Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available"); return this.advisorRetrievalHelper.findAdvisorBeans();}
public List<Advisor> findAdvisorBeans() { // Determine list of advisor bean names, if not cached already. String[] advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the auto-proxy creator apply to them! advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); // 缓存 Advisor,避免重复扫描 this.cachedAdvisorBeanNames = advisorNames; } if (advisorNames.length == 0) { return new ArrayList<>(); }
List<Advisor> advisors = new ArrayList<>(); for (String name : advisorNames) { if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isTraceEnabled()) { logger.trace("Skipping currently created advisor '" + name + "'"); } } else { try { advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { Throwable rootCause = ex.getMostSpecificCause(); if (rootCause instanceof BeanCurrentlyInCreationException) { BeanCreationException bce = (BeanCreationException) rootCause; String bceBeanName = bce.getBeanName(); if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) { if (logger.isTraceEnabled()) { logger.trace("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } // Ignore: indicates a reference back to the bean we're trying to advise. // We want to find advisors other than the currently created bean itself. continue; } } throw ex; } } } } return advisors;}TargetSource
实现 TargetSource 的 getTarget 方法,可以自定义 AOP 对象的获取方式,例如从对象池中获取对象,一般是自己创建动态代理的时候使用
在 postProcessBeforeInstantiation 中很少会遇到使用 targetSource 的情况,这里就简单介绍一下。不过需要注意在 shouldSkip 方法中会调用 findCandidateAdvisors 方法找出所有的 Advisor 切面,并缓存到 cachedAdvisorBeanNames,避免下次重复扫描
然后在advisedBeans中缓存记录不需要生成动态代理的 Bean,在后面postProcessAfterInitialization方法执行的时候避免重复创建动态代理
postProcessAfterInitialization
Bean 实例化后,从findCandidateAdvisors方法的cachedAdvisorBeanNames缓存中获取所有切面(例如 @Before 等注解),然后通过findAdvisorsThatCanApply方法筛选出当前 Bean 符合切面执行条件的 Advisor
@Overridepublic 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;}
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;}
protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray();}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 获取所有 Advisor 切面(@Before等注解) List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 筛选出当前 Bean 符合切面条件的 Advisor List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // advisors 里包含需要 MethodInvocation 的 Advice,会添加 ExposeInvocationInterceptor(添加到 advisors 列表的最前面) extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors;}如果有符合当前 Bean 的 Advisor,就会调用 createProxy 方法,把 Advisor 设置到 proxyFactory 中,然后调用 proxyFactory.getProxy(classLoader)通过代理工厂生成动态代理
protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
// ...
ProxyFactory proxyFactory = new ProxyFactory();
// ...
// 这里把 advisors 添加到 proxyFactory 中 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); // 这里会使用原来的 ClassLoader // Use original ClassLoader if bean class not locally loaded in overriding class loader ClassLoader classLoader = getProxyClassLoader(); if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) { classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader(); } // 通过代理工厂生成动态代理 return proxyFactory.getProxy(classLoader);}
public Object getProxy(@Nullable ClassLoader classLoader) { return createAopProxy().getProxy(classLoader);}
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); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); }}createAopProxy 方法会判断当前类是否实现了接口,是否强制使用 cglib 等判断创建对应的 AopProxy 对象然后调用对应的 getProxy 方法创建动态代理对象

代理对象都会实现 advised 接口,目的是为了兼容类型,这里以 Cglib 为例
@Overridepublic Object getProxy(@Nullable ClassLoader 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 ClassLoaderAwareGeneratorStrategy(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);}
private Callback[] getCallbacks(Class<?> rootClass) throws Exception { // Parameters used for optimization choices...
// Choose an "aop" interceptor (used for AOP calls). Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
//...
Callback[] mainCallbacks = new Callback[] { aopInterceptor, // for normal advice targetInterceptor, // invoke target without considering advice, if optimized new SerializableNoOp(), // no override for methods mapped to this targetDispatcher, this.advisedDispatcher, new EqualsInterceptor(this.advised), new HashCodeInterceptor(this.advised) };
Callback[] callbacks;
// If the target is a static one and the advice chain is frozen, // then we can make some optimizations by sending the AOP calls // direct to the target using the fixed chain for that method. if (isStatic && isFrozen) { //...
// Now copy both the callbacks from mainCallbacks // and fixedCallbacks into the callbacks array. 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;}cglib 动态代理对象会把 DynamicAdvisedInterceptor 设置到第一个 callbacks 里,所以在动态代理方法被调用时,入口是 DynamicAdvisedInterceptor.intercept 方法
jdk 动态代理在创建时 InvocationHandler 设置成 this,所以代理方法被调用时,入口是 JdkDynamicAopProxy.invoke 方法
public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } // this 是 JdkDynamicAopProxy return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);}AOP 拦截器
在动态代理方法被调用时,都会在入口方法调用 this.advised.getInterceptorsAndDynamicInterceptionAdvice 方法筛选出匹配代理方法的 Advisor转换成 MethodInterceptor 放到缓存中,最后通过责任链模式执行拦截器
以 cglib 为例
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 { // proceed 方法执行拦截链 // 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); } }}
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) { MethodCacheKey cacheKey = new MethodCacheKey(method); List<Object> cached = this.methodCache.get(cacheKey); if (cached == null) { // 缓存拦截器 cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this, method, targetClass); this.methodCache.put(cacheKey, cached); } return cached;}
public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, @Nullable Class<?> targetClass) {
// 获取生成动态代理时设置的 advisors // This is somewhat tricky... We have to process introductions first, // but we need to preserve order in the ultimate list. AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); Advisor[] advisors = config.getAdvisors(); List<Object> interceptorList = new ArrayList<>(advisors.length); Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); Boolean hasIntroductions = null;
for (Advisor advisor : advisors) { if (advisor instanceof PointcutAdvisor) { // Add it conditionally. PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); boolean match; if (mm instanceof IntroductionAwareMethodMatcher) { if (hasIntroductions == null) { hasIntroductions = hasMatchingIntroductions(advisors, actualClass); } // 匹配代理方法是否符合 advisor 条件 match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions); } else { match = mm.matches(method, actualClass); } if (match) { // 如果匹配转换成 MethodInterceptor MethodInterceptor[] interceptors = registry.getInterceptors(advisor); if (mm.isRuntime()) { // Creating a new object instance in the getInterceptors() method // isn't a problem as we normally cache created chains. for (MethodInterceptor interceptor : interceptors) { interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); } } else { interceptorList.addAll(Arrays.asList(interceptors)); } } } } else if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } } else { Interceptor[] interceptors = registry.getInterceptors(advisor); interceptorList.addAll(Arrays.asList(interceptors)); } }
return interceptorList;}拦截器的执行流程就不继续跟踪了,调试起来有点麻烦,有兴趣可以自行 debug 跟踪一下,主要使用了责任链模式,但有点不一样的地方是通过数组缓存拦截器,计算数组下标的方式实现责任链,这种方式减少了对象的创建,性能更好
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); }}总结
AOP 动态代理创建流程
- Spring 启动通过 AopAutoConfiguration 配置类引入了@EnableAspectJAutoProxy注解
- @EnableAspectJAutoProxy注解中 import 了 AspectJAutoProxyRegistrar 类
- AspectJAutoProxyRegistrar 实现了 ImportBeanDefinitionRegistrar 扩展注册了 AnnotationAwareAspectJAutoProxyCreator 类
- AnnotationAwareAspectJAutoProxyCreator 类实现了 BeanPostProcessor 接口
- postProcessBeforeInstantiation 方法(主要处理 TargetSource)在 Bean 初始化前会调用 findCandidateAdvisors 缓存 Advisor
- 通过 postProcessAfterInitialization 方法在 Bean 初始化之后调用
- 查找当前的 Bean 是否有 Advisor
- findCandidateAdvisors(),查找出所有的切面
- findAdvisorsThatCanApply(),筛选出当前当前类符合的 Advisor
- 如果有,调用 createProxy 方法创建动态代理
- createProxy 会根据当前类是否实现了接口,是否强制使用 cglib 判断使用哪种代理方式
- 并且都会实现 advised 的接口,目的是为了兼容类型
AOP 拦截器创建流程
- 代理方法被调用时
- cglib 代理类,把 DynamicAdvisedInterceptor 放在 callbacks 的第一个,所以 DynamicAdvisedInterceptor.intercept 方法是入口
- jdk 代理,JdkDynamicAopProxy.invoke 方法是入口
- 它们都通过 this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 获取生成动态代理时设置的 Advisor,转换成 MethodInterceptor
- 然后使用 ReflectiveMethodInvocation.proceed 方法执行责任链