Fabryki beanów
W tej części laboratorium zdefiniujemy główną część programu. Dodamy do naszego projektu model i zobaczymy jak można go zainicjować, używając do tego tzw. fabryki beanów.
Model aplikacji
Na razie zdefiniowaliśmy podstawowe serwisy w naszej aplikacji, ale nie posiadają one jeszcze logiki. W tej części zadania dodamy model oraz podstawową logikę, która pozwoli na wykorzystanie wszystkich komponentów.
-
Pobierz załączone klasy modelowe:
Student,GradeorazCoursei umieść je w odpowiednich pakietach. -
Zmodyfikuj odpowiednie serwisy zgodnie z opisem:
-
NotificationServicepowinien posiadać metodęvoid notify(Student, Grade), aConsoleNotificationServicepowinien ją implementować w taki sposób, by wypisywać informację o tym, że student o danym imieniu i nazwisku otrzymał ocenę o konkretnej wartości z przedmiotu o danej nazwie. -
GradeBookpowinno posiadać mapęnumer indeksu : lista ocen:private final Map<String, List<Grade>> studentGrades = new HashMap<>();oraz metodę
public Grade assignGrade(Student student, Course course, double gradeValue) {}która utworzy obiekt
Grade, doda go do mapy i zwróci.💡Podpowiedź: skorzystaj z metody
computeIfAbsent(). -
GradeServicepowinien posiadać metodęvoid assignGrade(Student student, Course course, double gradeValue), która użyjeGradeBookdo zapisania oceny, a następnie wyśle notyfikację przezNotificationService. -
StudentServicerównież powinien posiadać metodęvoid assignGrade(Student student, Course course, double gradeValue), w której jedynie oddeleguje polecenie doGradeService.
-
-
Dodaj klasę
SchoolInitializer, oznacz ją w odpowiedni sposób, by Spring stworzył jej instancję. Następnie stwórz testowy obiekt studenta i kursu i przetestuj działanie napisanego wcześniej modelu, przypisując ocenę do studenta. Pamiętaj, by wstrzyknąć odpowiedni serwis i upewnij się, że Twój kod wywoła się po zainicjowaniu klasy.
Tworzenie fabryki beanów
- W pakiecie
pl.edu.agh.to.school.coursestwórz dodatkową klasęCourseFactoryi oznacz ją adnotacją@Configuration.Adnotacja
@Configurationdziała podobnie jak@Service. Spring w tym przypadku również powoła do życia odpowiedniego beana. Inna nazwa beana ma głównie funkcję informacyjną i sugeruje, że w tym przypadku bean będzie służył jako fabryka innych beanów. Podobnie wygląda to w przypadku innych adnotacji oznaczających beany, np.@Component,@Repository. - W klasie dodaj następujący kod:
Zwróć uwagę na adnotację
@Bean public Course computerNetworksCourse() { var student = new Student("Piotr", "Budynek", LocalDate.of(1990, 11, 7), "22334455", "budynek@student.agh.edu.pl"); var course = new Course("Sieci komputerowe"); course.enrollStudent(student); return course; }@Bean. Zastanów się, jakie będzie jej przeznaczenie. Czy przypomina Ci to podobny mechanizm poznany na poprzednich zajęciach? - W
SchoolInitializerwstrzyknij obiektCoursei dodaj metodę (możesz pozostawić istniejącą metodę adnotowaną@PostConstructutworzoną w poprzednim ćwiczeniu):@PostConstruct public void initComputerNetworksCourse() { computerNetworksCourse.getStudents() .forEach(student -> studentService.assignGrade(student, computerNetworksCourse, 4.5)); } - Przetestuj działanie systemu. W tym momencie w logach powinny być widoczne informacje o inicjowaniu i zamykaniu serwisów oraz powiadomienia z wszystkich wystawionych ocen.
- W analogiczny sposób dodaj do fabryki tworzenie innego kursu, np.
objectOrentedProgrammingCourse()oraz kod inicjujący wSchoolInitializer.💡Jeśli chcesz, możesz dla czytelności stworzyć kilka metod oznaczonych
@PostConstruct, Spring wywoła każdą z nich. - Działa? Jeśli nie to zastanów się, w jaki sposób Spring wyszukuje komponenty i co się dzieje, gdy mamy kilka beanów reprezentujących tę samą klasę (tutaj:
Course).Odpowiedź - kliknij
Spring domyślnie wyszukuje beany po typie, ale jeśli pojawi się kolizja, zaczyna wyszukiwać po nazwie samego beana. Domyślnie nazwę beana określa nazwa jego klasy (co niewiele zmienia), ale jeśli bean jest tworzony przez fabrykę to nazwa metody tworzącej beana staje się nazwą beana. Wówczas żeby wstrzyknąć odpowiedniego beana musimy zadbać, by wstrzykiwany parametr również miał taką samą nazwę. Jeśli chcemy tego uniknąć, możemy posłużyć się adnotacją
@Qualifier, która działa podobnie jak np. nazwane zależności z Guice. - Wszystkie beany danego typu możemy również potraktować jako kolekcję, którą Spring potrafi wstrzyknąć w całości. Dodaj do
SchoolInitializerjeszcze jeden atrybut i parametr konstruktora:List<Course>. Następnie dodaj jeszcze jedną metodę, która doda ocenę wszystkim studentom ze wszystkich kursów z listy i upewnij się, że program działa.💡Zwróć uwagę, że w tym przypadku nie będzie żadnej kolizji przy wstrzykiwaniu, bo wstrzykujemy wszystkie beany bez rozróżniania ich!