Java Springbean中的Firebase初始化未触发onDataChange

Java Springbean中的Firebase初始化未触发onDataChange,java,firebase-realtime-database,firebase-admin,Java,Firebase Realtime Database,Firebase Admin,我正在使用firebase admin sdk for java。当我创建java控制台应用程序时,下面的代码按预期工作。但是,如果我在springboot应用程序的bean初始化中放入相同的代码,那么它永远不会进入onDataChange EventHandler中 我甚至试着在末尾放置一个具有足够延迟的Thread.sleep(),以检查这是否是因为初始化bean的线程正在退出而发生的。然而,这没有帮助 在SpringBean中这样做的正确方法是什么 import com.google.au

我正在使用firebase admin sdk for java。当我创建java控制台应用程序时,下面的代码按预期工作。但是,如果我在springboot应用程序的bean初始化中放入相同的代码,那么它永远不会进入onDataChange EventHandler中

我甚至试着在末尾放置一个具有足够延迟的Thread.sleep(),以检查这是否是因为初始化bean的线程正在退出而发生的。然而,这没有帮助

在SpringBean中这样做的正确方法是什么

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.database.*; 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Repository;

import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;

@Repository
@EnableAsync
public class MyBean {


    FirebaseDatabase db;


    public MyBean() {

        try {

            File file = new File(
                    getClass().getClassLoader().getResource("myKey.json").getFile()
            );

            FileInputStream fis = new FileInputStream(file);

            FirebaseOptions options = new FirebaseOptions.Builder()
                    .setCredentials(GoogleCredentials.fromStream(fis))
                    .setDatabaseUrl("https://myUrl/")
                    .build();

            FirebaseApp.initializeApp(options);

            db = FirebaseDatabase.getInstance();

            DatabaseReference ref = db
                    .getReference("/a/b/c");


            ref.addValueEventListener(new ValueEventListener() {
            //The code execution never comes in here

                public void onDataChange(DataSnapshot dataSnapshot) {
                    System.out.println("dataSnapshot.exists() :"+ dataSnapshot.exists()); 
                }


                public void onCancelled(DatabaseError error) {
                    System.out.print("Error: " + error.getMessage());
                }
            });

           // Thread.sleep(10000);

        } catch (Exception ex) {
            String err=ex.getMessage();
        }


    }

`

查看
FirebaseReference.getReference()方法的源代码:

public DatabaseReference getReference() {
  return new DatabaseReference(ensureRepo(), Path.getEmptyPath());
}
每次调用时,它都会创建一个新的
数据库引用。您现在的做法是将
ValueEventListener
添加到
DatabaseReference
中,因为您没有以某种方式将其从该方法中传递出去,因此它将被垃圾收集

我建议将您的
数据库引用
对象声明为
@配置
类中的bean:

@Configuration
public class FirebaseConfig {

  // This is just the ApplicationContext, injected through abstraction 
  private final ResourceLoader resourceLoader;

  @Autowired // Constructor injection, recommended above field and setter injections
  public FirebaseConfig(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }

  @Bean
  public FirebaseDatabase firebaseDatabase() throws IOException {
    Resource resource = resourceLoader.getResource("classpath:myKey.json");
    InputStream inputStream = resource.getInputStream();
    FirebaseOptions firebaseOptions = new FirebaseOptions.Builder()
            .setCredentials(GoogleCredentials.fromStream(inputStream))
            .setDatabaseUrl("url")
            .build();
    FirebaseApp.initializeApp(firebaseOptions);
    return FirebaseDatabase.getInstance();
  }

  @Bean
  public DatabaseReference databaseReference(FirebaseDatabase firebaseDatabase) {
    DatabaseReference ref = firebaseDatabase.getReference();
    ref.addValueEventListener(new ValueEventListener() {

      public void onDataChange(DataSnapshot dataSnapshot) {
        System.out.println("dataSnapshot.exists() :" + dataSnapshot.exists());
      }


      public void onCancelled(DatabaseError error) {
        System.out.print("Error: " + error.getMessage());
      }
    });

    return ref;
  }
}
现在,在Spring应用程序中,您应该自动连接这个数据库引用bean

更优雅的解决方案是将
ValueEventListener
分离到它自己的bean中,并将其自动连接到
DatabaseReference
bean方法中:

  @Bean
  public DatabaseReference databaseReference(
    FirebaseDatabase firebaseDatabase, 
    // List injection will be populated with all ValueEventListener beans in the ApplicationContext
    List<ValueEventListener> valueEventListeners 
  ) {
    DatabaseReference ref = firebaseDatabase.getReference();
    valueEventListeners.forEach(ref::addValueEventListener);
    return ref;
  }

您可能希望启用调试日志并查看SDK中发生了什么。很可能它遇到了错误,并以静默方式失败。您能否尝试将
@Import(MyBean.class)添加到
@springbootplication
所在的
main应用程序`类中?这只是为了确保MyBean的组件扫描正确。
@Component
public class SystemOutListener implements ValueEventListener {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    System.out.println("dataSnapshot.exists() :"+ snapshot.exists());
  }

  @Override
  public void onCancelled(DatabaseError error) {
    System.out.print("Error: " + error.getMessage());
  }
}