SpringCloud中,Seata和Oauth2发生的版本冲突

285 阅读3分钟

前言

在SpringCloud的学习中,发现出现了Seata和OAuth2组件产生冲突的情况。具体的情况在于引入依赖后,服务无法正常启动。

项目结构

image.png

如图,在book-service中引入了OAuth2依赖

image.png

操作

引入之后,启动book-service服务,发现无法启动SpringBoot

2023-09-18 21:13:11.785 ERROR 292 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration$OAuth2ClientContextConfiguration': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'accessTokenRequest': Post-processing of FactoryBean's singleton object failed; nested exception is java.lang.RuntimeException: org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.accessTokenRequest': 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.
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:332) ~[spring-context-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:953) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.15.jar:5.3.15]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.15.jar:5.3.15]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.6.3.jar:2.6.3]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) [spring-boot-2.6.3.jar:2.6.3]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:414) [spring-boot-2.6.3.jar:2.6.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:302) [spring-boot-2.6.3.jar:2.6.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) [spring-boot-2.6.3.jar:2.6.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) [spring-boot-2.6.3.jar:2.6.3]
	at com.test.BookApplication.main(BookApplication.java:12) [classes/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'accessTokenRequest': Post-processing of FactoryBean's singleton object failed; nested exception is java.lang.RuntimeException: org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.accessTokenRequest': 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.
	at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:119) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1884) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getObjectForBeanInstance(AbstractAutowireCapableBeanFactory.java:1284) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:345) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeanByName(AbstractAutowireCapableBeanFactory.java:479) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:550) ~[spring-context-5.3.15.jar:5.3.15]
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:520) ~[spring-context-5.3.15.jar:5.3.15]
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:673) ~[spring-context-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329) ~[spring-context-5.3.15.jar:5.3.15]
	... 17 common frames omitted
Caused by: java.lang.RuntimeException: org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.accessTokenRequest': 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.
	at io.seata.spring.annotation.GlobalTransactionScanner.wrapIfNecessary(GlobalTransactionScanner.java:287) ~[seata-all-1.4.2.jar:1.4.2]
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:291) ~[spring-aop-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:455) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1947) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:116) ~[spring-beans-5.3.15.jar:5.3.15]
	... 28 common frames omitted
Caused by: org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.accessTokenRequest': 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.
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:383) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35) ~[spring-aop-5.3.15.jar:5.3.15]
	at io.seata.spring.util.SpringProxyUtils.findTargetClass(SpringProxyUtils.java:51) ~[seata-all-1.4.2.jar:1.4.2]
	at io.seata.spring.annotation.GlobalTransactionScanner.wrapIfNecessary(GlobalTransactionScanner.java:256) ~[seata-all-1.4.2.jar:1.4.2]
	... 32 common frames omitted
Caused by: 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.
	at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-5.3.15.jar:5.3.15]
	at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:42) ~[spring-web-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:371) ~[spring-beans-5.3.15.jar:5.3.15]
	... 36 common frames omitted

解答

参考:blog.csdn.net/liupantao/a…

查看Maven结构,发现里面Seata依赖的版本是

image.png

查找资料,发现是1.4.2的Seata版本有问题,因为1.4.2 动态扫描有问题-把oath 下的类给扫描到了。但是无法创建对应的代理导致的。

seata1.4.2中io.seata.spring.utilsProxyUtils#findTargetSpringClass中获取代理对象的方法targetSource.getTargetClass()引起的。在1.4.1 之前,对于TargetSource这个对象, 认为就是静态对象,所以直接取的targetSource.getTargetClass()

在1.4.2版本,又完全认为是一个动态对象,而这里的问题就是获取一个被代理的动态对象时,实际上是不能获取这个对象的, 只能获取这个对象的类型.

// 省略无关代码段
@Configuration
public class OAuth2ClientConfiguration {
       // spring 要创建这个对象,  创建后被seata的SpringProxyUtils感知
       //  实际上这是一个动态对象,是不能在启动阶段拿这个值的
       // 这里定义的方式是:  ScopedProxyFactoryBean->SimpleBeanTargetSource->当前对象
	@Bean
	@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
	protected AccessTokenRequest accessTokenRequest(@Value("#{request.parameterMap}")
	Map<String, String[]> parameters, @Value("#{request.getAttribute('currentUri')}")
	String currentUri) {
	}
	@Configuration
	protected static class OAuth2ClientContextConfiguration {
		// 1. spring 要注入这个对象
		@Resource
		@Qualifier("accessTokenRequest")
		private AccessTokenRequest accessTokenRequest;
	}
}


//1.4.2 代码
public static Class<?> findTargetClass(Object proxy) throws Exception {
        if (proxy == null) {
            return null;
        }
        if (AopUtils.isAopProxy(proxy) && proxy instanceof Advised) {
            Object targetObject = ((Advised) proxy).getTargetSource().getTarget();
            return findTargetClass(targetObject);
        }
        return proxy.getClass();
    }

改正如下,或者切换版本1.4.1,或者等待seata官网出1.4.3版本 (反射不熟悉,ORZ)

  public static Class<?> findTargetClass(Object proxy) throws Exception {
        if (proxy == null) {
            return null;
        }
// 把这里加一个条件判断,静态就可以获取对象并且递归获取具体的类型。  
// 动态的话,是万万不能直接获取对象的
        if (AopUtils.isAopProxy(proxy) && proxy instanceof Advised) {
            final TargetSource targetSource = ((Advised) proxy).getTargetSource();
            if (!targetSource.isStatic()) {
                return targetSource.getTargetClass();
            }
// 支持静态域的层次情况
            return findTargetClass(targetSource.getTarget());
        }
        return proxy.getClass();
    }

修改版本

参考:github.com/alibaba/spr…

把依赖修改一下就好

image.png

image.png

成功打开