使用自动装配注释的空指针异常 - Gemfire Listerner

Null pointer exception using Autowired annotation - Gemfire Listerner

我已经将所有的 Cassandra 移动到单个 class。当我尝试在 gemfire 缓存侦听器中创建 CassandraOperations 实例时得到空指针 exception.Can 请协助我解决此错误

我没有收到任何使用 spring 和 cassandra 的空指针异常,但在与 gemfire 集成时收到。

@Component  
public class CacheListener<K, V> extends CacheListenerAdapter<K, V> implements Declarable {

@Autowired
private CassandraOperations cassandraOperations;

@Override
public void init(Properties props) {

}

public void afterCreate(EntryEvent e) {
    cassandraOperations.insert(e.getNewValue());

}

@Override
public void close() {
}

}



public class CassandraConfig {
@Autowired
private Environment environment;
private static final Logger LOGGER = LoggerFactory.getLogger(CassandraConfig.class);
@Bean
public CassandraClusterFactoryBean cluster() {
    CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
    cluster.setContactPoints(environment.getProperty("cassandra.contactpoints"));
    cluster.setPort(Integer.parseInt(environment.getProperty("cassandra.port")));
    return cluster;
}
@Bean
public CassandraMappingContext mappingContext() {
    BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext(); 
    mappingContext.setUserTypeResolver(new SimpleUserTypeResolver(cluster().getObject(), environment.getProperty("cassandra.keyspace"))); return mappingContext;
}
@Bean
public CassandraConverter converter() {
    return new MappingCassandraConverter(mappingContext());
}
@Bean
public CassandraSessionFactoryBean session() throws Exception {
    CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
    session.setCluster(cluster().getObject());
    session.setKeyspaceName(environment.getProperty("cassandra.keyspace"));
    session.setConverter(converter());
    session.setSchemaAction(SchemaAction.NONE);
    return session;
}
@Bean
public CassandraOperations cassandraTemplate() throws Exception {
    return new CassandraTemplate(session().getObject());
}
}

异常

[error 2017/05/05 11:16:04.874 CDT <http-nio-7878-exec-1> tid=0x5b] Exception occurred in CacheListener
java.lang.NullPointerException
    at CacheListener.afterCreate(CacheListener.java:27)
    at com.gemstone.gemfire.internal.cache.EnumListenerEvent$AFTER_CREATE.dispatchEvent(EnumListenerEvent.java:97)
    at com.gemstone.gemfire.internal.cache.LocalRegion.dispatchEvent(LocalRegion.java:8897)
    at com.gemstone.gemfire.internal.cache.LocalRegion.dispatchListenerEvent(LocalRegion.java:7376)
    at com.gemstone.gemfire.internal.cache.LocalRegion.invokePutCallbacks(LocalRegion.java:6158)
    at com.gemstone.gemfire.internal.cache.EntryEventImpl.invokeCallbacks(EntryEventImpl.java:1919)
    at com.gemstone.gemfire.internal.cache.ProxyRegionMap$ProxyRegionEntry.dispatchListenerEvents(ProxyRegionMap.java:548)
    at com.gemstone.gemfire.internal.cache.LocalRegion.basicPutPart2(LocalRegion.java:6012)
    at com.gemstone.gemfire.internal.cache.ProxyRegionMap.basicPut(ProxyRegionMap.java:232)
    at com.gemstone.gemfire.internal.cache.LocalRegion.virtualPut(LocalRegion.java:5824)
    at com.gemstone.gemfire.internal.cache.LocalRegionDataView.putEntry(LocalRegionDataView.java:118)
    at com.gemstone.gemfire.internal.cache.LocalRegion.basicPut(LocalRegion.java:5214)
    at com.gemstone.gemfire.internal.cache.LocalRegion.validatedPut(LocalRegion.java:1597)
    at com.gemstone.gemfire.internal.cache.LocalRegion.put(LocalRegion.java:1580)
    at com.gemstone.gemfire.internal.cache.AbstractRegion.put(AbstractRegion.java:327)
    at org.springframework.data.gemfire.GemfireTemplate.put(GemfireTemplate.java:189)
    at org.springframework.data.gemfire.repository.support.SimpleGemfireRepository.save(SimpleGemfireRepository.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)

在上面的 code/configuration 中不明显的是您如何使用 Spring 配置特定于应用程序的 GemFire CacheListener (Data GemFire).

我看到您使用 Spring 的 @Component 立体类型注释对您的应用程序 CacheListener 进行了注释,但这没有任何帮助.

您是否在使用 Spring 的 Classpath component scanning functionality, or perhaps Spring's Annotation-based container configuration 支持?如果你使用后者,你知道你仍然必须在配置(JavaConfig 或 XML)中显式定义你的应用程序 CacheListener,对吗?

每当你在 @Autowired component/collaborator 字段上遇到 NullPointerException 来注入依赖时,尤其是在使用 Spring 的 @Autowired 注释的 @Autowired annotation, it is good indication you have a configuration problem, particularly since the @Autowired annotation implies that the "dependency" (e.g. CassandraOperations) is "required" (unless you explicitly set the required 属性为 false,而你没有; required 默认为 true).

因此,如果 CacheListener 组件在扫描中被拾取并且无法注入(自动连接)依赖项,因为没有指定类型的(其他)bean(例如 CassandraOperations ) 是在 Spring 应用程序上下文(它是)中定义的,然后 Spring 将在评估时抛出异常你的配置 class(es).

尽管如此,即使您的 CassandraConfig class 也必须使用 Spring 的 @Configuration 注释或使用@Component 使用 Spring 类路径组件扫描或基于注释的容器配置时的注释。或者,如果两者都不使用,则必须在 Spring 应用程序上下文中明确定义为 bean。

注意:命名约定(即 CacheListener)不是很好,因为它与 GemFire 自己的 CacheListener 界面冲突。最好将您的应用程序特定 extension/implementation 称为“GemFireToCassandraCacheListener

例如...

import ...;

@Configuration
class GemFireConfiguration {

  @Bean
  CacheFactoryBean gemfireCache() {
    return new CacheFactoryBean();
  }

  @Bean("CassandraCache")
  PartitionedRegionFactoryBean cassandraCacheRegion() {
    PartitionedRegionFactoryBean cassandraCacheRegion = 
      new PartitionedRegionFactoryBean();

    cassandraCacheRegion.setCache(gemfireCache());
    cassandraCacheRegion.setClose(false);
    cassandraCacheRegion.setCacheListeners(
      new CacheListener[] { gemfireToCassandraCacheListener() });

    return cassandraCacheRegion;
  }

  @Bean
  GemFireToCassandraCacheListener gemfireToCassandraCacheListener() {
    return new GemFireToCassandraCacheListener();
  }
}

import ...;

@Configuration
class CassandraConfig {

  // what you have above
}

我有很多 GemFire 配置示例 here,显示 GemFire 本机配置 Spring (Data GemFire) 配置,XML vs. JavaConfig vs. 注释等

终于...

从技术上讲,最好使用附加到区域的 GemFire CacheWriter,而不是 CacheListener,因为您正在做的(在创建缓存时更新 Cassandra)是CacheWriter.

的预期目的

当然,CacheListener被称为"after" create vs. the CacheWriter which is "before" create。但是,我会说在更新 "cache" 来反映数据源。这尤其适用于主数据源中存在可能导致更新失败的约束的情况。如果无法更新主数据源,您不希望更新缓存。

A CacheWriter 的配置类似于 CacheListener,就像这样...

  @Bean("CassandraCache")
  PartitionedRegionFactoryBean cassandraCacheRegion() {
    PartitionedRegionFactoryBean cassandraCacheRegion = 
      new PartitionedRegionFactoryBean();

    cassandraCacheRegion.setCache(gemfireCache());
    cassandraCacheRegion.setClose(false);
    cassandraCacheRegion.setCacheWriter(gemfireToCassandraCacheWriter());

    return cassandraCacheRegion;
  }

  @Bean
  GemFireToCassandraCacheWriter gemfireToCassandraCacheWriter(
      CassandraOperations cassandraOperations) {

    return new GemFireToCassandraCacheWriter(cassandraOperations);
  }

其中 GemFireToCassandraCacheWriter 将被定义为...

class GemFireToCassandraCacheWriter extends CacheWriterAdapter {

  private CassandraOperations cassandraOperations;

  // Using constructor injection is better than field injection
  GemFireToCassandraCacheWriter(CassandraOperations cassandraOperations) {
    this.cassandraOperations = cassandraOperations;
  }

  public void beforeCreate(EntryEvent<?, ?> event) {
    cassandraOperations.insert(event.getNewValue());
  }
}

注意:一个区域只能有 1 个 CacheWriter。仅供参考,在功能上 CacheWriter 对应于 CacheLoader. See the GemFire User Guide for more details. In particular, see here, here and here.

此外,如果您只是将 GemFire 用作主要在 Cassandra 中管理的状态的缓存,那么您也可以将 Spring's Cache Abstraction, for which Spring Data GemFire positions GemFire 视为抽象中的 "provider"。

不确定你的 GemFire to Cassandra UC 是关于什么的,但值得深思。

希望对您有所帮助!

-约翰