Spring - @Autowired jest nullem

0

Witam mam problem z adnotacja Autowired.

Klasa testowa

@Component
public class Test {

	@Autowired
	public static PersonService PersonService;

	public static void main(String[] args) {
		System.out.println(PersonService);
	}
}

Serwis i implementacja

public interface SystemVersionService {
	public SystemVersionData findById(String version);
}



@Service
public class SystemVersionServiceImpl implements SystemVersionService {
	
	@Autowired
	private final SystemVersionRepository systemVersionRepository;

	public SystemVersionServiceImpl(SystemVersionRepository systemVersionRepository) {		
		this.systemVersionRepository = systemVersionRepository;
	}

	@Override
    @Transactional
	public SystemVersionData findById(String version) {
		return systemVersionRepository.findById(version);
	}
}

Repozytorium i implementacja

@Repository
@Transactional
public class SystemVersionRepositoryImpl implements SystemVersionRepository {
	@Override
	public SystemVersionData findById(String version) {		
		return null;
	}
}


public interface SystemVersionRepository extends Repository<SystemVersionData, String> {
	SystemVersionData findById(String version);
}

Gdy probowałem dodac adnotacje @SpringBootApplication do klasy Test i

ApplicationContext ctx = SpringApplication.run(Test.class, args);

leci mi bład:

Error creating bean with name 'dataSourceInitializerPostProcessor': Injection of autowired dependencies failed; nested exception is java.lang.NoSuchMethodError: org.springframework.core.annotation.AnnotatedElementUtils.forAnnotations([Ljava/lang/annotation/Annotation;)Ljava/lang/reflect/AnnotatedElement;

Mógłby mi koś pomóc?

1

Po pierwsze to wywal to final z deklaracji wstrzykiwanego pola. Po drugie wywal to static z deklaracji wstrzykiwanego pola. No i nie pokazałeś czy na pewno skanujesz poprawnie wszystkie pakiety.

0

usunałem final ale static nie moge usunąc bo wtedy nie bede mógł użyc tego Serwisu w main.

@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
@ComponentScan()
public class Configuration{
//
}
0

A czy klasa Test jest w tym samym pakiecie co Configuration? Jeśli nie to musisz też podać pakiet do skanowania w @ComponentScan

0

w klasie Test musze dodac tez @ComponentScan ?

0

Nie, w klasie Configuration powinieneś podać pakiet w @ComponentScan

0

podałem ale dalej to nic nie dało

1

ale static nie moge usunąc bo wtedy nie bede mógł użyc tego Serwisu w main.

I nie powinieneś bo ty chcesz korzystać z beana wstrzykniętego do komponentu przez Springa wiec siłą rzeczy musisz operować na obiektach zarządzanych przez Springa a nie na jakichś statycznych operacjach. Musisz sobie tego beana pobrać ze springowego kontekstu, więc np. to twoje

Test component = ctx.getBean(Test.class);
1

Final musisz usunąć bo wstrzykujesz najgorszą możliwą metodą. Wstrzykiwanie za pomocą @Autowired nad polami powoduje, że klasa ma +10 do trudności testowania. Nie utworzysz już jej nigdy (np. na potrzeby testu bez pomocy Springa)

Dlatego dałbym @Autowired nad konstruktorem, a pole zostawił final, żeby wyrazić jasno intencję, że ta referencja się nigdy nie zmienia.

Co do static to nie ma to żadnego sensu i wyraźnie nie rozumiesz idei Dependency Injection.

Jeżeli używasz SpringBoot to on dodaje automatycznie ComponentScan od pakietu z @SpringBootApplication w głąb. Jeżeli nie używasz SB to musisz dodać @ComponentScan i określić pakiet do skanowania.

3

@nie100sowny jak testujesz to tosterem to jasne że nie potestujesz. Ale już Easymock czy Mockito spokojnie potrafią testować injectowanie, zaręczam.

1

Testy, które potrzebują Mockito (EasyMocka) zwykle testują tylko Mockito (lub odpowiednio EasyMocka).
Po paru latach pracy w różnych Korpo (z JEE i Springiem )- wiem jedno - Mockito(EasyMock) są już naprawde dobrze pretestowane.
Lepiej jednak pisać testy dla własnej aplikacji.

Btw. używanie @Inject , @Autowired prowadzi szybko do klas potworków, które mają takich wstrzyków 5-6 (albo gorzej).
Nie warto.

0

@jarekr000000 Z DI jak ze wszystkim - jak sie nie umie używać to potem są takie efekty. Zły kod można napisać niezależnie od stosowanej technologii, ale zrzucanie winy na technologię jest śmieszne. To nie DI/IoC jest problemem tylko to że ludzie nie potrafią programować i korzystać z dostępnych narzędzi.
Analogicznie z testami - jeśli nie potrafisz napisać porządnego testu jednostkowego który testuje twój własny kod małymi partiami, a zamiast tego testujesz sobie mocki to nie jest to wina frameworków testowych tylko braku umiejetności pisania testów :)

Zresztą pisanie testów to jest dobry test jakości kodu - jeśli pisząc test musisz zrobić milion mocków i je oprogramować w skomplikowany sposób to znaczy ze kod jest do d**y i tyle ;]

0

@Shalom Co do

jak sie nie umie używać to potem są takie efekty
. Pracuje w branży od jeszcze zeszłego wieku.... Nie widziałem jeszcze dobrze zrobionego projektu z @Autowired, @Inject, @ejb (nietrywialnego). To zawsze sie przeobraża w koszmar. (Mockito, 5 @Autowiredów w jednej klasie etc.). Co jakiś czas temu skłonilo mnie do refleksji, że może to nie ludzie są winni (tak samo jak to, że nikt nie ogarnia JPA, JSF i innych JEE syfów).

Ale najważniejsze pytania po co w ogóle komu taki @Inject... - przed Java 8 to jeszcze miało jakiś szczątkowy sens - ale w 2016?

Inaczej - jak masz projekt, który sie składa z różnych wymiennych modułów i na etapie assembly sobie składasz z pluginów, to idealnie - po to właśnie @Autowiredy powstały. Masz 5 implementacji jakiejś klasy i dorzucasz do WARa jedną - sama się odpowiednia implementacja wstrzyknie. Super (jeśli akurat nie jest piątek - u mnie na produkcji akurat @Injecty w piątki nie działają).

Ale w praktyce wszystkie Janusze mają jedną, tą samą implementację i sobie ją wstrzykują....(nie ogarniam).

@Shalom

Zresztą pisanie testów to jest dobry test jakości kodu

W pełni się zgadzam. Tylko jeszcze o krok dalej :-) - jak trzeba do kodu napisać test - to na pewno jest ten kod kiepski.
Przytłaczająca większość kodu jest kiepska - ale zdażało już mi się widzieć kod dobry.

2

@jarekr000000

Pracuje w branży od jeszcze zeszłego wieku

Więc masz dobrą perspektywę! Czy w takim razie uważasz że zanim pojawiły się kontenery IoC kod był lepszej jakości? Bo ja trochę takich kodów widziałem i mam wątpliwości co do tego ;) Analogicznie jeśli chodzi o inne frameworki. Ich brak wcale nie sprawiał że kod był lepszej jakości. Powiedziałbym że wręcz przeciwnie, bo ludzie sami próbowali pisać to co teraz siedzi zamknięte w jarach springa czy jee i z różnym skutkiem ;)

Inaczej - jak masz projekt, który sie składa z różnych wymiennych modułów i na etapie assembly sobie składasz z pluginów, to idealnie - po to właśnie @Autowiredy powstały. Masz 5 implementacji jakiejś klasy i dorzucasz do WARa jedną - sama się odpowiednia implementacja wstrzyknie. Super (jeśli akurat nie jest piątek - u mnie na produkcji akurat @Injecty w piątki nie działają).

Pracuje akurat przy projekcie mniej wiecej w tym stylu i jak dla mnie sprawdza sie to całkiem dobrze. Mamy coś w rodzaju generatorów kodu, z których każdy wykonuje pewne ustalone kroki. W efekcie logika generacji jest tylko w jednym miejscu do którego wstrzykiwane są implementacje dla poszczególnych kroków. Start danego generatora tworzy springowy kontekst i skanuje tylko pakiety tego generatora, w efekcie startując genreator z poprawnie wstrzykniętą logiką generacji.
Alternatywą byłoby co najwyżej napisanie identycznego mechanizmu tylko ręcznie i trudno mi sobie wyobrazić w jaki sposób byłoby to lepsze ;]

Ale najważniejsze pytania po co w ogóle komu taki @Inject

I co w zamian? Wstrzykiwanie ręcznie czy wszystko statycznie? Bo fakt że w wielu miejscach kodu możesz potrzebować dostępu do jakiegoś "serwisu" jest raczej niezaprzeczalny.

jak trzeba do kodu napisać test - to na pewno jest ten kod kiepski.

No jeśli ktoś "musi" go napisać bo nie jest pewien swojego kodu to jasne. Ale testy pisze się przecież w zupełnie innym celu - żeby wykrywać potencjalne błędy w modyfikacji kodu. Jak za pół roku ktoś postanowi w przypływie geniuszu coś zmienić to taki test może sie jednak przydać. Ja zresztą osobiśćie nie lubię siadać do refaktoryzacji bez testów :)
Testy dobrze też dokumentują "kontrakt" danej metody/klasy i wymuszają zachowanie tego kontraktu.

1

Więc masz dobrą perspektywę! Czy w takim razie uważasz że zanim pojawiły się kontenery IoC kod był lepszej jakości? Bo ja trochę takich kodów widziałem i mam wątpliwości co do tego ;) Analogicznie jeśli chodzi o inne frameworki. Ich brak wcale nie sprawiał że kod był lepszej jakości. Powiedziałbym że wręcz przeciwnie, bo ludzie sami próbowali pisać to co teraz siedzi zamknięte w jarach springa czy jee i z różnym skutkiem ;)

O dokładnie - przez chwilę było prawie nieźle (2005?)- jak się pojawiły wstrzyknięcia, ale programiści korzystali z nich niesmiało i było OK. Potem się to rozrosło do osobnej religii - kod poza annotacjami jest tylko komentarzem. (http://www.annotatiomania.com/)

I co w zamian? Wstrzykiwanie ręcznie czy wszystko statycznie? Bo fakt że w __wielu miejscach kodu możesz potrzebować dostępu do jakiegoś "serwisu"__ jest raczej niezaprzeczalny.
O widzisz! Własnie udało Ci się zidentyfikować błąd w projekcie twojego systemu.

testy pisze się przecież w zupełnie innym celu - żeby wykrywać potencjalne błędy w modyfikacji kodu
Tak, ale użycie kompilatora do tego celu (type system) wydaje się dużo fajniejsze.

I co w zamian? Wstrzykiwanie ręcznie..
Więc, jak nie używasz Springa/ JEE czy innego badziewia - to okazuje się, że takie konstruktory i słówko kluczowe new naprawdę są OK.
Rzadko trzeba coś wiele razy "wstrzykiwać", konstruktory przyjmują po 2 max 3 obiekty. Testy sa proste, i nie potrzeba Mockito.
Da się, chociaż Java rzuca trochę kłód pod nogi.

U mnie w firmie mimo, że mam dużo kodu JEE - regularnie odpędzam Injecty i przerabiam na zwykłe new.. Z testów wywalam Mockito. I wtedy i kod jakoś krótszy się robi, i działa nawet w piatki (bo pewnie juz pisałem, że w piatki nam @Injecty czasem nie startują).

0

@jarekr000000

O widzisz! Własnie udało Ci się zidentyfikować błąd w projekcie twojego systemu.

Już bez przesady, jest wiele sytuacji kiedy nie da się tego uniknąć bez tworzenia wielokrotnie takich samych obiektów bez potrzeby. Załóżmy że mam serwis służący do wczytywania czy zapisywania plików pewnego typu. Pliki tego typu są używane przez wiele różnych modułów. Mógłbym sobie pewnie tworzyć osobną instancje dla każdego serwisu który tego używa jeśli jest zupełnie bezstanowe. Komplikuje sie to gdybyśmy chcieli zbierać tam jakieś informacje, mieć tam cache czy coś podobnego. Można sie oczywiście bawić w ręczne wstrzykiwanie konstruktorem, tylko po co komplikować sobie życie? Naprodukujemy tylko dodatkowy kod, który robi to samo co dostępne narzędzia, a wiadomo że więcej kodu = większa szansa że gdzie jest błąd :)

Tak, ale użycie kompilatora do tego celu (type system) wydaje się dużo fajniejsze.

Ciekawe w jaki sposób. Mam metodę która ma zadanie wczytać podany przeze mnie plik konfiguracyjny i zwrócic jego zawartość, ale ktoś przypadkiem zrobił w pętli break modyfikując jakiś warunek i konfiguracja sie nie wczytała do końca. Type system powie że wszystko jest ok bo przecież typy argumentów i wartości zwracanej się zgadzają. A jednak system nie działa. Ciekawi mnie jak "za pomocą kompilatora" chciałbyś to przetestować.

Więc, jak nie używasz Springa/ JEE czy innego badziewia - to okazuje się, że takie konstruktory i słówko kluczowe new naprawdę są OK.
Rzadko trzeba coś wiele razy "wstrzykiwać", konstruktory przyjmują po 2 max 3 obiekty. Testy sa proste, i nie potrzeba Mockito.

Nie widzę jak chcesz uniknąć stosowania mocków w takiej sytuacji skoro i tak coś "wstrzykujesz", nawet jeśli przez konstruktor. Szczególnie kiedy testujesz jednostkowo coś w interakcji z obiektami których nie chcesz tworzyć w trakcie testów jednostkowych (jak baza danych czy endpoint webservice). Już nie wspomnę o sytuacji kiedy nasz testowany obiekt ma 2 zależności w konstruktorze, a każda z nich ma swoje 2, a każda z tamtych.... A mnie w ogóle nie obchodzi teraz działanie tych zależności bo robie test jednostkowy i zakładam że tamte obiekty działają poprawnie. Dużo wygodniej wrzucić tutaj dwa mocki niż instancjonować pół systemu żeby mieć "prawdziwe obiekty".

Co do reszty to znów wracamy do problemy słabych koderów a nie złej technologii. Jak ktoś robi klasę z milionem injectów to alternatywą będzie klasa z milionem parametrów konstruktora albo milionem setterów. Liczenie na to że nagle ktoś zacznie pisać lepszy kod bo mu zabiorą inject jest raczej naiwne ;)

0
Shalom napisał(a):

Już bez przesady, jest wiele sytuacji kiedy nie da się tego uniknąć bez tworzenia wielokrotnie takich samych obiektów bez potrzeby. Załóżmy że mam serwis służący do wczytywania czy zapisywania plików pewnego typu. Pliki tego typu są używane przez wiele różnych modułów. Mógłbym sobie pewnie tworzyć osobną instancje dla każdego serwisu który tego używa jeśli jest zupełnie bezstanowe. Komplikuje sie to gdybyśmy chcieli zbierać tam jakieś informacje, mieć tam cache czy coś podobnego. Można sie oczywiście bawić w ręczne wstrzykiwanie konstruktorem, tylko po co komplikować sobie życie? Naprodukujemy tylko dodatkowy kod, który robi to samo co dostępne narzędzia, a wiadomo że więcej kodu = większa szansa że gdzie jest błąd :)

  1. Jest 2016 - tworzenie wielokrotnie obiektów to nie jest jakiś dramat(poza wyjątkami). A współdzielenie obiektów z cache (między wątkami?), w sytuacji gdzie nie zarządzasz czasem życia obiektów (tylko robi to potwór Spring) - to droga do spektakularnych "sukcesów" na produkcji ( zdażył mi się jeden taki piękny...).
  2. Nie znam twojego projektu i trudno się abstrakcyjnie wypowiadać... ale znam troche takich masakr - gdzie ConfigurationContext czy coś podobnego jest wszędzie wstrzykiwane (dramat). Ale ni chuchu nie rozumiem po co SERWIS do wczytywania plików pewnego typu - ja tu mam zwykle prostą klasę, którą sobie gdzie trzeba instacjonuję. (Podobnie jak np, wczytywanie ResourceBundle)
  3. Co do wycinania kodu - w sumie dokładnie zawsze w zasadzie o to mi chodzi - jakoś tak się zwykle dzieje, że po wycięciu paru wstrzyknieć kod programu i testów istotnie sie zmniejsza (przeważnie, czasem są wyjątki).
  1. Generalnie - naprawdę uważam, że "wstrzyki" to nowe GOTO i czas je wywalić. Jak Dijkstra pisał GO TO considered harmful - to też pełno ludzi pokazywało jak to bez GO TO pisanie staje się trudne i nieefekywne (i pewnie są takie przypadki - ale ogólnie jakoś się ludzie nauczyli żyć bez GOTO i mało kto tęskni.)

Ciekawe w jaki sposób. Mam metodę która ma zadanie wczytać podany przeze mnie plik konfiguracyjny i zwrócic jego zawartość, ale ktoś przypadkiem zrobił w pętli break modyfikując jakiś warunek i konfiguracja sie nie wczytała do końca. Type system powie że wszystko jest ok bo przecież typy argumentów i wartości zwracanej się zgadzają. A jednak system nie działa. Ciekawi mnie jak "za pomocą kompilatora" chciałbyś to przetestować.

W sumie ciekawy przypadek. O ile samo IO - jak to IO - świnia. Jednakowoż, od momentu parsera i operacji na symbolach chyba mogło by być ciekawe zadanie.

Nie widzę jak chcesz uniknąć stosowania mocków w takiej sytuacji skoro i tak coś "wstrzykujesz", nawet jeśli przez konstruktor. Szczególnie kiedy testujesz jednostkowo coś w interakcji z obiektami których nie chcesz tworzyć w trakcie testów jednostkowych (jak baza danych czy endpoint webservice). Już nie wspomnę o sytuacji kiedy nasz testowany obiekt ma 2 zależności w konstruktorze, a każda z nich ma swoje 2, a każda z tamtych.... A mnie w ogóle nie obchodzi teraz działanie tych zależności bo robie test jednostkowy i zakładam że tamte obiekty działają poprawnie. Dużo wygodniej wrzucić tutaj dwa mocki niż instancjonować pół systemu żeby mieć "prawdziwe obiekty".

  1. Od końca - instancjonowanie pół systemu - to chyba najważniejsza zeleta testów bez mocków - nadal działa szybko -
    a testuje mój system, nie Mockito.
  2. Ręcznie pisane mocki ogólnie są fajne. U mnie po rozpoczęciu wywalania Mockito ze starego projektu - zespół
    zauważył, że pełno mockowań się powtarzało i teraz mamy kilka RĘCZNIE napisanych mocków - które są w wielu miejscach wykorzystane i zastąpiły kilkaset powtarzających się linii Mockito.when....
  3. Co do mockowania baz danych itd. - mockowanie bazy danych w typowym projekcie z JPA jest czysto bez sensu (nie wiadomo co po tym testujesz, ale na pewno nie system).
  4. I dlatego jestem też gorącym zwolennikiem wywalania baz danych... - ale to temat na inną dyskusję.

Ważne pytanie:
Czy pisałeś kiedyś jakią nietrywialną aplikację (prosty serwis REST np.) bez Springa , JEE itp.?

Zrobiłem taki eksperyment (Scala - 2 lata temu) - to mi troszkę otworzyło oczy. Nagle sie okazało, że rzeczy, które przyzwyczaiłem się, że są trudne (np. Testy, konfiguracja- są łatwe. A potem się okazało, że to działa również w Javie.... (see Ratpack).

1 użytkowników online, w tym zalogowanych: 0, gości: 1