CodeGym /Java блог /Случаен /Пролет за мързеливи хора Основа, основни концепции и прим...
John Squirrels
Ниво
San Francisco

Пролет за мързеливи хора Основа, основни концепции и примери с код. Част 2

Публикувано в групата
В предишната статия обясних накратко Howво е Spring и Howво са боб и контекст. Сега е време да го изпробвате. Ще го направя с помощта на IntelliJ IDEA Enterprise Edition. Но всички мои примери трябва да работят и в безплатното IntelliJ IDEA Community Edition. На екранните снимки, ако видите, че имам няHowъв прозорец, който вие нямате, не се притеснявайте — не е важен за този проект :) Пролет за мързеливи хора Основа, основни концепции и примери с code.  Част 2 - 1Първо създайте празен проект на Maven. Показах How да направите това в статията на този линк . Прочетете до думите „ Време е да преобразуваме нашия проект Maven в уеб проект. “ — след това статията показва How да направим уеб проект, но в момента нямаме нужда от това. В src/main/javaпапка, създайте пакет (в моя случай го нарекох " en.codegym.info.fatfaggy.animals. Можете да го наречете Howто искате. Само не забравяйте да замените името на моя пакет с вашето име на всички правилни места. Сега създайте класа Mainи добавете метод

public static void main(String[] args) {
    ...
}
След това отворете file pom.xml и добавете dependenciesсекцията. Сега отидете в хранorщето на Maven и намерете контекста на Spring за най-новата стабилна version. Поставете Howвото намерим в dependenciesраздела. Описах този процес по-подробно в тази друга статия на CodeGym (вижте раздела, озаглавен „ Свързване на зависимости в Maven “). Тогава самият Maven ще намери и изтегли необходимите зависимости. В крайна сметка трябва да получите нещо подобно: Пролет за мързеливи хора Основа, основни концепции и примери с code.  Част 2 - 2В прозореца отляво можете да видите структурата на проекта с пакета и класа Main. Средният прозорец показва How изглежда pom.xml за мен. Добавих и свойствараздел към него. Този раздел казва на Maven коя version на Java използвам в моите изходни файлове и коя version да компorрам. Това е само за да не ме предупреди IDEA, че използвам стара version на Java. Това не е задължително :) Десният прозорец показва ясно, че въпреки че свързахме само модула spring-context, той автоматично изтегли модулите spring-core, spring-beans, spring-aop и spring-expression. Можехме да свържем всеки модул поотделно, като напишем зависимост за всеки модул с изричната version във file pom.xml, но засега сме доволни от нещата, Howвито са. Сега създайте entitiesпакета и създайте 3 класа в него: Cat, Dog, Parrot. Нека да дадем име на всяко животно (private String name— можете да codeирате някои стойности там). Гетерите/сетерите са публични. Сега преминаваме към Mainкласа и main()метода и пишем нещо подобно:

public static void main(String[] args) {
	// Create an empty Spring context that will look for its own beans based on the annotations in the specified package
	ApplicationContext context = 
		new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.entities");

	Cat cat = context.getBean(Cat.class);
	Dog dog = (Dog) context.getBean("dog");
	Parrot parrot = context.getBean("parrot-polly", Parrot.class);

	System.out.println(cat.getName());
	System.out.println(dog.getName());
	System.out.println(parrot.getName());
}
Първо създаваме контекстен обект, казвайки на конструктора в кой пакет да търси, за да намери зърна. С други думи, Spring ще премине през този пакет и ще се опита да намери класове, маркирани със специални анотации, които показват, че те са beans. След това създава обектите на тези класове и ги поставя в контекста. След това получаваме котка от този контекст. Извикваме контекстния обект, за да ни даде bean (обект), указващ класа на обекта, който искаме (между другото, можем също да посочим интерфейси, не само класове). След това Spring връща обект от искания клас, който след това записваме в променлива. След това молим пролетта да ни донесе боб, наречен "куче". Когато пролетта създаде aDogобект, той дава на обекта стандартно име (освен ако на създадения bean не е присвоено изрично име), което е името на класа, но с начална малка буква. В този случай нашият клас се нарича Dog, така че името на компонента е be "куче". Ако имаме нужда от BufferedReaderобект там, тогава Spring ще го наименува "bufferedReader". И тъй като Java не може да бъде 100% сигурна кой клас искаме, тя връща обект Object, който след това ръчно прехвърляме към желания тип, т.е.Dog. По-удобен е вариантът, при който класът е посочен изрично. Третият вариант е да получите bean по име на клас и по име на bean. Възможно е контекстът да има няколко компонента от един клас. За да посочим конкретното зърно, от което се нуждаем, посочваме името му. Тъй като ние също изрично посочваме класа тук, вече не трябва да изпълняваме каст. ВАЖНО!Ако Spring намери няколко зърна, които отговарят на нашите изисквания, тогава тя не може да определи кой зърн да ни даде, така че ще хвърли изключение. Съответно, за да избегнете тази ситуация, трябва да се опитате да бъдете възможно най-конкретни, като казвате на Spring кое зърно ви трябва. Ако Spring търси своя контекст и не успее да намери нито един bean, който да отговаря на нашите изисквания, тогава той също ще хвърли изключение. И накрая, ние просто показваме имената на нашите животни, за да проверим дали наистина сме получor предметите, от които се нуждаем. Но ако стартираме програмата сега, ще видим, че Пролетта е нещастна — тя не може да намери животните, от които се нуждаем в своя контекст. Това е така, защото не е създал тези зърна. Както казах по-рано, когато Spring сканира класове, той търси свои собствени анотации на Spring. И ако Spring не намери тези анотации, значи не не мисля, че тези класове съответстват на зърна, които трябва да създаде. Коригирането на това просто изисква добавяне на@Componentанотация пред всеки от нашите класове животни.

@Component
public class Cat {
	private String name = "Oscar";
	...
}
Но има още. Ако трябва изрично да кажем на Spring, че бийнът за този клас трябва да има конкретно име, ние посочваме името в скоби след анотацията. Например, за да кажем на Spring да даде името " parrot-polly" на папагалското зърно, което е името, което ще използваме, за да получим този папагал в метода main, трябва да направим нещо подобно:

@Component("parrot-polly")
public class Parrot {
	private String name = "Polly";
	...
}
Това е целият смисъл на автоматичната конфигурация . Вие пишете вашите класове, маркирате ги с необходимите анотации и казвате на Spring пакета, който съдържа вашите класове. Това е пакетът, през който рамката ще премине, за да намери анотации и да създаде обекти от тези класове. Между другото, Spring не търси само @Componentанотации, но и всички други анотации, които наследяват тази. Например, @Controller, , , и други, които ще представим в бъдещи статии @RestController. Сега ще се опитаме да направим същото, като използваме базирана на Java конфигурация . За да започнете, премахнете@Service@Repository@Componentанотации от нашите класове. За да направим нещата по-предизвикателни, представете си, че не сме написали тези класове, така че не можем лесно да ги модифицираме, което означава, че не можем да добавяме анотации. Сякаш тези класове са опаковани в няHowва библиотека. В този случай няма начин да редактираме тези класове, така че да бъдат разпознати от Spring. Но имаме нужда от обекти от тези класове! Тук се нуждаем от базирана на Java конфигурация, за да създадем обектите. За да започнете, създайте пакет с име като configs. В този пакет създайте обикновен Java клас, нещо като MyConfig, и го маркирайте с @Configurationанотацията.

@Configuration
public class MyConfig {
}
Сега трябва да променим main()метода, като променим начина, по който създаваме контекста. Можем or изрично да посочим кой клас има нашата конфигурация:

ApplicationContext context =
	new AnnotationConfigApplicationContext(MyConfig.class);
Ако имаме няколко различни класа, които създават зърна и искаме да свържем няколко от тях едновременно, просто ги посочваме всички там, разделени със запетаи:

ApplicationContext context =
	new AnnotationConfigApplicationContext(MyConfig.class, MyAnotherConfig.class);
И ако имаме твърде много от тях и искаме да ги свържем всички едновременно, тогава просто посочваме името на пакета, в който се съдържат:

ApplicationContext context =
	new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.configs");
В този случай Spring ще прегледа пакета и ще намери всички класове, маркирани с анотацията @Configuration. Е, и ако имаме наистина голяма програма, в която конфигурациите са разделени на различни пакети, тогава просто посочваме разделен със запетаи списък с имена на пакетите, които съдържат конфигурациите:

ApplicationContext context =
	new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.database.configs",
		"en.codegym.info.fatfaggy.animals.root.configs",
		"en.codegym.info.fatfaggy.animals.web.configs");
Или името на пакет, който е общ за всички тях:

ApplicationContext context =
	new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals");
Можете да го направите Howто искате, но ми се струва, че първата опция, която просто посочва клас с конфигурациите, ще подхожда най-добре на нашата програма. Когато създава контекст, Spring търси класове, маркирани с @Configurationанотацията, и ще създаде свои собствени обекти от тези класове. Той се опитва да извика методи, маркирани с @Beanанотацията, което означава, че тези методи връщат beans (обекти), които Spring ще добави към контекста. А сега ще създадем бобчета за котка, куче и папагал в нашия клас с конфигурация, базирана на Java. Това се прави доста лесно:

@Bean
public Cat getCat() {
	return new Cat();
}
Тук ръчно създаваме нашата котка и я предаваме на Spring, която след това държи нашия обект в неговия контекст. Тъй като не сме дали изрично име на нашия bean, Spring ще му даде същото име като името на метода. В нашия случай котешкото зърно ще се нарича " getCat". Но тъй като използваме класа, а не името, за да получим котешкия компонент в метода main, името на компонента не е важно за нас. По същия начин създайте кучешки зърно, като имате предвид, че Spring ще даде името на метода на зърна. За да назовем изрично нашия папагалски боб, ние просто посочваме името му в скоби след анотацията @Bean:

@Bean("parrot-polly")
public Object weNeedMoreParrots() {
	return new Parrot();
}
Както можете да видите, тук посочих Objectтип връщане и дадох на метода произволно име. Това не засяга името на зърна, защото ние изрично посочихме името тук. Все пак е по-добре да посочите повече or по-малко значима върната стойност и име на метода. Направете това, ако не по друга причина, а за да си направите услуга, когато отворите отново проекта след година. :) Сега разгледайте ситуацията, в която имаме нужда от едно зърно, за да създадем друго зърно . Да предположим например, че искаме името на котката в котешкото зърно да бъде името на папагала плюс низа "-killer". Няма проблем!

@Bean
public Cat getCat(Parrot parrot) {
	Cat cat = new Cat();
	cat.setName(parrot.getName() + "-killer");
	return cat;
}
Тук Spring ще види, че за да създаде този bean, рамката трябва да премине в създадения преди това bean paparrot. Съответно, той ще организира необходимата верига от извиквания на метод: първо се извиква методът за създаване на папагал и след това рамката предава новия папагал на метода за създаване на котка. Ето къде влиза в действие инжектирането на зависимости : самата Spring предава необходимия папагалски компонент към нашия метод. Ако IDEA се разстрои от parrotпроменливата, не забравяйте да промените типа на връщане на метода за създаване на папагал от Objectна Parrot. В допълнение, базираната на Java конфигурация ви позволява да изпълнявате абсолютно всеки Java codeвъв вашите методи за създаване на боб. Наистина можете да правите всичко: да създавате други спомагателни обекти, да извиквате всяHowви други методи, дори и тези, които не са маркирани с Spring анотации, да създавате цикли, булеви условия — Howвото ви хрумне! Това не е възможно с автоматичната конфигурация и още по-малко с XML конфигурацията. Сега нека разгледаме проблем, който е малко по-забавен. Полиморфизъм и интерфейси :) Ще създадем WeekDayинтерфейс и ще създадем 7 класа, които имплементират този интерфейс: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday. Ще дадем на интерфейса String getWeekDayName()метод, който ще върне името на деня от седмицата за съответния клас. С други думи, Mondayкласът ще се върне "Monday"и т.н. При стартиране на приложението, да предположим, че нашата задача е да поставим bean, съответстващ на текущия ден от седмицата в контекста. Не bean-ове за всички класове, които имплементират интерфейса — само един bean, от който се нуждаем. Можете WeekDayда направете това приблизително така:

@Bean
public WeekDay getDay() {
	DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
	switch (dayOfWeek) {
		case MONDAY: return new Monday();
		case TUESDAY: return new Tuesday();
		case WEDNESDAY: return new Wednesday();
		case THURSDAY: return new Thursday();
		case FRIDAY: return new Friday();
		case SATURDAY: return new Saturday();
		default: return new Sunday();
	}
}
Тук типът връщане е нашият интерфейс. Методът връща добросъвестен обект на един от класовете, които имплементират интерфейса, в зависимост от текущия ден от седмицата. Сега можем да направим следното в main()метода:

WeekDay weekDay = context.getBean(WeekDay.class);
System.out.println("Today is " + weekDay.getWeekDayName() + "!");
За мен програмата ми казва, че е неделя :) Сигурен съм, че ако стартирам програмата утре, контекстът ще съдържа напълно различен обект. Обърнете внимание, че получаваме bean просто с помощта на интерфейса: context.getBean(WeekDay.class). Spring ще търси своя контекст за bean, който имплементира интерфейса, и ще го върне. Тогава се оказва, че нашата WeekDayпроменлива завършва с неделен обект и познатата концепция за полиморфизъм се прилага, когато работим с тази променлива. :) Сега няколко думи за комбинирания подход , при който някои bean-ове се създават автоматично от Spring, някои чрез сканиране на пакети за класове с анотация @Component, а други чрез Java-базирана конфигурация. Докато обмисляме това, ще се върнем към оригиналната version, където Cat, DogиParrotкласове бяха отбелязани с @Componentанотацията. Да предположим, че искаме да създадем зърна за нашите животни, като накараме Spring автоматично да сканира пакета entities, но също така искаме да създадем зърно с деня от седмицата, Howто току-що направихме. Всичко, което трябва да направите, е да добавите @ComponentScanанотацията на ниво клас MyConfig, което посочваме при създаването на контекста в main(), и да посочите в скоби пакета, който трябва да бъде сканиран и автоматично да създаде bean-ове на необходимите класове:

@Configuration
@ComponentScan("en.codegym.info.fatfaggy.animals.entities")
public class MyConfig {
	@Bean
	public WeekDay getDay() {
		DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
		switch (dayOfWeek) {
			case MONDAY: return new Monday();
			case TUESDAY: return new Tuesday();
			case WEDNESDAY: return new Wednesday();
			case THURSDAY: return new Thursday();
			case FRIDAY: return new Friday();
			case SATURDAY: return new Saturday();
			default: return new Sunday();
		}
	}
}
Когато създава контекста, Spring вижда, че трябва да обработи MyConfigкласа. Той влиза в класа и вижда, че трябва да сканира en.codegym.info.fatfaggy.animals.entitiesпакета " " и да създаде компоненти на тези класове, след което изпълнява метода MyConfigна класа getDay()и добавя WeekDayкомпонент към контекста. В main()метода вече имаме достъп до всички зърна, от които се нуждаем: Howто животински обекти, така и зърно с деня от седмицата. Ако някога ви се наложи да накарате Spring също да вземе някои XML конфигурационни файлове, можете да направите собствено търсене в мрежата, за да намерите обяснение :) Резюме:
  • Опитайте се да използвате автоматична конфигурация
  • По време на автоматичната конфигурация посочете името на пакета, който съдържа класовете, чиито компоненти трябва да бъдат създадени
  • Тези класове са маркирани с @Componentанотацията
  • Spring преминава през всички тези класове, създава обекти и ги поставя в контекста;
  • Ако автоматичната конфигурация не ни устройва по няHowва причина, използваме конфигурация, базирана на Java
  • В този случай създаваме обикновен Java клас, чиито методи връщат нужните ни обекти. Маркираме този клас с @Configurationанотацията, ако ще сканираме целия пакет, instead of да посочим конкретен клас с конфигурацията, когато създаваме контекста
  • Методите от този клас, които връщат beans, са маркирани с @Beanанотацията
  • Ако искаме да активираме автоматично сканиране, когато използваме базирана на Java конфигурация, използваме анотацията @ComponentScan.
Ако тази статия е била напълно объркваща, опитайте се да я прочетете след няколко дни. Или ако сте на едно от ранните нива на CodeGym, може да ви е малко рано да изучавате Spring. Винаги можете да се върнете към тази статия малко по-късно, когато се почувствате по-уверени в уменията си за програмиране на Java. Ако всичко е ясно, тогава можете да опитате да конвертирате някой ваш домашен проект в Spring :) Ако някои неща са ясни, но други не са, моля, оставете коментар :) Кажете ми вашите предложения и критики, ако съм сгрешил някъде or е написал няHowва глупост :) В следващата статия ще се потопим рязко в spring-web-mvc и ще направим просто уеб приложение с помощта на Spring.
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION