RPC, atau panggilan prosedur jarak jauh, hanya untuk mengatakannya: memanggil layanan di komputer jarak jauh sama seperti memanggil layanan lokal.
RPC dapat didasarkan pada protokol HTTP atau TCP. Layanan Web adalah RPC berdasarkan protokol HTTP. Ini memiliki kinerja lintas platform yang baik, tetapi kinerjanya tidak sebagus RPC berdasarkan protokol TCP. Dua aspek akan secara langsung mempengaruhi kinerja RPC, satu adalah metode transmisi, dan yang lainnya adalah serialisasi.
Seperti yang kita semua tahu, TCP adalah protokol lapisan transport, HTTP adalah protokol lapisan aplikasi, dan lapisan transport lebih banyak di bawah lapisan aplikasi. Dalam hal transmisi data, lapisan bawah lebih cepat. Oleh karena itu, secara umum, TCP harus lebih cepat dari HTTP. Sedangkan untuk serialisasi, Java menyediakan metode serialisasi default, tetapi dalam kasus konkurensi tinggi, metode ini akan membawa beberapa hambatan kinerja, sehingga serangkaian kerangka kerja serialisasi yang sangat baik telah muncul di pasar, seperti: Protobuf, Kryo, Hessian, Jackson, dll. Mereka dapat menggantikan serialisasi default yang lebih tinggi untuk memberikan pengaruh default.
Untuk mendukung konkurensi yang tinggi, pemblokiran tradisional IO jelas tidak cocok, jadi kita membutuhkan IO asinkron, yaitu nio. Java menyediakan solusi NIO, dan Java 7 juga memberikan dukungan NIO.2 yang lebih baik. Menerapkan NIO dengan Java bukanlah hal yang jauh, tetapi kita perlu terbiasa dengan detail teknis NIO.
Kita perlu menggunakan layanan pada node yang berbeda di lingkungan terdistribusi, dan melalui pendaftaran layanan, klien dapat secara otomatis menemukan layanan yang tersedia saat ini dan menghubungi layanan ini. Ini membutuhkan komponen registri layanan untuk mendaftarkan semua alamat layanan (termasuk: nama host dan nomor port) di lingkungan terdistribusi.
Hubungan antara aplikasi, layanan, dan registri layanan ditunjukkan pada gambar di bawah ini:
Beberapa layanan dapat dipublikasikan di setiap server. Layanan ini berbagi host dan port. Dalam lingkungan terdistribusi, server akan disediakan untuk secara bersama -sama menyediakan layanan. Selain itu, untuk mencegah satu titik kegagalan registri layanan, itu perlu dibangun ke dalam lingkungan cluster.
Artikel ini akan mengungkapkan proses spesifik pengembangan kerangka kerja RPC terdistribusi ringan. Kerangka kerja ini didasarkan pada protokol TCP, menyediakan fitur NIO, menyediakan metode serialisasi yang efisien, dan juga memiliki kemampuan untuk mendaftar dan menemukan layanan.
Menurut persyaratan teknis di atas, kami dapat menggunakan pilihan teknologi berikut:
Untuk dependensi Maven terkait, silakan lihat lampiran terakhir.
Langkah 1: Tulis antarmuka layanan
Antarmuka publik HelloService {String Hello (Name String);}Tempatkan antarmuka ini dalam paket jar klien mandiri untuk digunakan.
Langkah 2: Tulis kelas implementasi antarmuka layanan
@RPCService (HelloService.class) // Tentukan antarmuka jarak jauh kelas publik HelloServiceImpl mengimplementasikan HelloService {@Override Public String Hello (String Name) {return "Hello!" + nama; }}Gunakan anotasi RPCService untuk menentukan kelas implementasi antarmuka layanan. Anda perlu menentukan antarmuka jarak jauh untuk kelas implementasi, karena kelas implementasi dapat mengimplementasikan beberapa antarmuka, sehingga Anda harus memberi tahu kerangka kerja yang merupakan antarmuka jarak jauh.
Kode layanan RPCS adalah sebagai berikut:
@Target ({elementType.type})@retention(RetentionPolicy.runtime)@component // menunjukkan bahwa itu dapat dipindai oleh public spring @interface rpcservice {class <?> Value ();}Anotasi ini memiliki karakteristik anotasi komponen Spring dan dapat dipindai berdasarkan musim semi.
Kelas implementasi ini ditempatkan di paket JAR Server, yang juga menyediakan beberapa file konfigurasi server dan program bootstrap untuk memulai layanan.
Langkah 3: Mengkonfigurasi server
File konfigurasi pegas server bernama spring.xml, dan kontennya sebagai berikut:
<Beans ...> <Context: Component-Scan Base-Package = "com.xxx.rpc.sample.server"/> <Context: Properti-PlaceHolder Lokasi = "ClassPath: Config.Properties"/> <!-Konfigurasi Komponen Pendaftaran Layanan-> <bean id = "servicergistry"> <Konstruktor-Urgergasi = " value = "$ {registry.address}"/> </ bean> <!-Mengkonfigurasi server rpc-> <bean id = "rpcserver"> <konstruktor-arg name = "serveraddress" value = "$ {server.address}"/> <konstruktor-arg nama = "server" server. "Parameter konfigurasi spesifik ada di file config.properties, dan kontennya adalah sebagai berikut:
# Zookeeperer Server Registry.address = 127.0.0.1: 2181# server server RPC.address = 127.0.0.1: 8000
Konfigurasi di atas menunjukkan bahwa server zookeeper lokal terhubung dan layanan RPC dirilis pada port 8000.
Langkah 4: Mulai server dan publikasikan layanan
Untuk memuat file konfigurasi pegas untuk mempublikasikan layanan, cukup tulis bootloader:
kelas publik rpcbootstrap {public static void main (string [] args) {new classpathxmlappleCicedContext ("spring.xml"); }}Jalankan metode utama kelas RPCBootStrap untuk memulai server, tetapi ada dua komponen penting yang belum diimplementasikan, yaitu: Serviceregistry dan RPCServer. Detail implementasi spesifik akan diberikan di bawah ini.
Langkah 5: Menerapkan pendaftaran layanan
Fungsi pendaftaran layanan dapat dengan mudah diimplementasikan menggunakan klien Zookeeper. Kode Serviceregistry adalah sebagai berikut:
Public Class ServiceRegistry {private static final Logger Logger = loggerFactory.getLogger (serviceeregistry.class); Latch CountdownLatch Pribadi = CountdownLatch baru (1); RegistryDdress string pribadi; Public Serviceregistry (String RegistryAddress) {this.registryAddress = RegistryAddress; } public void register (data string) {if (data! = null) {zooKeeper zk = connectServer (); if (zk! = null) {createNode (zk, data); }}} Private ZooKeeper ConnectServer () {ZooKeeper zk = null; Coba {zk = zooKeeper baru (registryAddress, constant.zk_session_timeout, new Watcher () {@Override public void Process (WatchEdEvent Event) {if (event.getState () == event.keepertate.synconnected) {latch.countdown ();}}}}}}; latch.Await (); } catch (ioException | InterruptedException e) {logger.error ("", e); } return zK; } private void createNode (zooKeeper zK, string data) {try {byte [] bytes = data.getbytes (); String path = zk.create (constant.zk_data_path, bytes, zoodefs.ids.open_acl_unsafe, createMode.ephemeral_sequential); Logger.debug ("Buat node ZooKeeper ({} => {})", path, data); } catch (KeepeRexception | InterruptedException e) {Logger.Error ("", e); }}}Di antara mereka, semua konstanta dikonfigurasi melalui konstan:
Public Interface Constant {int zk_session_timeout = 5000; String zk_registry_path = "/registry"; String zk_data_path = zk_registry_path + "/data";}Catatan: Pertama, Anda perlu menggunakan baris perintah klien Zookeeper untuk membuat/mendaftar node permanen untuk menyimpan semua node layanan sementara.
Langkah 6: Menerapkan server RPC
Menggunakan Netty dapat mengimplementasikan server RPC yang mendukung NIO. Anda perlu menggunakan serviceeregistry untuk mendaftarkan alamat layanan. Kode RPCServer adalah sebagai berikut:
kelas publik RPCServer mengimplementasikan ApplicationContextAware, InitializingBean {private static final Logger Logger = loggerFactory.getLogger (rpcserver.class); Private String ServerAddress; Private Serviceregistry Serviceregistry; peta pribadi <string, objek> handlermap = hashmap baru <> (); // Simpan hubungan pemetaan antara nama antarmuka dan objek layanan RPCServer publik (String ServerAddress) {this.serverAddress = serverAddress; } public RPCServer (String ServerAddress, ServiceRegistry ServiceRegistry) {this.serverAddress = serverAddress; this.serviceregistry = serviceregistry; } @Override public void setApplicationContext (ApplicationContext CTX) melempar BeansException {MAP <String, Object> ServiceBeanMap = ctx.getBeanSwithannotation (rpcservice.class); // Dapatkan semua pegas dengan annotasi layanan RPCService if (maputils.isnotempty (serviceBeanMap)) {for (Object ServiceBean: ServiceBeanMap.VALUES ()) {String Interfacename = serviceBean.getClass (). GetAnnotation (rpcservice.class). ValeeE (). handlermap.put (interfacename, serviceBean); }}} @Override public void afterpropersiesset () melempar Exception {EventLoopGroup bossgroup = new nioeventloopGroup (); EventLoopGroup workergroup = new nioeventloopgroup (); coba {serverbootstrap bootstrap = serverbootstrap baru (); bootstrap.group (bossgroup, workergroup) .channel (nioSerVersocketchannel.class) .childhandler (channelInitializer baru <socketchannel> () {@override public initchannel (socketchannel) melempar pengecualian {channel.pipeline (). Permintaan RPC (untuk menangani permintaan) .ArdLast (RPCEncoder baru (RPCRESPONSE.Class)) // encode respons RPC (untuk mengembalikan respons) .AddLast (RPChandler baru (handlermap)); .ChildOption (channeloption.so_keepalive, true); String [] array = serveraddress.split (":"); Host string = array [0]; int port = integer.parseint (array [1]); Channelfuture Future = bootstrap.bind (host, port) .sync (); Logger.debug ("Server dimulai pada port {}", port); if (serviceeregistry! = null) {serviceeregistry.register (serveraddress); // Daftarkan Alamat Layanan} Future.Channel (). CloseFuture (). Sync (); } akhirnya {workergroup.shutdowngracelly (); bossgroup.shutdowngracelly (); }}}Dalam kode di atas, ada dua POJOS penting yang perlu dijelaskan, yaitu RPCREQUEST dan RPCRESPONSE.
Gunakan RPCRequest untuk merangkum permintaan RPC, kodenya adalah sebagai berikut:
kelas publik rpcRequest {private string requestId; Private String ClassName; Private String MethodName; kelas pribadi <?> [] ParameterTypes; Parameter objek pribadi []; // Getter/Setter ...}Gunakan RPCRespons untuk merangkum respons RPC, kode ini sebagai berikut:
kelas publik rpCresponse {private string requestId; Kesalahan Private Throwable; hasil objek pribadi; // Getter/Setter ...}Gunakan RPCDECODER untuk menyediakan decoding RPC, cukup memperpanjang metode decode kelas abstrak Netty BytetomessagedEcoder, kode ini sebagai berikut:
kelas publik rpcdecoder memperluas bytetomessagedecoder {kelas privat <?> genericclass; publik rpcdecoder (kelas <?> genericclass) {this.genericclass = genericclass; } @Override public void decode (channelHandlerContext ctx, bytebuf in, list <pesop> out) melempar Exception {if (in.readableBytes () <4) {return; } in.markreaderIndex (); int dataLength = in.readInt (); if (panjang data <0) {ctx.close (); } if (in.readableBytes () <datalength) {in.resetreaderIndex (); kembali; } byte [] data = byte baru [panjang data]; in.readbytes (data); Objek obj = serialisasiutil.deserialize (data, genericclass); out.add (obj); }}Gunakan RPCEncoder untuk memberikan pengkodean RPC, cukup memperpanjang Metode Encode Kelas Abstrak Netty's MessageToByTeencoder, kode ini adalah sebagai berikut:
kelas publik RPCEncoder memperluas MessageToByTeEncoder {private class <?> GenericClass; RPCENCODER PUBLIK (Kelas <?> GenericClass) {this.genericclass = genericclass; } @Override public void encode (channelHandlerContext ctx, objek di, bytebuf out) melempar Exception {if (genericclass.isInstance (in)) {byte [] data = serializationutil.serialize (in); out.writeint (data.length); out.writebytes (data); }}}Tulis kelas alat serialisasi dan gunakan Protostuff untuk mengimplementasikan serialisasi:
Serialisasi kelas publik {private static Map <class <?>, skema <? >> cacheedschema = concurrenthashMap baru <> (); Objenesis statis pribadi objenesis = objenesis baru (true); Serialisasi pribadi () {} @suppresswarnings ("Uncecked") Private Static <T> skema <T> getschema (kelas <T> cls) {skema <T> skema = (skema <T>) cacheedschema.get (cls); if (schema == null) {schema = runTimeschema.createFrom (cls); if (skema! = null) {cacheedschema.put (cls, skema); }} return skema; } @SuppressWarnings ("Uncecked") Public Static <T> byte [] serialize (t obj) {class <t> cls = (class <t>) obj.getClass (); LinkedBuffer buffer = LinkedBuffer.allocate (LinkedBuffer.default_buffer_size); coba {skema <T> schema = getschema (cls); Return ProtoStuffioutil.tobytearray (OBJ, skema, buffer); } catch (exception e) {lempar IllegalStateException baru (e.getMessage (), e); } akhirnya {buffer.clear (); }} public static <T> t deserialize (data byte [], kelas <T> cls) {coba {t message = (t) objenesis.newinstance (cls); Skema <T> skema = getschema (cls); Protostuffioutil.mergefrom (data, pesan, skema); kembali pesan; } catch (exception e) {lempar IllegalStateException baru (e.getMessage (), e); }}}Di atas menggunakan objenesis untuk membuat objek, yang lebih kuat dari refleksi Java.
Catatan: Jika Anda perlu mengganti kerangka kerja serialisasi lainnya, cukup ubah serialisasi. Tentu saja, cara yang lebih baik untuk mengimplementasikannya adalah dengan menyediakan item konfigurasi untuk memutuskan metode serialisasi mana yang akan digunakan.
Untuk menangani permintaan RPC di RPChandler, Anda hanya perlu memperluas kelas abstrak SimplechannelinBoundhandler Netty, kodenya adalah sebagai berikut:
kelas publik RPChandler memperluas SimpleChannelinBoundHandler <rpcRequest> {private static final Logger Logger = loggerFactory.getLogger (rpchandler.class); peta akhir pribadi <String, Object> Handlermap; rpchandler publik (peta <string, object> handlermap) {this.handlermap = handlermap; } @Override public void channelRead0 (final channelHandlerContext ctx, rpcRequest request) melempar Exception {rpCresponse response = rpCresponse baru (); response.setRequestId (request.getRequestId ()); Coba {Object Hasil = handle (request); response.setresult (hasil); } catch (Throwable t) {response.setError (t); } ctx.writeAndflush (respons) .addlistener (channelfutureListener.close); } pegangan objek pribadi (permintaan RPCRequest) melempar lempar {string className = request.getClassName (); Object ServiceBean = handlermap.get (className); Kelas <?> serviceClass = serviceBean.getClass (); String methodName = request.getMethodName (); Kelas <?> [] ParameTerTypes = request.getParameterTypes (); Objek [] parameter = request.getParameters (); /*Metode metode = serviceClass.getMethod (MethodName, parameterTypes); method.setAccessible (true); return method.invoke (serviceBean, parameter);*/ fastclass serviceFastclass = fastclass.create (serviceClass); Fastmethod serviceFastMethod = serviceFastclass.getMethod (MethodName, parameterTypes); return serviceFastMethod.invoke (servicebean, parameter); } @Override public void exceptionCaught (ChannelHandlerContext CTX, Throwable Cause) {Logger.Error ("Server Catch Exception", Cause); ctx.close (); }}Untuk menghindari masalah kinerja yang disebabkan oleh penggunaan refleksi Java, kita dapat menggunakan API refleksi yang disediakan oleh CGLIB, seperti FastClass dan FastMethod yang digunakan di atas.
Langkah 7: Mengkonfigurasi klien
Juga gunakan file konfigurasi pegas untuk mengonfigurasi klien RPC. Kode Spring.xml adalah sebagai berikut:
<beans ...> <konteks: Lokasi pemegang tempat properti = "classpath: config.properties"/> <!-Konfigurasi komponen penemuan layanan-> <bean id = "servicediscovery"> <konstruktor-arg nama = "registryaddress" value = "$ {registry.address}"/</bean> <! <constructor-arg name = "servicediscovery" ref = "serviceDiscovery"/> </t bean> </tagel>config.properties menyediakan konfigurasi spesifik:
# Zookeeper Server Registry.address = 127.0.0.1: 2181
Langkah 8: Menerapkan penemuan layanan
Juga gunakan Zookeeper untuk mengimplementasikan fungsi penemuan layanan, lihat kode berikut:
Public Class ServicedIscovery {private static final Logger Logger = LoggerFactory.getLogger (servicediscovery.class); Latch CountdownLatch Pribadi = CountdownLatch baru (1); Daftar Volatile Pribadi <String> Datalist = ArrayList baru <> (); RegistryDdress string pribadi; Public ServicedIscovery (String RegistryAddress) {this.registryAddress = RegistryAddress; Zookeeper ZK = ConnectServer (); if (zk! = null) {watchnode (zk); }} public String Discover () {string data = null; int size = datalist.size (); if (size> 0) {if (size == 1) {data = datalist.get (0); Logger.debug ("Menggunakan hanya data: {}", data); } else {data = datalist.get (threadlocalrandom.current (). nextInt (size)); Logger.debug ("Menggunakan data acak: {}", data); }} mengembalikan data; } private ZooKeeper ConnectServer () {ZooKeeper zk = null; Coba {ZK = ZooKeeper baru (RegistryAddress, Constant.zk_Session_Timeout, pengamat baru () {@Override public void Process (WatchEdEvent Event) {if (event.getState () == Event.KeeperSate.Syncconnected) {latch.countdown ();}}}}}}}}}}} {latch.countdown (); latch.Await (); } catch (ioException | InterruptedException e) {logger.error ("", e); } return zK; } private void watchnode (zooKeeper final zk) {coba {list <string> nodelist = zk.getChildren (constant.zk_registry_path, watcher baru () {@override public Process (watchedevent event) {if event.getType () == event.eVentType.nodype.nodyphent) {if event.getType () == event.eventype.nodype.nody }); Daftar <String> datalist = ArrayList baru <> (); untuk (string node: nodeList) {byte [] bytes = zk.getData (constant.zk_registry_path + "/" + node, false, null); datalist.add (string baru (bytes)); } Logger.debug ("data simpul: {}", datalist); this.datalist = distalist; } catch (KeepeRexception | InterruptedException e) {Logger.Error ("", e); }}}Langkah 9: Menerapkan Agen RPC
Di sini kami menggunakan teknologi proxy dinamis yang disediakan oleh Java untuk mengimplementasikan proxy RPC (tentu saja, itu juga dapat diimplementasikan menggunakan CGLIB). Kode spesifiknya adalah sebagai berikut:
kelas publik rpcproxy {private string serveraddress; Servicediscovery Private Servicediscovery; publik rpcproxy (string serveraddress) {this.serverAddress = serveraddress; } public rpcproxy (servicediscovery servicediscovery) {this.servicediscovery = servicediscovery; } @SuppressWarnings ("Uncecked") Public <T> t create (class <?> InterfaceClass) {return (t) proxy.newProxyInstance (Metode InterfaceClass.getClassLoader (), Objok, Objek InterfaceClass}, Objek Public () @OBSEKE (), Objek Public {] {Objur @EXTRACLASS {) {Objectan @Objek (), Throwable {RPCRequest Request = RPCREQUEST baru (); request.setParametertypes (method.getParametertypes ()); Integer.parseint (array [1]); } else {return response.getResult (); }}}); }}Untuk mengimplementasikan klien RPC menggunakan kelas RPCCLIENT, Anda hanya perlu memperluas kelas abstrak SimplechannelinBoundhandler yang disediakan oleh Netty, kodenya adalah sebagai berikut:
kelas publik RPCCLIENT meluas SimpleChannelinBoundHandler <RPCRESPONSE> {private static final Logger Logger = loggerFactory.getLogger (rpcclient.class); host string pribadi; port int pribadi; respons response pribadi; objek akhir privat obj = objek baru (); RPCClient publik (host string, int port) {this.host = host; this.port = port; } @Override public void channelRead0 (ChannelHandlerContext CTX, RPCRESPONSE Respons) Melempar Exception {this.response = respons; disinkronkan (obj) {obj.notifyall (); // Terima respons, bangun utas}} @Override public void exceptionCaught (ChannelHandlerContext CTX, Throwable Cause) melempar Exception {Logger.Error ("Client Catch Exception", Cause); ctx.close (); } public RPCresponse Send (RPCRequest Request) melempar Exception {EventLoopGroup grup = new nioeventLoopGroup (); coba {bootstrap bootstrap = bootstrap baru (); bootstrap.group (grup) .channel (niosocketchannel.class) .handler (channelInitializer baru <socketchannel> () {@override void initchannel (Socketchannel Channel) Lempar (channel.pipeline () .addLast (rpcencencoder baru (rpcu). .AddLast (RPCDECODER baru (RPCRESPONSE.Class)) // Decode respons RPC (untuk menangani respons) .AddLast (RPCCLIENT.THIS); Channelfuture Future = bootstrap.connect (host, port) .sync (); future.channel (). writeandflush (request) .sync (); disinkronkan (obj) {obj.wait (); // tidak ada respons yang diterima, menyebabkan utas menunggu} if (response! = Null) {found.channel (). CloseFuture (). Sync (); } return response; } akhirnya {group.shutdowngracelly (); }}}Langkah 10: Kirim permintaan RPC
Gunakan JUnit untuk menulis tes unit dalam kombinasi dengan musim semi, dengan kode berikut:
@Runwith (springjunit4classrunner.class) @contextConfiguration (lokasi = "classpath: spring.xml") kelas publik helloServicetest {@autowired private rpcproxy rpcproxy; @Test public void hellotest () {helloService helloService = rpcproxy.create (helloService.class); Hasil String = HelloService.hello ("Dunia"); Assert.assertequals ("halo! Dunia", hasilnya); }}Jalankan tes unit di atas dan jika tidak ada yang tidak terduga terjadi, Anda akan melihat bilah hijau.
Meringkaskan
Artikel ini mengimplementasikan kerangka kerja RPC yang ringan melalui Spring + Netty + Protostuff + Zookeeper. Ini menggunakan Spring untuk memberikan injeksi ketergantungan dan konfigurasi parameter, menggunakan Netty untuk mengimplementasikan transmisi data NIO, menggunakan Protostuff untuk mengimplementasikan serialisasi objek, dan menggunakan Zookeeper untuk mengimplementasikan pendaftaran dan penemuan layanan. Dengan menggunakan kerangka kerja ini, layanan dapat digunakan pada node apa pun di lingkungan terdistribusi. Klien memanggil implementasi spesifik server melalui antarmuka jarak jauh, sepenuhnya memisahkan pengembangan server dan klien, memberikan dukungan dasar untuk implementasi aplikasi terdistribusi skala besar.
Lampiran: Ketergantungan Maven
<!-junit-> <dependency> <GroupId> Junit </proupid> <ArtifactId> junit </artifactid> <version> 4.11 </version> <scope> tes </seupope> </dependency> <!-slf4j-> <dependency> <groupid> org.slf4j </groupid> <t Artifact> <roupid> SLF4J1 SLF4J1 <version> 1.7.7 </version> </dependency> <!-spring-> <dependency> <groupid> org.springframework </groupid> <ArTifactId> Spring-Context </arttifactid> <version> 3.2.12.release </version> </Dependency> <groupping> <sgroupid> <sroupdid> <org.springping> </versif> <sependency> <groupdency> <RupproupD> <version> 3.2.12.release </version> <scope> test </ scope> </dependency> <!-netty-> <dependency> <groupid> io.netty </proupid> <ArtifactId> netty-all </artifactid> <version> 4.0.24. final </version> </dependency> </artifactid> <version> 4.0.24. final </version> </dependency> </artifactid> <version> 4.0.24. final </version> </dependency> </artifactD> <version> 4.0.24. final </version> </Dependency> </lottifactD> <version> 4.0.24. Final </Versi </Dependency> </! <groupid> com.dyuproject.protostuff </groupid> <ArTifactId> Protostuff-core </artifactid> <version> 1.0.8 </version> </gandendency> <!-ZooKeeper-<ceplependency> <groupid> org.apache.zooKeeper </groupid> <ArtacAcid> <sroupEpID> <ROTACEEPER.ZOEKEEPER </Groupid> <version>3.4.6</version></dependency><!-- Apache Commons Collections --><dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version></dependency><!-- Objenesis --><dependency> <groupId>org.objenesis</groupId> <ArtifactId> Objenesis </artifactId> <version> 2.1 </version> </gandendency> <!-cglib-> <dependency> <groupid> CGLIB </groupid> <ArtifactId> CGLIB </artifactid> <version> 3.1 </version> </dependensi>