ومرة أخرى ، يوم جيد! قريباً جداً ، سنبدأ التدريب للمجموعة التالية ،
"Developer on the Spring Framework" ، التي أجرى فيها درسًا مفتوحًا ، والذي أصبح بالفعل تقليدًا متوقعًا للإطلاق. تحدث ندوة الويب هذه حول تطوير عملاء REST باستخدام Spring ، وتعلموا أيضًا بالتفصيل حول تقنيات مثل Spring Cache و Spring Retry و Hystrix.
المحاضر:
يوري دفورشتسكي - مدرب في مركز لوكسوف للتدريب ، مطور رئيسي ، مرشح للعلوم الفيزيائية والرياضية.
حضر الندوة عبر الويب جمهور مختلف تمامًا ، حيث قاموا بتقييم معرفتهم بـ Spring خلال 0-6 نقاط على مقياس من 10 نقاط ، ومع ذلك ، واستنادا إلى المراجعات ، بدا الدرس المفتوح مفيدًا حتى للمستخدمين ذوي الخبرة.
بضع كلمات عن الربيع 5كما تعلمون ، فإن Spring Framework هو إطار عالمي وشعبي إلى حد ما لمنصة Java. يتكون الربيع من مجموعة من المشاريع الفرعية أو الوحدات النمطية ، والتي تتيح لك حل العديد من المشكلات. في الواقع ، هذه مجموعة كبيرة من "الأطر في إطار عمل" ، على سبيل المثال ، عدد قليل منها:
- ربيع IoC + AOP = السياق ،
- الربيع JDBC ،
- ربيع ORM ،
- بيانات الربيع (هذه مجموعة كاملة من المشاريع الفرعية) ،
- Spring MVC، Spring WebFlux،
- أمن الربيع
- Spring Cloud (هذه مجموعة أكبر من المشاريع الفرعية)
- دفعة الربيع ،
- التمهيد الربيع.
يستبدل Spring البرمجة ببعض المهام من أجل التكوين ، لكن التكوين يتحول أحيانًا إلى كابوس. لإنشاء تطبيقات على مستوى الإنتاج بسرعة ، يستخدمون
Spring Boot . هذا هو إطار خاص يحتوي على مجموعة من المبتدئين ("بداية") ، والتي تبسط تكوين أطر الربيع وغيرها من التقنيات.
لإظهار بعض ميزات Spring ، يكون موضوع حظر المواقع مثاليًا ، حيث أصبح من المألوف الآن)). إذا كنت ترغب في المشاركة بنشاط في الدرس والتمرين ، يوصى بتنزيل
المستودع برمز الخادم الذي اقترحه المعلم. نستخدم الأمر التالي:
git clone git@github.com:ydvorzhetskiy/sb-server.git
بعد ذلك ، قم فقط بتشغيل ، على سبيل المثال ، مثل هذا:
mvnw spring-boot:run
أكبر إنجاز لـ Spring Boot هو القدرة على بدء تشغيل الخادم عن طريق تشغيل الفئة الرئيسية في IntelliJ IDEA.
يحتوي ملف BlockedSite.java على الكود المصدري الخاص بنا:
package ru.otus.demoserver.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class BlockedSite { @Id @GeneratedValue private int id; private String url;
وهنا محتويات وحدة تحكم BlockedSitesController.java:
package ru.otus.demoserver.rest; @RestController public class BlockedSitesController { private final Logger logger = LoggerFactory.getLogger(BlockedSitesController.class); private final BlockedSitesRepository repository; public BlockedSitesController(BlockedSitesRepository repository) { this.repository = repository; } @GetMapping("/blocked-sites") public List<BlockedSite> blockedSites() { logger.info("Request has been performed"); return repository.findAll(); } }
انتبه أيضًا إلى قاعدة البيانات المتداخلة في pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> <relativePath/> </parent> <groupId>ru.otus</groupId> <artifactId>demo-server</artifactId> <version>0.0.1-SNAPSHOT</version> <url>demo-server</url> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
الآن ، بطريقة بسيطة وبسيطة ، نقوم بحفظ موقعين محظورين (DemoServerApplication.java) في قاعدة البيانات الخاصة بنا من خلال المستودع:
package ru.otus.demoserver; @SpringBootApplication public class DemoServerApplication { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(DemoServerApplication.class, args); BlockedSitesRepository repository = ctx.getBean(BlockedSitesRepository.class); repository.save(new BlockedSite("https://telegram.org/")); repository.save(new BlockedSite("https://azure.microsoft.com/")); } }
يبقى أن تبدأ الخادم باستخدام Spring Boot وفتح عنوان URL المناسب على المضيف المحلي
(localhost:8080/blocked-sites)
المضيف المحلي
(localhost:8080/blocked-sites)
. في الوقت نفسه ، سيعود الخادم الخاص بنا إلينا قائمة بالمواقع التي حظرناها ، أي تلك المواقع التي أضفناها عبر قاعدة البيانات.
حسنًا ، لقد حان الوقت لكتابة عميل إلى هذا الخادم. ولكن قبل الانتقال إلى هذا ، تحتاج إلى تذكر شيء ما.
تراجع نظريدعونا سرد بعض أساليب HTTP (الأفعال):
- الحصول على - الحصول على كيان أو قائمة.
- وظيفة - إنشاء كيان.
- وضع - تغيير الكيان ؛
- PATCH - تغيير الكيان (RFC -...) ؛
- حذف - حذف الكيان.
- HEAD ، OPTIONS - طرق "صعبة" لدعم بروتوكول HTTP وخدمات REST بشكل عام ؛
- TRACE هي طريقة قديمة لا يتم استخدامها.
لا يسع المرء إلا أن يتذكر خاصية مهمة مثل
الشغف . بعبارات بسيطة ، بغض النظر عن عدد المرات التي تطبق فيها العملية ، ستكون النتيجة هي نفسها كما لو كنت طبقتها مرة واحدة فقط. على سبيل المثال ، استقبلت رجلاً في الصباح ، قائلة "مرحبًا!" نتيجة لذلك ، يذهب صديقك إلى حالة "الترحيب" :-). وإذا قلت "مرحبًا!" له عدة مرات خلال اليوم ، فلن يتغير شيء ، وسيبقى في نفس الحالة.
الآن ، دعنا نفكر في أي من أساليب HTTP المذكورة أعلاه غير مناسبة؟ بالطبع ، من المفهوم أنك تلاحظ الدلالات. إذا كنت لا تعرف ، فسيعلمك المعلم أكثر حول هذا الموضوع ، بدءًا من الدقيقة 26 من الفيديو.
الراحةمن أجل كتابة وحدة تحكم REST ، يجب أن تتذكر ماهية REST:
- REST - نقل الحالة التمثيلية ؛
- إنه أسلوب معماري ، وليس معيارًا ؛
- إنه ، في الواقع ، مجموعة من قيود المبادئ ؛
- كان REST منذ زمن طويل ، ولكن ظهر المصطلح مؤخرًا نسبيًا ؛
- يسمى تطبيق الويب REST-style RESTful ، API الخاص به في هذه الحالة هو RESTful API (المتناقض هو Stateful) ؛
- يسمى الآن REST كل ما يريدون ...
أولاً ، إذا تحدثنا عن التفاعل في شكل خادم عميل ، فيجب أن يتم بناؤه في شكل طلب استجابة. نعم ، لا يتم بناء التفاعل دائمًا بهذه الطريقة ، ولكن هذا التفاعل شائع للغاية ، وبالنسبة لتطبيقات الويب ، يبدو شيء آخر غريبًا للغاية. ولكن ، على سبيل المثال ، مآخذ الويب - وهذا ليس مجرد REST.
ثانياً ، أهم قيود REST هو عدم وجود حالة العميل على الخادم. من المفترض أن يقوم العميل دائمًا بتمرير الحالة اللازمة إلى الخادم مع كل طلب ، أي يتم حفظ الحالة من جانب العميل ، ولا توجد جلسات على الخادم.
كيفية كتابة عميل في الربيع
للمتابعة ، ضع في الاعتبار العميل وقم بتشغيله (استخدم الرابط إلى المستودع):
git clone git@github.com:ydvorzhetskiy/sb-client.git
mvnw spring-boot:run
هذا تطبيق عميل ووحدة تحكم مكتوبة بالفعل ، وليس خادم ويب.
نحن ننظر إلى التبعيات:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> <relativePath/> </parent> <groupId>ru.otus</groupId> <artifactId>demo-client</artifactId> <version>0.0.1-SNAPSHOT</version> <url>demo-client</url> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.4.RELEASE</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.0.2.RELEASE</version> </dependency> <dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-javanica</artifactId> <version>1.5.12</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
العميل لديه التكوين:
1. RestTemplateConfig.java
package ru.otus.democlient.config; @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { return restTemplateBuilder .setConnectTimeout(Duration.ofSeconds(2)) .setReadTimeout(Duration.ofSeconds(3)) .build(); }
2. CacheConfig.java
package ru.otus.democlient.config; @Configuration public class CacheConfig { @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("sites"); } }
وهنا محتويات ملف SiteServiceRest.java:
package ru.otus.democlient.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.Cacheable; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.Collections; import java.util.List; @Service public class SiteServiceRest implements SiteService { private final RestTemplate restTemplate; private final String serverUrl; public SiteServiceRest( RestTemplate restTemplate, @Value("${application.server.url}") String serverUrl ) { this.restTemplate = restTemplate; this.serverUrl = serverUrl; } @Override public List<SiteInfo> findAllBlockedSites() { return restTemplate.exchange( serverUrl + "/blocked-sites", HttpMethod.GET, null, new ParameterizedTypeReference<List<SiteInfo>>() { } ).getBody(); } public List<SiteInfo> getDefaultSites() { return Collections.singletonList(new SiteInfo() {{ setUrl("http://vk.com/"); }}); } }
تلخيص قليلا:- يتم تقديم الطلبات من خلال RestTemplate.
- يمكن تخصيص RestTemplate ، وهذا هو الحبة العادية.
- يستخدم جاكسون لتعيين JSON في الكائنات.
- بعد ذلك - فقط رحلة الهوى الخاصة بك (توجد تفاصيل حول إطلاق العميل في الفيديو).
أيها الزملاء ، تحولت الندوة عبر الإنترنت إلى أنها كانت مفيدة للغاية ، لذلك ، حتى لا تفوت أي شيء ، فمن الأفضل مشاهدته بالكامل.
@Cacheable
واجهة برمجة التطبيقات الحقيقية "في القتال" ، أضف
@Cacheable
إلى الخدمة ، والعمل مع Spring Retry ،
@Cacheable
على
@Cacheable
وأكثر من ذلك بكثير. ندعوك أيضًا إلى
يوم الربيع
المفتوح الذي سيعقد قريبًا.
وكالعادة ، نحن في انتظار تعليقاتكم على الدرس المفتوح الماضي!