Pengenalan Spring dengan Akka

1. Pengenalan

Dalam artikel ini, kami akan memusatkan perhatian untuk mengintegrasikan Akka dengan Spring Framework - untuk membolehkan penyuntikan perkhidmatan berasaskan Spring kepada pelakon Akka.

Sebelum membaca artikel ini, disarankan untuk mengetahui asas-asas Akka terlebih dahulu.

2. Suntikan Ketergantungan di Akka

Akka adalah kerangka aplikasi yang kuat berdasarkan model kesesuaian Pelakon. Kerangka ini ditulis dalam Scala yang tentu saja membuatnya dapat digunakan sepenuhnya dalam aplikasi berbasis Java juga. Oleh itu, selalunya kita mahu menggabungkan Akka dengan aplikasi berasaskan Spring yang ada atau hanya menggunakan Spring untuk memasang kabel menjadi pelakon.

Masalah dengan integrasi Spring / Akka terletak pada perbezaan antara pengurusan kacang di Spring dan pengurusan pelakon di Akka: pelakon mempunyai kitaran hidup tertentu yang berbeza dari kitaran hidup kacang biasa .

Lebih-lebih lagi, pelakon dibagi menjadi pelakon itu sendiri (yang merupakan perincian pelaksanaan dalaman dan tidak dapat dikendalikan oleh Spring) dan rujukan pelakon, yang dapat diakses oleh kod pelanggan, serta dapat diselaraskan dan mudah alih antara waktu tayang Akka yang berbeza.

Nasib baik, Akka menyediakan mekanisme, iaitu sambungan Akka, yang menjadikan penggunaan kerangka suntikan ketergantungan luaran adalah tugas yang cukup mudah.

3. Pergantungan Maven

Untuk menunjukkan penggunaan Akka dalam projek Spring kami, kami memerlukan pergantungan Spring minimum - perpustakaan konteks musim bunga , dan juga perpustakaan akka-aktor . Versi perpustakaan dapat diekstrak kebahagian pom :

 4.3.1.RELEASE 2.4.8    org.springframework spring-context ${spring.version}   com.typesafe.akka akka-actor_2.11 ${akka.version}  

Pastikan untuk memeriksa Maven Central untuk versi terkini pergantungan konteks musim bunga dan akka-pelakon .

Dan perhatikan bagaimana, bahawa kebergantungan akka -aktor mempunyai postfix _2.11 pada namanya, yang menandakan bahawa versi kerangka Akka ini dibina berdasarkan versi Scala 2.11. Versi perpustakaan Scala yang sesuai akan dimasukkan dalam bangunan anda secara sementara.

4. Menyuntik Kacang Musim Semi ke dalam Pelakon Akka

Mari buat aplikasi Spring / Akka ringkas yang terdiri daripada pelakon tunggal yang dapat menjawab nama seseorang dengan mengeluarkan ucapan kepada orang ini. Logik ucapan akan diekstrak ke perkhidmatan yang berasingan. Kami ingin mengaktifkan perkhidmatan ini kepada contoh pelakon. Integrasi musim bunga akan membantu kita dalam tugas ini.

4.1. Menentukan Pelakon dan Perkhidmatan

Untuk menunjukkan suntikan perkhidmatan kepada pelakon, kami akan membuat kelas ringkas GreetingActor yang ditakrifkan sebagai pelakon yang tidak diketik (memperluas kelas asas Akka's UntypedActor ). Kaedah utama setiap pelakon Akka adalah kaedah onReceive yang menerima mesej dan memprosesnya mengikut beberapa logik yang ditentukan.

Dalam kes kami, pelaksanaan GreetingActor memeriksa apakah mesej itu adalah jenis Greet yang telah ditentukan , kemudian mengambil nama orang itu dari contoh Greet , kemudian menggunakan GreetingService untuk menerima ucapan untuk orang ini dan menjawab pengirim dengan rentetan ucapan yang diterima. Sekiranya mesej itu dari jenis lain yang tidak diketahui, ia disampaikan kepada kaedah pengendalian yang telah ditentukan oleh pelakon .

Mari kita lihat:

@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class GreetingActor extends UntypedActor { private GreetingService greetingService; // constructor @Override public void onReceive(Object message) throws Throwable { if (message instanceof Greet) { String name = ((Greet) message).getName(); getSender().tell(greetingService.greet(name), getSelf()); } else { unhandled(message); } } public static class Greet { private String name; // standard constructors/getters } }

Perhatikan bahawa jenis mesej Greet didefinisikan sebagai kelas dalaman statik di dalam pelakon ini, yang dianggap sebagai amalan yang baik. Jenis mesej yang diterima harus didefinisikan sedekat mungkin dengan pelakon untuk mengelakkan kekeliruan mengenai jenis mesej yang dapat diproses oleh pelakon ini.

Perhatikan juga anotasi Spring @Component dan @Scope - ini menentukan kelas sebagai kacang musim bunga yang dikendalikan oleh Spring dengan skop prototaip .

Skopnya sangat penting, kerana setiap permintaan pengambilan kacang harus menghasilkan contoh yang baru dibuat, kerana tingkah laku ini sesuai dengan siklus hidup pelakon Akka. Sekiranya anda menerapkan kacang ini dengan ruang lingkup lain, kes khas memulakan semula pelakon di Akka kemungkinan besar akan berfungsi dengan tidak betul.

Akhir sekali, melihat bahawa kita tidak perlu jelas @Autowire yang GreetingService contoh - ini adalah mungkin disebabkan oleh ciri-ciri baru Spring 4.3 dipanggil tersirat Constructor suntikan .

Pelaksanaan GreeterService cukup mudah, perhatikan bahawa kami mendefinisikannya sebagai kacang musim bunga dengan menambahkan anotasi @Component kepadanya (dengan skop singleton lalai ):

@Component public class GreetingService { public String greet(String name) { return "Hello, " + name; } }

4.2. Menambah Spring Support melalui Akka Extension

Kaedah termudah untuk mengintegrasikan Spring dengan Akka adalah melalui peluasan Akka.

Sambungan adalah contoh tunggal yang dibuat setiap sistem pelakon. Ia terdiri daripada kelas peluasan itu sendiri, yang menerapkan pemanjangan antara muka penanda , dan kelas id peluasan yang biasanya mewarisi AbstractExtensionId .

Oleh kerana kedua-dua kelas ini digabungkan erat, masuk akal untuk menerapkan kelas Extension yang bersarang dalam kelas ExtensionId :

public class SpringExtension extends AbstractExtensionId { public static final SpringExtension SPRING_EXTENSION_PROVIDER = new SpringExtension(); @Override public SpringExt createExtension(ExtendedActorSystem system) { return new SpringExt(); } public static class SpringExt implements Extension { private volatile ApplicationContext applicationContext; public void initialize(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public Props props(String actorBeanName) { return Props.create( SpringActorProducer.class, applicationContext, actorBeanName); } } }

Pertama - SpringExtension mengimplementasikan kaedah createExtension tunggal dari kelas AbstractExtensionId - yang merangkumi pembuatan instance pelanjutan, objek SpringExt .

The SpringExtension kelas juga mempunyai bidang statik SPRING_EXTENSION_PROVIDER yang memegang merujuk kepada hanya contoh itu. Selalunya masuk akal untuk menambahkan konstruktor peribadi untuk menyatakan secara terang-terangan bahawa SpringExtention sepatutnya menjadi kelas tunggal, tetapi kami akan menghilangkannya untuk kejelasan.

Kedua , SpringExt kelas dalaman yang statik adalah pelanjutan itu sendiri. Oleh kerana Extension hanyalah antara muka penanda, kami mungkin menentukan isi kelas ini mengikut kehendak kami.

Dalam kes kami, kami akan memerlukan kaedah inisialisasi untuk menjaga instance Spring ApplicationContext - kaedah ini akan dipanggil hanya sekali setiap inisialisasi peluasan.

Also we’ll require the props method for creating a Props object. Props instance is a blueprint for an actor, and in our case the Props.create method receives a SpringActorProducer class and constructor arguments for this class. These are the arguments that this class’ constructor will be called with.

The props method will be executed every time we’ll need a Spring-managed actor reference.

The third and last piece of the puzzle is the SpringActorProducer class. It implements Akka’s IndirectActorProducer interface which allows overriding the instantiation process for an actor by implementing the produce and actorClass methods.

As you probably already have guessed, instead of direct instantiation, it will always retrieve an actor instance from the Spring’s ApplicationContext. As we’ve made the actor a prototype-scoped bean, every call to the produce method will return a new instance of the actor:

public class SpringActorProducer implements IndirectActorProducer { private ApplicationContext applicationContext; private String beanActorName; public SpringActorProducer(ApplicationContext applicationContext, String beanActorName) { this.applicationContext = applicationContext; this.beanActorName = beanActorName; } @Override public Actor produce() { return (Actor) applicationContext.getBean(beanActorName); } @Override public Class actorClass() { return (Class) applicationContext .getType(beanActorName); } }

4.3. Putting It All Together

The only thing that’s left to do is to create a Spring configuration class (marked with @Configuration annotation) which will tell Spring to scan the current package together with all nested packages (this is ensured by the @ComponentScan annotation) and create a Spring container.

We only need to add a single additional bean — the ActorSystem instance — and initialize the Spring extension on this ActorSystem:

@Configuration @ComponentScan public class AppConfiguration { @Autowired private ApplicationContext applicationContext; @Bean public ActorSystem actorSystem() { ActorSystem system = ActorSystem.create("akka-spring-demo"); SPRING_EXTENSION_PROVIDER.get(system) .initialize(applicationContext); return system; } }

4.4. Retrieving Spring-Wired Actors

To test that everything works correctly, we may inject the ActorSystem instance into our code (either some Spring-managed application code, or a Spring-based test), create a Props object for an actor using our extension, retrieve a reference to an actor via Props object and try to greet somebody:

ActorRef greeter = system.actorOf(SPRING_EXTENSION_PROVIDER.get(system) .props("greetingActor"), "greeter"); FiniteDuration duration = FiniteDuration.create(1, TimeUnit.SECONDS); Timeout timeout = Timeout.durationToTimeout(duration); Future result = ask(greeter, new Greet("John"), timeout); Assert.assertEquals("Hello, John", Await.result(result, duration));

Here we use the typical akka.pattern.Patterns.ask pattern that returns a Scala's Future instance. Once the computation is completed, the Future is resolved with a value that we returned in our GreetingActor.onMessasge method.

Kita mungkin menunggu hasilnya dengan menerapkan kaedah Scala's Await.result ke Masa Depan , atau, lebih baik lagi, membina keseluruhan aplikasi dengan corak tak segerak.

5. Kesimpulan

Dalam artikel ini kami telah menunjukkan bagaimana untuk menggabungkan Spring Framework dengan Akka dan autowire kacang menjadi pelakon.

Kod sumber untuk artikel tersebut terdapat di GitHub.