Аннотации @Value внутри моего класса Java не загружают значения из файла .properties

Прежде чем задать этот вопрос, я попытался ответить на следующие вопросы, которые похожи:

Внедрение свойств с использованием Spring и аннотации @Value

Как можно Я добавляю значение свойства в Spring Bean, настроенный с помощью аннотаций?

Загрузка файла свойств в класс в Spring

Однако в моем случае я не использую никаких веб-приложений или Tomcat; Я просто пытаюсь загрузить файл cluster.properties в обычный проект Java через Spring, чтобы затем я мог вводить фиктивные данные в Accumulo. Кроме того, я пытаюсь загрузить свойства из файла cluster.properties, а не из пар ключ-значение, определенных в файле xml.

Используя то, что я узнал из приведенных выше ссылок и много читал о Spring, вот что у меня есть:

Я создал следующий файл context.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

      <!-- Define the Spring Bean to load our cluster properties -->
      <bean id="props" class="accumuloIngest.LoadProperties"></bean>

    </beans>  

А вот небольшой фрагмент того, как выглядит мой файл cluster.properties:

    cluster.instance=instance
    cluster.username=user

    etc...

Затем я создал следующий основной метод Spring в классе MainApp.java:

    package accumuloIngest;

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;

    public class MainApp {

        // Spring main method used to load a cluster.properties file with the Spring framework
        public static void main(String[] args) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
            LoadProperties myObj = LoadProperties.class.cast(ctx.getBean("props"));

            // Now print out the cluster.properties loaded by Spring to verify they aren't null
            StringBuffer springPropsBuffer = new StringBuffer();
            springPropsBuffer.append("Printing out cluster.properties read via Spring...");
            springPropsBuffer.append("\n\n");
            springPropsBuffer.append("instanceName= ");
            springPropsBuffer.append(myObj.getInstanceName());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("userName= ");
            springPropsBuffer.append(myObj.getUserName());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("password= ");
            springPropsBuffer.append(myObj.getPassword());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("zooServers= ");
            springPropsBuffer.append(myObj.getZooServers());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("tableName= ");
            springPropsBuffer.append(myObj.getTableName());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("dataFile= ");
            springPropsBuffer.append(myObj.getDataFile());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("dataDelim= ");
            springPropsBuffer.append(myObj.getDataDelim());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("rowCount= ");
            springPropsBuffer.append(myObj.getRowCount());  
            springPropsBuffer.append("\n");
            System.out.println(springPropsBuffer.toString());

            // now start data ingest
            myObj.startIngest(); // method that calls Ingester class to start data ingest
        } // end of main method

    } // end of MainApp class

Spring загружает мой файл context.xml и загружает Bean, который я назвал «реквизитом», но значения по-прежнему равны нулю. Кажется, что мои аннотации @Value не работают в моем классе LoadProperties:

    package accumuloIngest;
    import java.io.IOException;

    import org.apache.accumulo.core.client.AccumuloException;
    import org.apache.accumulo.core.client.AccumuloSecurityException;
    import org.apache.accumulo.core.client.TableExistsException;
    import org.apache.accumulo.core.client.TableNotFoundException;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;

    public class LoadProperties {

        // this class defines the Spring Bean and loads the cluster properties
        // using the SpringFramework

        @Bean
        public static PropertyPlaceholderConfigurer props(){
        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        Resource[] resource = new ClassPathResource[ ]
                { new ClassPathResource("/EclipseProjectName/src/cluster.properties") };
        ppc.setLocations(resource);
        ppc.setIgnoreUnresolvablePlaceholders(true);
        return ppc;
}

// Now load the properties from cluster.properties using the Spring Framework
private @Value("${cluster.instance}") String instanceName;
private @Value("${cluster.username}") String userName;
private @Value("${cluster.password}") String password;
private @Value("${cluster.zooServers}") String zooServers;
private @Value("${cluster.TableName}") String tableName;
private @Value("${cluster.DataFile}") String dataFile;
private @Value("${cluster.DataDelimiter}") String dataDelim;
private @Value("${cluster.rowCount}") int rowCount;

// Getters for the other Java classes to access properties loaded by Spring
public String getInstanceName() {
    return instanceName;
}
public String getUserName() {
    return userName;
}
public String getPassword() {
    return password;
}
public String getZooServers() {
    return zooServers;
}
public String getTableName() {
    return tableName;
}
public String getDataFile() {
    return dataFile;
}
public String getDataDelim() {
    return dataDelim;
}
public int getRowCount() {
    return rowCount;
}

        // method to kick off the ingest of dummy data
        void startIngest() {
            Ingester ingestObject = new Ingester();
            try {
                ingestObject.ingestData();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (TableNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (TableExistsException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (AccumuloException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (AccumuloSecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } // end of try-catch block
        } // end of startIngest method

    } // end of LoadProperties class

Тем не менее, когда я запускаю MainApp.java в Eclipse, значения равны нулю, когда мой класс Ingester.java вызывает геттеры.

Вот вывод консоли, когда я запускаю MainApp.java в Eclipse:

    13/09/24 14:08:24 INFO support.ClassPathXmlApplicationContext: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@191f667c: startup date [Tue Sep 24 14:08:24 EDT 2013]; root of context hierarchy
    13/09/24 14:08:24 INFO xml.XmlBeanDefinitionReader: Loading XML bean definitions from class path resource [context.xml]
    13/09/24 14:08:24 INFO support.DefaultListableBeanFactory: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@3cdd17f5: defining beans [props]; root of factory hierarchy
    Printing out cluster.properties read via Spring...

    instanceName= null
    userName= null
    password= null
    zooServers= null
    tableName= null
    dataFile= null
    dataDelim= null
    rowCount= 0

    Exception in thread "main" java.lang.IllegalArgumentException: argument was null:Is null- arg1? true arg2? true
        at org.apache.accumulo.core.util.ArgumentChecker.notNull(ArgumentChecker.java:36)
        at org.apache.accumulo.core.client.ZooKeeperInstance.<init>(ZooKeeperInstance.java:99)
        at org.apache.accumulo.core.client.ZooKeeperInstance.<init>(ZooKeeperInstance.java:85)
        at accumuloIngest.Ingester.ingestData(Ingester.java:65)
        at accumuloIngest.LoadProperties.startIngest(LoadProperties.java:69)
        at accumuloIngest.MainApp.main(MainApp.java:44)

Мне не хватает части среды Spring, которая загружает свойства в файле cluster.properties? Я пытался добавить @AutoWired в классы java MainApp и LoadProperties, но это, похоже, не помогло.


person erj2code    schedule 24.09.2013    source источник
comment
Здесь есть ряд проблем. Во-первых, почему ваше контекстное пространство имен объявляет Spring beans версии 2.0, когда вы используете @Bean, который был представлен в 3.0?   -  person Sotirios Delimanolis    schedule 24.09.2013
comment
Да, это была ошибка копирования/вставки с моей стороны. Я определил Spring Framework 3.2.4 в своем pom.xml, поэтому я изменил свой context.xml, чтобы использовать Spring beans версии 3.2.   -  person erj2code    schedule 24.09.2013


Ответы (1)


Если вы собираетесь использовать @Bean, вам понадобится @Configuration. Вы не должны объявлять контекст xml для включения контекста аннотации. Вы также не должны использовать свой экземпляр класса @Configuration в качестве компонента. ClassPathXmlApplicationContext не подходит для обработки конфигураций на основе аннотаций.

Используйте что-то вроде следующего

@Configuration
@ComponentScan(basePackageClasses = LoadProperties.class)
public static class Config {
    @Bean
    public static PropertyPlaceholderConfigurer props() {
        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        Resource[] resource = new ClassPathResource[] { new ClassPathResource(
                "/EclipseProjectName/src/cluster.properties") };
        ppc.setLocations(resource);
        ppc.setIgnoreUnresolvablePlaceholders(true);
        return ppc;
    }  

    @Bean
    public LoadProperties loadProperties() {
        return new LoadProperties();
    }
}

public static class LoadProperties {
    private @Value("${cluster.zooServers}") String zooServers;
    ... // getters and setters
}

public static void main(String[] args) throws Exception {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
    LoadProperties load = (LoadProperties) context.getBean(LoadProperties.class);
    System.out.println(load.getZooServers());
}

Несколько замечаний:

  1. В вашем ClassPathResource вам нужно указать ресурс classpath. У вас действительно есть ресурс /EclipseProjectName/src/cluster.properties в корне пути к классам? Я очень сомневаюсь.
  2. В этом случае @ComponentScan вам не понадобится, но ознакомьтесь с ним.
  3. PropertyPlaceholderConfigurer необходимо объявить статическим, чтобы его можно было инициализировать перед другими объявлениями @Bean. Вы должны использовать PropertySourcesPlaceholderConfigurer как объяснено в javadoc.
person Sotirios Delimanolis    schedule 24.09.2013
comment
Да, вы правы, у меня нет ресурса в корне пути к классам. Я читал Spring API, в котором говорится о ClassPathResource, т.е. docs. spring.io/spring/docs/3.2.4.RELEASE/javadoc-api и понял, что я должен был указать класс java выше. Но как определить, какой ClassPathResource указать? - person erj2code; 24.09.2013
comment
@user2160928 user2160928 Здесь я предполагаю, что вам нужен файл .properties, поэтому укажите путь к нему. Вам нужно выяснить, как компилируется и строится ваша программа, чтобы понять, что это за путь или каким он должен быть. - person Sotirios Delimanolis; 24.09.2013
comment
Хорошо, значит, то, что у меня есть, верно. Мой файл cluster.properties находится в каталоге src моего проекта Eclipse (который я назвал EclipseProjectName выше). - person erj2code; 24.09.2013
comment
@user2160928 user2160928 Это может быть в вашей файловой системе, но eclipse создает ваше приложение в папке bin (афаик). Кроме того, как вы думаете, где это было бы, если бы вы запускали его из файла war? - person Sotirios Delimanolis; 24.09.2013
comment
Я зашел в меню «Свойства» для своего проекта Eclipse, а в разделе «Путь сборки Java» он включает исходную папку /src в моем пути сборки. Кроме того, я попробовал внести изменения в код, которые вы предложили выше, но, похоже, мой файл cluster.properties не найден. - person erj2code; 25.09.2013
comment
@ user2160928 Да, поэтому он возьмет все в /src, скомпилирует его, если это файл .java, и скопирует его, если это что-то еще. В свойствах вы можете проверить Deployment Assembly, чтобы увидеть, где развернуто приложение. - person Sotirios Delimanolis; 25.09.2013
comment
Я должен добавить, что я думаю, что у вас есть опечатка при определении ваших классов выше, поскольку я получил ошибку при попытке определить класс Config как общедоступный статический класс Config - person erj2code; 25.09.2013
comment
@user2160928 user2160928 Это был просто SSCCE, вы можете объявлять классы в их собственных файлах, т.е. нестатический. - person Sotirios Delimanolis; 25.09.2013
comment
Да, именно это я и сделал. - person erj2code; 25.09.2013
comment
@user2160928 user2160928 Ничего не могу сделать, пока не скажешь мне об ошибке :) - person Sotirios Delimanolis; 25.09.2013
comment
Я объявил их как собственные файлы, а не статические. Я также попытался переместить файл cluster.properties в каталог /bin и изменить путь к моему файлу .properties, но у меня были те же ошибки. Я также изменил свой context.xml, чтобы он указывал на bean-компонент props в классе Config. Вот ошибка, которую я вижу: Исключение в потоке main org.springframework.beans.factory.BeanInitializationException: не удалось загрузить свойства; вложенным исключением является java.io.FileNotFoundException: ресурс пути к классу [EclipseProjectName/src/cluster.properties] не может быть открыт, поскольку он не существует - person erj2code; 25.09.2013
comment
@user2160928 user2160928 Это потому, что корень вашего пути к классам, вероятно, bin. Измените путь к ресурсу на cluster.properties. - person Sotirios Delimanolis; 25.09.2013
comment
Кстати, эта ошибка возникла после того, как я переместил файл cluster.properties обратно в каталог /src. - person erj2code; 25.09.2013
comment
Эй, я изменил свой путь к ресурсам на cluster.properties, и это сработало! Он распечатал мое значение для Zookeepers из моего файла cluster.properties. - person erj2code; 25.09.2013
comment
@user Начните с этой вики-статьи, а затем поищите еще немного о том, как работает classpath . - person Sotirios Delimanolis; 25.09.2013