Utwórz własną adnotację, która służy do dekorowania zmiennych instancji lub metod ustawiania, a następnie postprocesora, który przetwarza adnotację i wstrzykuje ogólny serwer proxy, który rozwiązuje poprawną implementację w czasie wykonywania i deleguje do niej wywołanie.
@Component
public class TransactionService {
@LocalizedResource
private TransactionRules rules;
//..
}
@Retention(RUNTIME)
@Target({FIELD, METHOD})
public @interface LocalizedResource {}
Oto algorytm metody w swojej fasoli post-procesor postProcessBeforeInitialization(bean, beanName)
:
- introspekcji klasa fasola, aby wybrać zmienne instancji lub metod dostępowych, które są oznaczone adnotacją z @LocalizedResource. Zapisz wynik w pamięci podręcznej (tylko na mapie) indeksowanej według nazwy klasy. Możesz użyć do tego celu Spring
InjectionMetadata
.Możesz szukać przykładów, jak to działa, wyszukując odnośniki do tej klasy w kodzie źródłowym.
- Jeśli takie pole lub metoda istnieje dla komponentu bean, utwórz proxy za pomocą metody InvocationHandler opisanej poniżej, przekazując ją bieżącemu komponentowi BeanFactory (postprocesor komponentu bean musi być aplikacją ApplicationContextAware). Wstaw ten serwer proxy do zmiennej instancji lub wywołaj metodę ustawiającą za pomocą instancji proxy.
Oto InvocationHandler dla serwera proxy, który będzie używany do tworzenia zlokalizowanych zasobów.
public class LocalizedResourceResolver implements InvocationHandler {
private final BeanFactory bf;
public LocalizedResourceResolver(BeanFactory bf) {
this.bf = bf;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String locale = lookupCurrentLocale();
Object target = lookupTarget(locale);
return method.invoke(target, args);
}
private String lookupCurrentLocale() {
// here comes your stuff to look up the current locale
// probably set in a thread-local variable
}
private Object lookupTarget(String locale) {
// use the locale to match a qualifier attached to a bean that you lookup using the BeanFactory.
// That bean is the target
}
}
Być może trzeba będzie wprowadzić więcej kontroli nad typem komponentu bean lub dodać żądany typ komponentu bean w polu InvocationHandler.
Następną sprawą jest automatyczne wykrycie implementacji danego interfejsu, zależnego od lokalnych zasobów, i zarejestrowanie ich za pomocą kwalifikatora odpowiadającego ustawieniom regionalnym. W tym celu można zaimplementować BeanDefinitionRegistryPostProcessor
lub BeanFactoryPostProcessor
, aby dodać nowy BeanDefinition
s do rejestru z odpowiednim kwalifikatorem, po jednym dla każdej implementacji interfejsów obsługujących locale. Można odgadnąć ustawienia narodowe implementacji, stosując następujące konwencje nazewnictwa: jeśli interfejs rozpoznający ustawienia narodowe nazywa się TransactionRules, wówczas implementacje mogą mieć nazwę TransactionRules_ISOCODE w tym samym pakiecie.
Jeśli nie możesz pozwolić sobie na taką konwencję nazewnictwa, będziesz potrzebować jakiegoś rodzaju skanowania klasy clas + sposobu na odgadnięcie lokalizacji danej implementacji (może adnotacji na temat klas implementacji). Skanowanie metodą Classpath jest możliwe, ale dość złożone i powolne, więc staraj się tego unikać.
Oto podsumowanie tego, co się dzieje:
- Gdy aplikacja uruchamia się, implementacje TransactionRules zostaną odkryte i będą tworzone definicje fasoli dla każdego z nich, z kwalifikator odpowiadający lokalizacji każdego wdrożenia . Nazwa fasoli dla tych komponentów nie jest istotna, ponieważ wyszukiwanie odbywa się na podstawie typu i kwalifikatora.
- Podczas wykonywania ustaw bieżące ustawienia narodowe w zmiennej lokalnej dla wątku. Wyszukaj żądany komponent (np. TransactionService). Postprocesor wprowadzi proxy dla każdego pola instancji @LocalizedResource lub metody ustawiającej.
- Podczas wywoływania metody na TransactionService, która kończy się niektórymi metodami TransactionRules, program wywołujący powiązany z serwerem proxy przełącza się na poprawną implementację na podstawie wartości przechowywanej w zmiennej lokalnej wątku, a następnie deleguje wywołanie do tej implementacji.
Niezbyt trywialny, ale działa. W ten sposób przetwarzanie @PersistenceContext jest przetwarzane przez Spring, z wyjątkiem wyszukiwania implementacji, który jest dodatkową cechą twojego przypadku użycia.
Czy w 2017 r. Nie ma prostszej metody osiągnięcia tego celu? – maxxyme