تحية ، أصدقاء. سيكون يوم الجمعة هذا هو الدرس الأول في مجموعة دورات
Java Developer الجديدة. سيتم تخصيص المنشور الحالي لهذه الدورة.

يستخدم العديد من مطوري جافا
Spring لتطبيق التبعيات. ربما جرب البعض
Google Guice أو حتى
خدمات OSGi . لكن الكثيرين لا يعرفون أن جافا يحتوي بالفعل على DI مدمج. هل تعتقد أنه ظهر في Java 11 أو 12؟ لا ، إنه متوفر مع Java 6.
يوفر
ServiceLoader القدرة على البحث وإنشاء مثيلات مسجلة للواجهات أو الطبقات المجردة. إذا كنت معتادًا على Spring ، فهذا مشابه جدًا
لتعليقات Bean و
Autowired . دعونا نلقي نظرة على أمثلة لاستخدام Spring و ServiceLoader. وناقش أوجه التشابه والاختلاف.
ربيع
أولاً ، دعنا نرى كيفية عمل DI بسيط في Spring. إنشاء واجهة بسيطة:
public interface SimpleService { String echo(String value); }
وتنفيذ الواجهة:
import org.springframework.stereotype.Component; @Component public class SimpleServiceImpl implements SimpleService { public String echo(final String value) { return value; } }
تحقق من
@Component
. سيسجل هذا التعليق التوضيحي فصلنا كحبة في سياق الربيع.
وطبقتنا الرئيسية.
@SpringBootApplication public class SpringExample implements CommandLineRunner { private static final Logger log = LoggerFactory.getLogger(SpringExample.class); @Autowired List<SimpleService> simpleServices; public static void main(String[] args) { SpringApplication.run(SpringExample.class, args); } public void run(final String... strings) throws Exception { for (SimpleService simpleService : simpleServices) { log.info("Echo: " + simpleService.echo(strings[0])); } } }
لاحظ التعليق التوضيحي
@Autowired
على
SimpleService
التحرير والسرد
SimpleService
.
@SpringBootApplication
تصميم AnnotationSpringBootApplication للبحث تلقائيًا عن الفول في الحزمة. ثم عند بدء التشغيل ، يتم حقنها تلقائيًا في
SpringExample
.
ServiceLoader
سنستخدم نفس الواجهة كما في مثال Spring ، لذلك لن نكررها هنا. بدلاً من ذلك ، انظر فورًا إلى تنفيذ الخدمة:
import com.google.auto.service.AutoService; @AutoService(SimpleService.class) public class SimpleServiceImpl implements SimpleService { public String echo(final String value) { return value; } }
في التنفيذ ، نقوم "بتسجيل" مثيل للخدمة باستخدام تعليق توضيحي
@AutoService
. هناك حاجة إلى هذا التعليق التوضيحي فقط في وقت الترجمة ، نظرًا لأن javac يستخدمه لإنشاء ملف تسجيل خدمة تلقائيًا (
ملاحظة المترجم: من أجل التبعية التي تحتوي على @AutoService
، حدد النطاق - قدم) :
META-INF/services/io.github.efenglu.serviceLoader.example.SimpleService
يحتوي هذا الملف على قائمة الفئات التي تنفذ الخدمة:
io.github.efenglu.serviceLoader.example.SimpleServiceImpl
يجب أن يكون اسم الملف هو الاسم الكامل للخدمة (الواجهة). يمكن أن يحتوي الملف على أي عدد من التطبيقات ، كل على سطر منفصل.
في التطبيقات ،
يجب أن يكون هناك مُنشئ بدون معلمات. يمكنك إنشاء مثل هذا الملف يدويًا ، ولكن استخدام التعليقات التوضيحية أسهل كثيرًا. والفئة الرئيسية:
public class ServiceLoaderExample { public static void main(String [] args) { final ServiceLoader<SimpleService> services = ServiceLoader.load(SimpleService.class); for (SimpleService service : services) { System.out.println("Echo: " + service.echo(args[0])); } } }
يتم استدعاء أسلوب
ServiceLoader.load
للحصول على
ServiceLoader
، والذي يمكن استخدامه للحصول على مثيلات الخدمة. يقوم مثيل ServiceLoader بتنفيذ واجهة
Iterable
لنوع الخدمة ، وبالتالي ، يمكن استخدام متغير
services
في
for each
حلقة.
ماذا بعد؟
كلتا الطريقتين صغيرة نسبيا. يمكن استخدام كلاهما مع التعليقات التوضيحية وبالتالي فهي سهلة الاستخدام إلى حد ما. فلماذا استخدام ServiceLoader بدلاً من الربيع؟
اعتمادا على
لنلقِ نظرة على شجرة التبعية في مثال الربيع البسيط:
[INFO] -----------< io.github.efenglu.serviceLoader:spring-example >----------- [INFO] Building spring-example 1.0.X-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ spring-example --- [INFO] io.github.efenglu.serviceLoader:spring-example:jar:1.0.X-SNAPSHOT [INFO] +- org.slf4j:slf4j-api:jar:1.7.25:compile [INFO] +- org.springframework:spring-context:jar:4.3.22.RELEASE:compile [INFO] | +- org.springframework:spring-aop:jar:4.3.22.RELEASE:compile [INFO] | +- org.springframework:spring-core:jar:4.3.22.RELEASE:compile [INFO] | | \- commons-logging:commons-logging:jar:1.2:compile [INFO] | \- org.springframework:spring-expression:jar:4.3.22.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.19.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot:jar:1.5.19.RELEASE:compile [INFO] \- org.springframework:spring-beans:jar:4.3.22.RELEASE:compile
والمقارنة مع ServiceLoader:
[INFO] io.github.efenglu.serviceLoader:serviceLoader-example:jar:1.0.X-SNAPSHOT ## Only provided dependencies for the auto service annotation [INFO] \- com.google.auto.service:auto-service:jar:1.0-rc4:provided [INFO] +- com.google.auto:auto-common:jar:0.8:provided [INFO] \- com.google.guava:guava:jar:23.5-jre:provided [INFO] +- com.google.code.findbugs:jsr305:jar:1.3.9:provided [INFO] +- org.checkerframework:checker-qual:jar:2.0.0:provided [INFO] +- com.google.errorprone:error_prone_annotations:jar:2.0.18:provided [INFO] +- com.google.j2objc:j2objc-annotations:jar:1.1:provided [INFO] \- org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:provided
إذا لم ننتبه إلى التبعيات المقدمة ، فلن يكون ServiceLoader أي تبعيات. هذا صحيح ، فهو يحتاج فقط إلى Java.
لا يهم حقًا ما إذا كنت تقوم بتطوير تطبيقك الذي يستند إلى Spring ، ولكن إذا كنت تكتب شيئًا ما سيتم استخدامه في العديد من الأطر المختلفة أو إذا كان لديك تطبيق وحدة تحكم صغير ، فقد يكون لهذا بالفعل أهمية كبيرة.
سرعة
بالنسبة لتطبيقات وحدة التحكم ، يكون وقت بدء تشغيل
ServiceLoader أقصر
بكثير من تطبيق Spring Boot. ويرجع ذلك إلى العدد الأقل من الشفرة القابلة للتنزيل ، وغياب المسح ، وغياب الانعكاس ، وغياب الأطر الكبيرة.
الذاكرة
الربيع ليس مشهورا لتوفير الذاكرة. إذا كان استخدام الذاكرة مهمًا لك ، فكر في استخدام ServiceLoader لـ DI.
وحدات جافا
أحد الجوانب الرئيسية لوحدات Java هي القدرة على حماية الفئات في وحدة نمطية بالكامل من الكود خارج الوحدة. ServiceLoader هي آلية تسمح للرمز الخارجي "بالوصول" إلى التطبيقات الداخلية. تسمح لك وحدات Java بتسجيل الخدمات للتطبيقات الداخلية ، مع الحفاظ على الحدود.
في الواقع ، هذه هي الآلية الوحيدة المعتمدة لدعم حقن التبعية لوحدات Java. يستخدم Spring ومعظم أطر عمل DI الأخرى الانعكاس للعثور على مكوناتها وتوصيلها. لكن هذا غير متوافق مع وحدات Java. حتى الانعكاس لا يمكن أن ينظر إلى الوحدات (ما لم تسمح بذلك ، ولكن لماذا تحتاج إلى السماح بذلك).
استنتاج
الربيع شيء عظيم. لديها وظائف أكثر بكثير مما كانت عليه في ServiceLoader. ولكن هناك أوقات يكون فيها ServiceLoader هو الاختيار الصحيح. انها بسيطة وصغيرة وسريعة ومتاحة دائما.
شفرة المصدر الكاملة للحصول على أمثلة في
Git Repo .
هذا كل شيء. أراك في
الدورة !