Gemfire NoSuchBeanDefinitionException自动装配缓存(Spring 5.0.2 / Gemfirev9.2.7)

时间:2018-01-02 09:16:40

标签: spring spring-boot gemfire spring-data-gemfire

我们正在从Gemfire 8.2.7迁移到9.2.1

作为Gemfire创业公司的一部分,我们利用SpringContextBootstrappingInitializer初始化@Autowire缓存的spring-beans。

迁移到Gemfire 9.2.1(与其他堆栈一起)时的相同代码在服务器启动时失败,但错误率低。

Gemfire 8.2.7 --> Gemfire 9.2.1
Spring-data-Gemfire 1.8.4 --> 2.0.2
Spring-Boot 1.4.7 --> 2.0.0.M7
Spring --> 5.0.2
  

引起:   org.springframework.beans.factory.NoSuchBeanDefinitionException:没有   属于' org.apache.geode.cache.Cache'的限定bean可供选择:   预计至少有1个豆有资格成为autowire候选人。   依赖注释:   {@ org.springframework.beans.factory.annotation.Autowired(所需=真)}

GemfireConfig需要什么指针/更改?下面是我们的JavaConfig。

@Bean
public CacheFactoryBean gemfireCache() {

    return new CacheFactoryBean();
}

看起来ComponentScan在配置处理器之前就已经开始了。有关控制此行为的任何想法?这个测试在Spring-Boot 1.4.6(Spring-4.3.8)中进行了测试,并使用@Depends选项解决了 - 但只是想了解更新Spring版本的bean初始化顺序是否有任何根本性的变化

@Configuration
@EnableAutoConfiguration(exclude = { HibernateJpaAutoConfiguration.class, BatchAutoConfiguration.class })
@Import(value = { GemfireServerConfig.class, JpaConfiguration.class, JpaConfigurableProperties.class })
@ComponentScan(basePackages = "com.test.gemfire", excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class) )

1 个答案:

答案 0 :(得分:2)

首先,让我给你一些提示,因为上面的问题陈述有3个问题......

1)首先,您尚未说明使用o.s.d.g.support.SpringContextBootstrappingInitializer文档here的原因或方式。

我只能假设这是因为您使用 Gfsh 启动GemFire服务器 使用以下命令......

gfsh> start server --name=MyServer --cache-xml-file=/path/to/cache.xml ...

cache.xml的定义与this类似。毕竟,这是使用SpringContextBootstrappingInitializer的原始意图。

如果是这种情况,为什么不使用 Gfsh start server command--spring-xml-location选项。例如:

gfsh> start server --name=MyServer --spring-xml-location=/by/default/a/classpath/to/applicationContext.xml --classpath=/path/to/spring-data-gemfire-2.0.2.RELEASE.jar:...

通过这样做,您不再需要提供cache.xml来声明SpringContextBootstrappingInitializer以便在GemFire JVM进程内引导Spring容器。您可以简单地使用--spring-xml-location选项,并在启动服务器时将SDG放在服务器的类路径上。

2)其次,您不明白将哪种类型的应用程序组件/ bean注入GemFire Cache引用(例如,Region或其他应用程序组件类,如DAO等)。提供一段代码,显示您如何使用Cache注释注入@Autowired引用,即注入点,这将有所帮助。例如:

@Service
class MyService {

  @Autowired
  private Cache gemfireCache;

  ...
}

3)如果您包含完整堆栈跟踪而不仅仅是NoSuchBeanDefinitionException消息,那么#2会更加明显。

尽管您的问题陈述存在问题,但我可以推断出以下内容:

  1. 显然,您正在使用" 类路径组件扫描" (使用@ComponentScan注释)并按类型"自动布线" ;实际上可能是关键;我将在下面回到这一点。

  2. 您在bean类字段(字段注入)或属性(setter注入)上使用Spring的@Autowired注释,甚至可能是构造函数。

  3. 此字段/属性(或构造函数参数)的类型绝对是org.apache.geode.cache.Cache

  4. 继续......

    通常, Spring 将首先遵循依赖顺序。也就是说,如果A依赖于B,那么B必须在A之前创建并在A之后销毁。通常,Spring将会并且可以在没有事件的情况下尊重它。

    超越"依赖顺序" bean创建和满足bean之间的依赖关系(包括@DependsOn注释),bean的创建顺序非常松散。

    有几个因素可以影响它,例如"注册顺序" (即声明bean定义的顺序,对于XML中定义的bean尤其如此)," import order" (在@Import类上使用@Configuration注释时),Java反射(包括@Bean类中声明的@Configuration定义)等。配置组织绝对重要,不应该是小心翼翼。

    这是我不是" 类路径组件扫描的主要支持者的原因之一。虽然它可能很方便,但IMO总是更好,更明确"在您的配置和配置的组织中,出于here概述的原因以及其他非明显的限制。在最坏的情况下,你绝对应该限制扫描的范围。

    具有讽刺意味的是,您排除/过滤了实际上可以帮助您解决组织问题的一件事...... @Configuration类型的组件:

    ... excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
    
      

    注意:如果排除,您确定没有排除包含@Configuration定义的1 CacheFactoryBean类吗?我想不是因为你说在包含@DependsOn注释之后这是有效的。

    显然,在你的某些应用程序组件(??)和类型o.a.g.cache.Cache的bean(使用@Autowired)之间定义了依赖关系,但是Spring无法解决它。

    我的想法是,Spring无法解析Cache依赖关系,因为1)尚未创建GemFire缓存bean而且2)Spring无法找到所需类型的适当bean定义(即{{1}在你的配置中,它将解决依赖关系并强制首先创建GemFire o.a.g.cache.Cache,或者3)首先创建了GemFire Cache bean,但Spring无法将类型解析为{{ 1}}。

    之前我遇到过这两种情况,并且在每种情况发生时我并不完全清楚,因为我还没有追踪到这一点。我只是纠正了它并继续前进。我注意到它与版本有关。

    有几种方法可以解决这个问题。

    如果问题出现在后面,3),那么简单地将依赖声明为类型Cache应该可以解决问题。所以,例如:

    o.a.g.cache.Cache

    之所以这样,是因为o.a.g.cache.GemFireCachegetObjectType() method会返回一般扩展为@Repository class MyDataAccessObject { @Autowired private GemFireCache gemfireCache; ... } 的类类型。这是设计,因为o.s.d.g.CacheFactoryBean扩展o.a.g.cache.GemFireCache,但如果我创建了这些类,我可能不会这样做。但是,它与GemFire中的实际缓存类型为o.a.g.internal.cache.GemFireCacheImpl这一事实是一致的,它间接实现o.s.d.g.client.ClientCacheFactoryBean接口和o.s.d.g.CacheFactoryBean接口。

    如果您的问题是前者(1)+ 2),这有点棘手),那么我建议您使用更智能的配置组织,并将其分开。例如,您可以使用以下命令封装GemFire配置:

    o.a.g.cache.Cache

    然后,您的应用程序组件(其中一些依赖于GemFire组件)可以使用以下命令定义:

    o.a.g.cache.client.ClientCache

    通过导入@Configuration class GemFireConfiguration { // define GemFire components (e.g. CacheFactoryBean) here } ,您可以确保首先创建(实例化,配置和初始化)GemFire组件/ bean。

    您甚至可以使用更有针对性的,有限的" 类路径组件扫描"在您拥有大量应用程序组件(服务,DAO等)的情况下,在@Configuration @Import(GemFireConfiguration.class) class ApplicationConfiguration { // define application beans, including beans dependent on GemFire components } 类级别。

    然后,您可以让您的主要Spring Boot应用程序类驱动所有这些:

    GemFireConfiguration

    重点是,您可以根据自己的选择精细化。我喜欢通过关注来封装配置并清楚地组织配置(使用导入)以反映我希望创建(构造,配置和初始化)组件的顺序。

    老实说,我基本上按依赖顺序组织我的配置。如果我的应用程序最终依赖于数据存储而没有该数据存储就无法运行,那么它确保首先进行初始化,否则,启动应用程序的重点是什么。

    最后,您可以始终依赖ApplicationConfiguration注释,正如您已经适当地完成的那样,以确保Spring将在期望它的组件之前创建组件。

    基于@Configuration @Import(ApplicationConfiguration.class) class MySpringBootApplication { public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class, args); } } 注释解决了您的问题这一事实,我会说这是一个组织问题,属于我在上面概述的1)/ 2)类别。

    我将深入研究这一点,并在评论中回答我的回答。

    希望这有帮助!

    -John