在bean初始化階段,經過cglib代理方式將其適配爲實現 IJobHandler接口 的自定義對象XxlJobTraceWrapper。(保證bean實例的真實class類型是一致的)java
真正執行時調用invoke方法先從beanFactory中獲取Tracing對象開啓新的Span。spring
(也能夠相似於仿@EnableAsync 實現自定義切面日誌 文章中的方案,對IJobHandler.execute()構建 StaticMethodMatcherPointcut 的Advisor方式來實現切面)app
XxlJobBeanPostProcessoride
import java.lang.reflect.Modifier; import org.aopalliance.aop.Advice; import org.springframework.aop.framework.AopConfigException; import org.springframework.aop.framework.ProxyFactoryBean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; import com.xxl.job.core.handler.IJobHandler; import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** * xxjob入口增長trace日誌 * * @author admin * */ @Component @Slf4j public class XxlJobBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { @Setter private BeanFactory beanFactory; @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { /** * 不能直接用XxjobHnadlerTraceWrapper進行包裝,返出去的類型不匹配。只能經過代理方式 */ return bean instanceof IJobHandler ? wrap(bean) : bean; } private Object wrap(Object bean) { boolean classFinal = Modifier.isFinal(bean.getClass().getModifiers()); boolean cglibProxy = !classFinal; IJobHandler job = (IJobHandler) bean; try { return createProxy(bean, cglibProxy, new XxlJobMethodInterceptor<IJobHandler>(job, this.beanFactory)); } catch (AopConfigException ex) { if (cglibProxy) { if (log.isDebugEnabled()) { log.debug("Exception occurred while trying to create a proxy, falling back to JDK proxy", ex); } return createProxy(bean, false, new XxlJobMethodInterceptor<IJobHandler>(job, this.beanFactory)); } throw ex; } } Object createProxy(Object bean, boolean cglibProxy, Advice advice) { ProxyFactoryBean factory = new ProxyFactoryBean(); factory.setProxyTargetClass(cglibProxy); factory.addAdvice(advice); factory.setTarget(bean); return factory.getObject(); } }
XxlJobMethodInterceptorpost
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.beans.factory.BeanFactory; import org.springframework.util.ReflectionUtils; import com.xxl.job.core.handler.IJobHandler; import lombok.AllArgsConstructor; @AllArgsConstructor public class XxlJobMethodInterceptor<T extends IJobHandler> implements MethodInterceptor { private final T delegate; private final BeanFactory beanFactory; @Override public Object invoke(MethodInvocation invocation) throws Throwable { IJobHandler jobHandler = new XxlJobTraceWrapper(this.beanFactory, this.delegate); Method methodOnTracedBean = getMethod(invocation, jobHandler); if (methodOnTracedBean != null) { try { return methodOnTracedBean.invoke(jobHandler, invocation.getArguments()); } catch (InvocationTargetException ex) { Throwable cause = ex.getCause(); throw (cause != null) ? cause : ex; } } return invocation.proceed(); } private Method getMethod(MethodInvocation invocation, Object object) { Method method = invocation.getMethod(); return ReflectionUtils.findMethod(object.getClass(), method.getName(), method.getParameterTypes()); } }
XxlJobTraceWrapperui
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.SpanNamer; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.handler.IJobHandler; import brave.ScopedSpan; import brave.Tracing; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @RequiredArgsConstructor public class XxlJobTraceWrapper extends IJobHandler { private final BeanFactory beanFactory; private final IJobHandler delegate; private Tracing tracing; private SpanNamer spanNamer; @Override public ReturnT<String> execute(String param) throws Exception { if (this.tracing == null) { try { this.tracing = this.beanFactory.getBean(Tracing.class); } catch (NoSuchBeanDefinitionException e) { return this.delegate.execute(param); } } return doExecute(param); } private ReturnT<String> doExecute(String param) throws Exception { ScopedSpan span = this.tracing.tracer().startScopedSpanWithParent(spanNamer().name(delegate, "XXJOB"), this.tracing.currentTraceContext().get()); try { return this.delegate.execute(param); } catch (Exception | Error e) { span.error(e); throw e; } finally { span.finish(); } } // due to some race conditions trace keys might not be ready yet private SpanNamer spanNamer() { if (this.spanNamer == null) { try { this.spanNamer = this.beanFactory.getBean(SpanNamer.class); } catch (NoSuchBeanDefinitionException e) { log.warn("SpanNamer bean not found - will provide a manually created instance"); return new DefaultSpanNamer(); } } return this.spanNamer; } }