Для начала позвольте мне дать вам несколько советов, так как есть 3 проблемы с вашей формулировкой проблемы выше...
1) Во-первых, вы не дали понять, почему и как вы используете o.s.d.g.support.SpringContextBootstrappingInitializer
Документы здесь.
Я могу только предположить, что это потому, что вы запускаете свои серверы GemFire с помощью Gfsh, используя следующую команду...
gfsh> start server --name=MyServer --cache-xml-file=/path/to/cache.xml ...
Где ваш cache.xml
определяется аналогично это. В конце концов, это и было первоначальным намерением использовать SpringContextBootstrappingInitializer
.
Если это так, почему бы не использовать Gfsh, start server
команда, вместо этого --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
для начальной загрузки контейнера Spring внутри процесса JVM GemFire. Вы можете просто использовать параметр --spring-xml-location
и поместить SDG в путь к классам сервера при запуске сервера.
2) Во-вторых, неясно, в какой тип компонента/бина приложения вы вводите ссылку GemFire Cache
(например, регион или другой класс компонентов приложения, например, DAO и т. д.). Предоставление фрагмента кода, показывающего, как вы внедрили ссылку Cache
, то есть точку внедрения с использованием аннотации @Autowired
, было бы полезно. Например:
@Service
class MyService {
@Autowired
private Cache gemfireCache;
...
}
3) № 2 был бы более очевидным, если бы вы включили полную трассировку стека, а не только сообщение NoSuchBeanDefinitionException
.
Несмотря на проблемы с вашей постановкой задачи, я могу сделать следующий вывод:
Очевидно, вы используете "сканирование компонентов пути к классам" (с аннотацией @ComponentScan
) и выполняете автоматическое связывание "по типу"; что может быть ключевым на самом деле; Я вернусь к этому позже ниже.
Вы используете аннотацию Spring @Autowired
для поля класса компонента (внедрение поля) или свойства (внедрение сеттера), возможно, даже в конструкторе.
Тип этого поля/свойства (или параметра конструктора) определенно org.apache.geode.cache.Cache
.
Идем дальше...
Как правило, Spring в первую очередь следует порядку зависимостей. То есть, если A зависит от B, то B должен быть создан до и уничтожен после A. Как правило, Spring будет и может соблюдать это без инцидентов.
Помимо создания bean-компонентов "порядок зависимости" и удовлетворения зависимостей между bean-компонентами (в том числе с аннотацией @DependsOn
), порядок создания bean-компонентов довольно слабо определен.
Есть несколько факторов, которые могут повлиять на это, такие как «порядок регистрации» (то есть порядок, в котором объявляются определения bean-компонентов, что особенно верно для bean-компонентов, определенных в XML), «порядок импорта» (при использовании аннотации @Import
для @Configuration
классов ), отражение Java (включает @Bean
определений, объявленных в @Configuration
классах) и т. д. Организация конфигурации определенно важна, и к ней нельзя относиться легкомысленно.
Это одна из причин, по которой я не большой сторонник "сканирования компонентов пути к классам. Хотя это может быть удобно, всегда лучше, IMO, быть более "явным" в вашей конфигурации и организации вашей конфигурации по причинам, изложенным здесь в дополнение к другим неочевидным ограничениям. В худшем случае вам определенно следует ограничить область сканирования.
По иронии судьбы, вы исключили/отфильтровали 1 вещь, которая действительно могла бы помочь вашим организационным проблемам... компоненты типа @Configuration
:
... excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
ПРИМЕЧАНИЕ. Учитывая исключение, уверены ли вы, что не исключили класс 1 @Configuration
, содержащий ваше определение CacheFactoryBean
? Я полагаю, что нет, поскольку вы говорите, что это сработало после включения аннотации @DependsOn
.
Очевидно, что существует определенная зависимость между некоторым компонентом вашего приложения (??) и bean-компонентом типа o.a.g.cache.Cache
(использующим @Autowired
), однако Spring не может ее разрешить.
Я думаю, что Spring не может разрешить зависимость Cache
, потому что 1) компонент кэша GemFire еще не создан и 2) Spring не может найти подходящее определение компонента желаемого типа (т.е. o.a.g.cache.Cache
) в вашей конфигурации, которое разрешило бы зависимость и заставить сначала создать GemFire Cache
, или 3) GemFire Cache
был создан первым, но Spring не может разрешить тип как o.a.g.cache.Cache
.
Я сталкивался с обоими сценариями раньше, и мне не совсем ясно, когда происходит каждый сценарий, потому что я просто еще не проследил это. Я просто исправил это и пошел дальше. Я заметил, что это связано с версией.
Есть несколько способов решить эту проблему.
Если проблема в более позднем, 3), то простое объявление вашей зависимости как типа o.a.g.cache.GemFireCache
должно решить проблему. Так, например:
@Repository
class MyDataAccessObject {
@Autowired
private GemFireCache gemfireCache;
...
}
Причина этого в том, что класс o.s.d.g.CacheFactoryBean
getObjectType()
возвращает тип класса, в общем расширяющий o.a.g.cache.GemFireCache
. Это было задумано, поскольку o.s.d.g.client.ClientCacheFactoryBean
расширяет o.s.d.g.CacheFactoryBean
, хотя я, вероятно, не сделал бы этого таким образом, если бы создал эти классы. Однако это согласуется с тем фактом, что фактический тип кэша в GemFire — o.a.g.internal.cache.GemFireCacheImpl
, который косвенно реализует как интерфейс o.a.g.cache.Cache
, так и интерфейс o.a.g.cache.client.ClientCache
.
Если ваша проблема относится к первому (1) + 2, что немного сложнее), то я бы посоветовал вам использовать более разумную организацию вашей конфигурации, разделенную по проблемам. Например, вы можете инкапсулировать свою конфигурацию GemFire с помощью:
@Configuration
class GemFireConfiguration {
// define GemFire components (e.g. CacheFactoryBean) here
}
Затем компоненты вашего приложения, некоторые из которых зависят от компонентов GemFire, могут быть определены с помощью:
@Configuration
@Import(GemFireConfiguration.class)
class ApplicationConfiguration {
// define application beans, including beans dependent on GemFire components
}
Импортируя GemFireConfiguration
, вы обеспечиваете создание компонентов/бинов GemFire (создание экземпляров, настройку и инициализацию) в первую очередь.
Вы даже можете использовать более целенаправленное, ограниченное "сканирование компонентов пути к классам" на уровне класса ApplicationConfiguration
в тех случаях, когда у вас есть большое количество компонентов приложения (службы, DAO и т. д.).
Затем вы можете заставить свой основной класс приложения Spring Boot управлять всем этим:
@Configuration
@Import(ApplicationConfiguration.class)
class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
}
Дело в том, что вы можете быть настолько детализированы, насколько захотите. Мне нравится инкапсулировать конфигурацию по задачам и четко организовывать конфигурацию (используя импорт), чтобы отразить порядок, в котором я хочу, чтобы мои компоненты были созданы (сконструированы, настроены и инициализированы).
Честно говоря, я в основном организую свою конфигурацию в порядке зависимостей. Если мое приложение в конечном итоге зависит от хранилища данных и не может работать без этого хранилища данных, то оно делает так, чтобы убедиться, что оно инициализировано первым, иначе какой смысл запускать приложение.
Наконец, вы всегда можете положиться на аннотацию @DependsOn
, как вы и сделали, чтобы гарантировать, что Spring создаст компонент раньше компонента, который его ожидает.
Основываясь на том факте, что аннотация @DependsOn
решила вашу проблему, я бы сказал, что это организационная проблема, подпадающая под категорию 1)/2), которую я изложил выше.
Я собираюсь углубиться в это немного глубже и отвечу на свой ответ в комментариях тем, что я найду.
Надеюсь это поможет!
-Джон
person
John Blum
schedule
12.01.2018