本文共 7766 字,大约阅读时间需要 25 分钟。
开发碰到了一个奇怪的异常如下
Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
错误原因是引入了xxl-job和springsecurity。翻了下xxl-job官方issue如下 自己跟了下代码 XxlJobSpringExecutor#initJobHandlerMethodRepository
private void initJobHandlerMethodRepository(ApplicationContext applicationContext) { //省略。。。。 String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { // 代码在此处抛了异常 Object bean = applicationContext.getBean(beanDefinitionName); //省略代码
AbstractBeanFactory#doGetBean
跟到代码块如下
//异常抛出位置Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
进入Scope.get发现其实现如下。
根据异常可知scopedTarget.oauth2ClientContext
这个bean
的Scope
为request
,所以它走到了AbstractRequestAttributesScope
这个实现中 AbstractRequestAttributesScope#get
代码如下在第一行时就抛出了异常 @Override public Object get(String name, ObjectFactory objectFactory) { RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); Object scopedObject = attributes.getAttribute(name, getScope()); if (scopedObject == null) { scopedObject = objectFactory.getObject(); attributes.setAttribute(name, scopedObject, getScope()); // Retrieve object again, registering it for implicit session attribute updates. // As a bonus, we also allow for potential decoration at the getAttribute level. Object retrievedObject = attributes.getAttribute(name, getScope()); if (retrievedObject != null) { // Only proceed with retrieved object if still present (the expected case). // If it disappeared concurrently, we return our locally created instance. scopedObject = retrievedObject; } } return scopedObject; }
然而我们xxl-job并不需要拿这个bean,xxl-job需要的bean都是单例的。但是我们又不想去修改xxl-job代码怎么办呢?
那就新写个类XxlJobSpringExecutorProxy继承XxlJobSpringExecutorpublic class XxlJobSpringExecutor extends XxlJobSpringExecutor { @Override public void afterPropertiesSet() throws Exception { // init JobHandler Repository initJobHandlerRepository(XxlJobSpringExecutor .getApplicationContext); // init JobHandler Repository (for method) initJobHandlerMethodRepository(XxlJobSpringExecutor .getApplicationContext); // refresh GlueFactory GlueFactory.refreshInstance(1); // super start super.start(); } // destroy @Override public void destroy() { super.destroy(); } private void initJobHandlerRepository(ApplicationContext applicationContext) { if (applicationContext == null) { return; } // init job handler action MapserviceBeanMap = applicationContext.getBeansWithAnnotation(JobHandler.class); if (serviceBeanMap != null && serviceBeanMap.size() > 0) { for (Object serviceBean : serviceBeanMap.values()) { if (serviceBean instanceof IJobHandler) { String name = serviceBean.getClass().getAnnotation(JobHandler.class).value(); IJobHandler handler = (IJobHandler) serviceBean; if (loadJobHandler(name) != null) { throw new RuntimeException("xxl-job jobhandler[" + name + "] naming conflicts."); } registJobHandler(name, handler); } } } } private void initJobHandlerMethodRepository(ApplicationContext applicationContext) { if (applicationContext == null) { return; } // 修改掉这一行,然后初始化的时候使用我们自己这个类即可,意味只获取单例的beanDefinitionNames这样就不会取到Scope为其它的bean。 String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true); for (String beanDefinitionName : beanDefinitionNames) { Object bean = applicationContext.getBean(beanDefinitionName); Method[] methods = bean.getClass().getDeclaredMethods(); for (Method method: methods) { XxlJob xxlJob = AnnotationUtils.findAnnotation(method, XxlJob.class); if (xxlJob != null) { // name String name = xxlJob.value(); if (name.trim().length() == 0) { throw new RuntimeException("xxl-job method-jobhandler name invalid, for[" + bean.getClass() + "#"+ method.getName() +"] ."); } if (loadJobHandler(name) != null) { throw new RuntimeException("xxl-job jobhandler[" + name + "] naming conflicts."); } // execute method if (!(method.getParameterTypes()!=null && method.getParameterTypes().length==1 && method.getParameterTypes()[0].isAssignableFrom(String.class))) { throw new RuntimeException("xxl-job method-jobhandler param-classtype invalid, for[" + bean.getClass() + "#"+ method.getName() +"] , " + "The correct method format like \" public ReturnT execute(String param) \" ."); } if (!method.getReturnType().isAssignableFrom(ReturnT.class)) { throw new RuntimeException("xxl-job method-jobhandler return-classtype invalid, for[" + bean.getClass() + "#"+ method.getName() +"] , " + "The correct method format like \" public ReturnT execute(String param) \" ."); } method.setAccessible(true); // init and destory Method initMethod = null; Method destroyMethod = null; if(xxlJob.init().trim().length() > 0) { try { initMethod = bean.getClass().getDeclaredMethod(xxlJob.init()); initMethod.setAccessible(true); } catch (NoSuchMethodException e) { throw new RuntimeException("xxl-job method-jobhandler initMethod invalid, for[" + bean.getClass() + "#"+ method.getName() +"] ."); } } if(xxlJob.destroy().trim().length() > 0) { try { destroyMethod = bean.getClass().getDeclaredMethod(xxlJob.destroy()); destroyMethod.setAccessible(true); } catch (NoSuchMethodException e) { throw new RuntimeException("xxl-job method-jobhandler destroyMethod invalid, for[" + bean.getClass() + "#"+ method.getName() +"] ."); } } // registry jobhandler registJobHandler(name, new MethodJobHandler(bean, method, initMethod, destroyMethod)); } } } }
大功告成。
转载地址:http://atkfb.baihongyu.com/