Вы можете создать свой собственный репозиторий, который расширяет SimpleJpaRepository. Затем реализуем RepositoryFactoryBean и RepositoryFactory, которые будут возвращать репозитории, содержащие вашу реализацию. Для JPA вы можете использовать пример в https://github.com/jkubrynski/spring-data-examples/tree/master/src/main/java/com/kubrynski/data/repository/generic
Помните о том, чтобы включить ваш завод в @EnableJpaRepositories, указав repositoryFactoryBeanClass.
Вот что происходит:
Recreate.Car.TESLA
enum Car
. Как отмечено ниже, класс Recreate
еще не загружен или не инициализирован. TESLA
относится к FEATURES
Recreate
и initialized Recreate
, загружается класс Garage
, инициализируется и создается экземпляр ONE_CAR_GARAGE
. Проблема здесь что в этой точке конструкция enum Car
не является полной, а Car.TESLA
имеет значение null
.
Несмотря на то, что классы могут быть вложенными, это не тот случай, когда вложенные классы загружаются и инициализируются как часть инициализации внешнего класса. Они могут выглядеть вложенными в источник, но каждый класс независим. Статические вложенные классы эквивалентны классам верхнего уровня. Нестатические классы также совпадают, но имеют возможность ссылаться на членов в содержащем классе через скрытую ссылку.
Вы можете сами убедиться, если вы запустили это в отладчике, поставили точки останова в нескольких местах и исследовали стек на каждой точке останова.
Я тестировал / отлаживал это в Eclipse с помощью следующий код, с установленными точками останова, где указано. Он немного отличается от вашего кода, но не должен вести себя по-другому:
public class Foo5
{
static class Recreate {
private static ArrayList FEATURES = new ArrayList();
public enum Car {
TESLA(FEATURES);
Car(ArrayList l) {
System.out.println("car"); // *** Breakpoint ***
}
}
public static Garage ONE_CAR_GARAGE = new Garage(Car.TESLA);
public static class Garage {
final Car car;
Garage(Car car) {
this.car = car; // *** Breakpoint ***
}
}
}
public static void main(String[] args) throws Exception {
Recreate.Car car = Recreate.Car.TESLA;
System.out.println(Recreate.Car.TESLA);
System.out.println(Recreate.ONE_CAR_GARAGE.car.toString());
}
}
Первая точка останова, на которую вы нажмете, будет таковой в конструкторе Garage(Car car)
. Изучив стек в этой точке, вы увидите
Foo5$Recreate$Garage.<init>(Foo5$Recreate$Car) line: 23
Foo5$Recreate.<clinit>() line: 17
Foo5$Recreate$Car.<clinit>() line: 12
Foo5.main(String[]) line: 29
. Поэтому, когда вызывается конструктор Garage
, он еще не вернулся из создания Car
. Это диктуется запутанными зависимостями, которые вы создали между классами, поэтому решение заключается в распутывании зависимостей. Как вы это сделаете, это будет зависеть от ваших конечных целей.
У вас есть скрытая циклическая зависимость, которая запутывает JVM. Давайте посмотрим на ваш код.
class Recreate {
private static ArrayList FEATURES = new ArrayList();
public enum Car {
TESLA(FEATURES);
Car(ArrayList l) { }
}
public static class Garage {
final Car car;
Garage(Car car) {
this.car = car;
}
}
public static Garage ONE_CAR_GARAGE = new Garage(Car.TESLA);
}
Нам также понадобится несколько фрагментов с страницы из JLS .
Тип класса или интерфейса T будет инициализирован непосредственно перед первым вхождением любого из следующих:
blockquote>
- Используется статическое поле, объявленное T, и поле не является постоянной переменной (§ 4.12.4).
& nbsp;
12.4.2. Подробную процедуру инициализации
...
blockquote>
- Затем выполните либо инициализаторы переменных класса, и статические инициализаторы класса, либо инициализаторы полей интерфейса, в текстовом порядке, как если бы они были одним блоком.
Таким образом, наши статические данные инициализируются, когда они впервые упоминаются. Теперь ваш
Car.TESLA
неявноstatic final
, но он не является константой, согласно определению .Постоянная переменная является конечной переменной примитивного типа или введите String, которая инициализируется константным выражением
blockquote>. Итак, для наших целей здесь есть три статических непостоянных переменных:
FEATURES
,TESLA
иONE_CAR_GARAGE
.Теперь в вашем рабочем случае вы ссылаетесь на
Recreate.ONE_CAR_GARAGE
. Это ссылка на статическое поле вRecreate
, поэтомуFEATURES
, а затемONE_CAR_GARAGE
инициализируются. Затем во время инициализацияONE_CAR_GARAGE
,TESLA
инициализируется, так как ссылается на ее класс перечисления. Все хорошо.Однако, если мы слишком рано ссылаемся на перечисление, мы делаем вещи в неправильном порядке.
Recreate.Car.TESLA
получает ссылку, поэтомуTESLA
получает инициализацию.TESLA
ссылается наFEATURES
, поэтомуRecreate
должен быть инициализирован. Это заставляетFEATURES
иONE_CAR_GARAGE
инициализироваться до того, какTESLA
закончит существующие.Это скрытая зависимость, которая вас трогает.
Recreate.Car
зависит отRecreate
, который зависит отRecreate.Car
. Перемещение поляONE_CAR_GARAGE
в классGarage
приведет к тому, что он не будет инициализирован с помощьюFEATURES
и устранит вашу проблему.