Spring Tips: Spring Native 0.10.0

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

I have to say, I'm still not 100% what to think of native builds. They have tons of shiny stuff that looks cool, but also a bunch of drawbacks. And especially in server applications, they're not even faster, just smaller.

๐Ÿ‘๏ธŽ︎ 8 ๐Ÿ‘ค๏ธŽ︎ u/CartmansEvilTwin ๐Ÿ“…๏ธŽ︎ Jun 17 2021 ๐Ÿ—ซ︎ replies

Neat stuff. Once you get past the long intro where you get a very basic introduction to things like aot, jit, graal(skip the first 8 minutes to bypass) it mostly showcases examples of compiling and executing sample projects with graalvm. The later half of the video shows how to support things like serialization, proxies, reflection, external resource loading, that need workarounds to work properly.

๐Ÿ‘๏ธŽ︎ 7 ๐Ÿ‘ค๏ธŽ︎ u/deadron ๐Ÿ“…๏ธŽ︎ Jun 17 2021 ๐Ÿ—ซ︎ replies
Captions
hi spring fans in this installment we're going to talk about growl vm and spring native but first let me tell you a story i was walking through a forest and i found some mushrooms that i consumed and then i grew very very small oh you know oh wait wait a minute no that might have been that might have just been a weird dream i had or was that the uh beginning to alice in wonderland it doesn't matter i still feel like the story is pretty appropriate because gravium makes me sometimes feel like alice in wonderland what's gravium well graviem is a open jdk distribution with some promising extras gravium comes from oracle labs a part of oracle who are coincidentally the same folks that drive a lot of the work being done on java and open jdk itself java is awesome and graviem is awesome but they are not the same thing or at least they're not the same thing for the moment hopefully project laden which strives to bring aot support to the open jdk platform will see a convergence of functionality but for now since we can't really speculate on that in middle of 2021 let's just talk about grav vm it's the tool to know anyway gravium started off as a faster just in time implementation so just in time compiler that was implemented in nice clean refractable java code that had hoped to replace the creakingly old spaghetti c plus code and hotspot the just-in-time compiler analyzes code at runtime and turns frequently access code in to native architecture-specific binaries so while your application may start as purely interpreted java bytecode it might end up as a mix of architecture and operating specific native code and interpret byte code the process is usually transparent to the developer it's just one of the freebies that java developers enjoy for using the jvm the result can be applications that for certain hot path scenarios may run many times faster than the equivalent interpreted byte code large organizations know about this dynamic and sometimes try to leverage it some like google and alibaba won't even put services into production until they've been warmed up the resulting application goes through a lot of different runs to exercise certain code paths to trigger the just-in-time compilation these applications run very quickly and can even in some cases outperform equivalent c or c plus plus code because the just-in-time compiler can do whole code based optimizations that we may not be able to take advantage of when we write our native c and c plus code you can use graviem as a better just in time compiler if you like but grav vm has a few other party tricks one graviem feature that's super interesting is a polyglot runtime that lets you run artifacts from different languages like javascript java ruby r etc in the same run time so imagine defining r functions then injecting a reference to that function in your spring boot application yes that's the thing i can say and do that's not crazy in 2021 neat gravel vm has this nifty new feature called the native image builder the native image builder is an ahead of time compiler as opposed to a just-in-time compiler if having your code retroactively turned into native code at runtime is cool then surely having all of your code proactively turned into native code is even cooler so let's review some vocabulary when we talk about compilation in the java platform we're typically talking about taking our java source code files and turning that into bytecode.class files just in time compilation return refers to turning your runtime code as as the program is running and finding the frequently accessed code paths and then turning those particular segments of code into native code and then ahead of time compilation turns all the code at compile time uh including all the byte code in the class path into native code proactively the native image builder is an ahead of time compiler it chucks out everything that it has determined isn't being used at runtime i repeat this is important it throws out everything that it determines during static analysis at compile time that it thinks is not being used at run time this process takes a long time because it's not just compiling our java source code files but also compiling all the class files into native code and analyzing which is which code is used and and when the resulting architecture specific binaries small very small like alice in wonderland small it includes only the minimum jre and the minimum types from all the libraries on the class path that are required to support the application the resulting applications tend to start in milliseconds and take tens of megabytes of ram not hundreds or you know you know worse uh it's possible to get a spring boot application with data access and a web server and a java runtime uh not exactly a jre but you know something like that that's less than 20 megs of space on disc this is phenomenal growl vm really is a wonderland but just as they did for our protagonist alice things get well uh you know weird when our applications get that small java is a funny old thing it feels like a more stodgy systems programming language when compared to languages like ruby python javascript etc but it feels very dynamic compared to something like c or c plus plus or go it has a it has a runtime and it has the ability to answer questions about its own state you can dynamically generate new classes or interfaces uh at runtime and load them into the class loader then use reflection to with just the string name of that generated class uh dynamically instantiate an instance of that class and then if you generated an interface you can also generate a jdk invocation handler based proxy that gives you an object that implements the interface and then you can invoke that code using nothing but strings if you want and then if you wanted to you could also then take that object and serialize its bytes to disk or to database or load it into another jvm over the network the possibilities are endless all those things may be done after the application has been compiled started and is running and so perhaps you're beginning to see the problem there's no guarantee that growl vm will see or understand all this dynamic behavior especially at compile time when it does its static analysis it might throw away all the code that would later on be used by these dynamic behaviors and that are required to support those things at runtime graph vm is what we in the highfalutin framework business called a party pooper so gravim supports a configuration through a java api through json files and through command line switches that clue gravium to the particular behavior desired at runtime if you want to create a jdk proxy you can put an entry in the appropriate json configuration file and graphvm will pre-compute a type that implements all the interfaces that you specify and then bake that into the memory of the native image heap then at runtime when your code tries to build the proxy using that those types those interfaces gravel vm will hand you the pre-computed type the possibilities are there certainly but as you can imagine this can quickly get tedious or exhausting if you're building on top of more dynamic abstractions like spring so what we want is some way to automate all of that and this is where spring native comes in spring native knows about the scenarios in which your applications and which typical spring boot applications might need configuration and it provides that configuration for you so that we don't have to uh to do that ourselves it's awesome the goal with spring native is that you can bring your application unchanged and get a gravium native image in this video we're going to look at growl vm and spring native and we're going to see how they can help us deliver our tiny applications for big systems to production and sure things are going to look a little different but at the end of the day it's still spring alice said it best i know who i was when i got up this morning but i think i must have been changed several times since then this rabbit hole is deep and very interesting so let's get started let's talk about how to install grav vm on your local machine one thing you can do is to go to gravium.org and then download either the gravium community or the gravium enterprise edition of the software i chose the community version choose download from github it'll take you to a page where you can find different variants for your operating system and the version of java that you'd like to use i am on mac os and i'm using java 11 so i chose that one once you've downloaded it you're going to want to make sure you install it like you would any other open jdk distribution set up in a directory add that directory to an environment variable or reference that directory in an environment variable called java underscore home capital letters and then make sure that java home forward slash bin is in the path for your operating system another option if you have a package manager on your operating system is to use that to install this open jk distribution one very popular option in the jvm is sdkman sdkman is a jvm specific ecosystem tool just say sdk list java it will show you all the different virtual machines that you have i have got the gravium 21.1.0 r11 version of gravion installed so you can see it's already installed you just say sdk installed java and then the particular version you can go a step further and make it the default version of java by saying sdk default java and then referencing the particular version and with that we've got graviem installed let's take a look at the basics we're going to start with the absolute default bare minimum configuration that you get if you were to go to start that spring rail today we're just going to build a traditional application an imperative traditional kind of application maybe imperative is the right way to think about it we're going to use jpa we'll use h2 we use lombok we're going to use the web support etc and then finally most importantly we're going to bring in spring native the experimental spring native okay hit generate let's open this in the ide and this is just a traditional imperative non-reactive application i'll do a reactive example in just a second here but what you need to understand is that this just works right so the private integer id private uh string name this is going to be using uh jpa because i make terrible life choices so here we go and we're going to create a repository to manage that data customer integer alrighty let's write some data to the database as usual create a little schema and some data okay so there's some people that are going to write to the database so now if we build a little rest endpoint okay i just want to inject that into a constructor that i'll synthesize here using lumbar which is a compile-time annotation processor all right so there's our basic rest api let's just run it locally to make sure that that works as we expect oh we need to put the add entity annotation and restart oh another thing i want to do is i'm going to disable hibernates ddl right so false auto okay just in case let's try this out now so localhost customers et voila there's our data it's worked just fine the application starts in 1.7 seconds keep in mind i'm screencasting now i want to build this into a gravium native image [Music] well that was an adventure but it's built let's check it out and see what happens we we've got a docker image here right this docker image was built through something called a build pack so when we generate a brand new project uh on the spring initializer it gives us a configuration that sets up the uh the spring boot maven plug-in which in turn sets up the native image build pack builder the core conceit of build packs is to contain riser applications no i didn't say we have to use yaml or docker files don't freak out that's not what i'm all about i don't like those things they waste time they're bureaucratic and they're needless the whole idea is that there are only so many different ways that we have to containerize a given application so why reinvent that that's what build packs help you do it's a cloud native computing foundation uh project it's a technology that you can use if you want to go to buildpacks.io to find the specification and there's an implementation called paquetto now pacquiao is part of the cloudfoundry foundation and it has builders for different languages so given an artifact it'll turn into a container build packs originally the work of heroku the pivotal cloud foundry team uh uh working with heroku eventually embraced that that work and eventually worked together they collaborated on something that would become cloud native build packs right the thing that we see today so the earlier versions are similar but not exactly the same it's now a cloud native computing foundation uh project that you can use you can use to easily turn your application from something trivial into something that's ready for production all right so we've got that image let's run it i want to run it on port 8080. so the application's spun up uh it's available it's started up in 163 milliseconds uh oops actually just refresh this page there you are all right we looked at traditional imperative i o let's look at a simple reactive application we're going to bring in spring native we're going to bring in h2 we'll bring in the reactive web support we'll bring in r2dbc we use kotlin and we'll hit generate we can use kotlin because why not alrighty we've got our basic class public static void main let's build a entity using a data class this is kind of like a java record except it's you know a decade ago uh we'll have a annotation to mark it as a primary key or create a repository to handle the tedious soul and eyewittingly boring read write update delete data management life cycle and then finally we're going to create a wrist controller in here rather than doing that the old fashioned way with the rest controller i'm just going to create a functional react reactive endpoint here except i'm actually not going to use even the functional reactive stuff i'm going to use the functional reactive stuff within kotlin's co-routine uh library so i'll say code router create an endpoint here server response dot okay dot body value and weight body and weight uh we'll say ref customer repository dot find all dot 2 or as flow rather alrighty so there's our co routine router let's run this uh we need some data before we can do that though so we'll say data.sql insert into customer name values josh andy sebastian uh older okay so there's some names now i need to create the table schema now we'll start the application okay it's up and running 1.3 seconds already markedly faster than traditional imperative i o even on just the jvm now we're going to turn it into a native image with gravim so let's use the build pack based build again we'll say maven clean package spring boot build image [Music] all right our application is built it's ready to use let's run it as before so we'll say docker run all right and the application starts up fairly quickly in 83 milliseconds so less than half or roughly half of what the previous application started up with the same functionality let's take a look at what's happening when we compile our applications using spring native so let's start again at the spring initializer we'll create a new application here and we're just going to call this the aot project we're going to download that and then open it up in our ide all right so as before we'll add a simple runner and we're going to compile it as we reviewed grav vm has two different types of compilation it's going to take our java source code and turn it into bytecode then it's going to turn that bytecode into native images and we talked about how when we pass everything into that aot compiler the native image compiler we can provide configuration well we can actually preview that configuration without actually running the compilation and the reason is because with spring native we have a new plug-in called the spring aot plug-in that gets configured automatically for you in addition to the build pack support that spring aot plug-in is the thing that actually generates the sort of configuration that will be passed to the pacquiao build pack for the compilation so we can actually preview that without actually doing the work so let's recompile this we can kind of do a dry run of the compilation without actually waiting for the full native image compilation process to finish that way we can see what configuration was generated for us and what's being fed into the gravim native image compilation process okay the process is completed and drastically quicker than it would have if we had to wait for the full process so if we go to the target directory now we can see that we've got uh in the classes directory a meta-imp folder where we find another folder called native image there you'll find uh you know hints files and properties that we expect to be passed to the command line we also find that there have been some generated sources that have been created for us and i think this is one of the most interesting parts of the aot framework that we've set up here spring native does transpilation for certain things so you may know if you've ever looked into the internals of spring boot that there's a text file called spring dot factories and spring.factories uh is a service loader basically it's a list of things that spring will start up when the application starts up and you can see that lots of different jars have them the most prominent of them i think is probably the one inside of spring boot auto configure and there you can see it says oral spring framework boot auto configure enable auto configuration and there you've got all these different java configuration classes well these java configuration classes themselves depend on other things being present on the class path so while you may not be able to see it here i've got red ink on my screen i've got red type on my screen saying suggesting that these types are not in the class path that means that this will fail and so there's no point in trying to run this at runtime and so gravim spring native compilation through gravim actually prunes this stuff from the class path before it ever gets to the point where it's going to be evaluated at runtime that makes for an extra boost in performance another thing that we can do is we can evaluate these conditions and we say okay if this type rabbit template.class is on the class path then load this configuration so rabbit auto configuration is one of many different examples of an auto configuration class these are regular java configuration classes that get loaded when the application starts up there are conditions that guard against their activation unless certain types are on the on the class path so for example rapid template that class and channel.class need to be on the class path before spring boot will try and run these well of course in a native image there's no such thing as a runtime class path versus a compile-time class path that is to say once you've compiled it there's no changing the class path it's just it is what it is there's no dlls so to speak um there's no jars there's libraries that you can swap out externally so when you run this this this code will be evaluated at runtime but what's the point we already know at compile time whether this type is going to be there and so spring native proactively prunes this from the call graph from the the object graph before the application's even compiled we go we do that by evaluating all these auto configuration classes and all these conditional and class annotations the other thing that's kind of interesting is that there's a text file the spring nut fractures that i just showed you that gets evaluated at startup time but of course this doesn't represent the final state of the service loader as graveyard will see it because we as we just talked about we remove certain things so spring aot will automatically create a set of java classes that do basically the same thing right you can see that here and this is a java version of the same thing so there's no strings there's no resource loading there's no reflection none of that nothing like that everything in this class by virtue of the fact that it's a java class it's java code it gets compiled much easier for the graphim compiler to process we can take advantage of this mechanism let's go back to our application here and we're going to create a configuration class and we'll have a application runner in here conditional runner and we're just going to print out hello conditionally okay and so this is going to be a java config that we're going to put in a separate package i'm going to load via the service loader mechanism as as sort of auto config auto configuration and we'll take this and put it there and we'll go here and we'll create a meta imp directory create a new file called spring.factories enable auto-configuration equals comm auto-configuration example auto configuration dot demo configuration okay so now if we run this process again you can see hello tea and hello conditionally okay so now let's go to the generated configuration there and we can see that our and we can see our spring factories has our custom auto config demo configuration okay so it's uh it's registered there now let's add a conditional that could not possibly evaluate to true so for example conditional on class and i think we can use let's see what class do we know for sure isn't here jdbc so that type doesn't exist in my class path therefore this shouldn't be included in the resulting build let's run it one more time okay check out the child class one more time and you can see that there's no mention of our demo configuration it was pruned from the output so this is just one example of the power of the aot framework is that we can do translations on the code before they get fed into the grav vm native image compiler you'll see that we're going to do a lot of interesting things with the spring iot framework one interesting possible so now let's create a new project we'll use gradle we're gonna use spring native we're gonna just call this gradle build and so the goal here is to develop an application that uses gradle to create a local binary on my host machine as opposed to a linux binary instead of a linux docker image inside of a container maybe you just want the build maybe you have some other process whereby you get the docker image so we'll hit generate open this up okay there's our build our application let's just open up the code here and we're going to create a public static void main you know i just want something to print hello world as is the custom okay there's our our application we're not going to use anything in particular here the focus is more on how to take our existing build and turn it into a gravium build so we have these dependencies here we need one more we need the gravium plug-in version okay so we've added the gravion build tools native plugin now we need to make sure that we have the right repositories so i'm going to add maven local because i've done a local snapshot build i'm going to add a bunch of other things as well for the published snapshot repositories i'm going to leave basically everything else as if the only thing that remains is to change the settings okay so we'll go to settings.gradle and just as i did before i'm going to add all those repositories alright that should be it that's all i need to get started all right we've got our build let's go ahead and use it we're going to go ahead and run the tests as native tests and then we're going to run the build so there's two different tasks here remember recall that with maven you have one life cycle phase to which only one thing can be mapped if you want to switch what gets mapped to it then you need to have a profile which is basically a set of behavior that only activates when you activate a profile by passing in a command line switch and so in that way you can remap things to the particular phases which makes sense for a maven life cycle sort of workflow but for gradle it's natural just define arbitrary tasks and arbitrary steps and so those can you know they can help differentiate themselves in their naming so instead of using test and build we're going to use native test and native built let's do that so we'll say native test and then native build and this is going to compile the test this is a new feature in uh spring native zero ten zero this is a new feature that allows you to run your tests not on not just in the jvm but as native code compiled using gravim and run in that context as you've no doubt learned thus far compilation instead of growl is not the same as compilation on the jvm there are some substantial differences between those execution contexts and so running tests in that context is critical to being confident that what you've written will work in the production environment so let's go ahead and kick off this build [Music] all right the build is finished running we can see if we scroll up that the application itself has been compiled but also the tests is the actual tests themselves running in 61 milliseconds that's not bad and then here's the actual application the whole compilation etc and we can look at the the actual binaries if we go to the native image folder gradle and then we can see that and here we have the native tests which is the binary we can run again which is kind of cool to run the tests and then we have the gradle build so let's go ahead and run these first the native tests okay they're done 32 milliseconds and then the build itself the actual application 26 milliseconds not bad and so that my friends is how you create a gradle uh based spring native application all right this time we're gonna use maven to do a local build we're gonna call this the maven hyphen build we're gonna bring in spring native and i'm going to hit generate so as as always we'll have a little runner okay now we need to change our build so let's go top to bottom and kind of review some of the changes first things first we're going to set up a plugin management section to configure uh our plugins globally the first section the first plugin that we're going to specify is the springbook maven plugin then we're going to specify the growl gravim maven you know the gravion build tools native maven plugin uh and then finally we want to set up a profile that will run uh when when we want to do a native build so normally you would have uh one phase you know for building and for packaging and for testing and for things like that and you can assign one thing to it the only way to bifurcate that to to partition it is to introduce a profile so that you can remap certain things to certain phases so here we're going to create a native profile now interestingly this jaina platform native dependency comes from the gravion build tools module but it'll eventually be in janet itself so it won't be it won't be needed as of janup 5.8 okay so we have now our profiles we've got our profile we've got the write repositories on the class path let's now recompile the application this time using the profile so minus p to activate a profile and then the profile is called native then we're gonna do package and this is gonna run both our tests in native code and our application in native code all right the application application is built let's see what it gave us so if we go to the target directory we've got our maven build good 27 milliseconds and our native tests all right so with that we've managed to configure our build to compile an application natively the application itself is very small 41 megs that includes a gre a web server embedded database everything right that's the entire application you can put that in a container and deploy it and that's everything that's required to run that now granted this is a macintosh binary right so if i go here unix executable file that won't run on just any operating system though right you can try uh but it won't right it's a architecture-specific binary but you know that we're going to look at some of the ways that we can extend spring native beyond the defaults that are provided out of the box now that we understand some of the basics of how it works let's look one step further how you can integrate it into your code if you should ever need to or at the very least so you can understand what spring native is doing let's build a new application we're going to call this application hints and we're going to just bring in the spring native dependency here we don't need the reactive web we'll hit generate recall what i said earlier earlier when you compile an application with spring native it creates the configuration that gets passed to the graviem compiler through the spring aot framework so we can contribute custom hints custom bits of information that should be included in that resulting build one way to do this beyond using some of the standard supported json files for gravium itself or the command line switches is to provide annotations is to use the spring native annotations for example suppose i wanted to provide a command line switch to my to my native build i could say native hint options enable https right and so this will actually get passed to the uh the generated options uh to the graphim compilers let's see how that looks if we can inspect the generated target directory we can see that in the classes in the meta infra directory in the native image folder there's a native image properties line file and that file contains enable https if we remove this and then rebuild we'll see that that line is now gone right so these this this annotation contributes directly to our ability to compile our application with graviem the native hint is the centerpiece of all the annotations that are provided by spring native native hint in turn supports a number of other interesting things like a trigger and this is a thing that you can specify it's a class that you compiled your code against that you're linked against but that may or may not be available on the class path at runtime you probably won't have this use case indeed for most of the hints that we're going to look at you probably won't need to do this this is why i would think of this stuff as more advanced it should be the case that you don't need this most of the time and if we've done our job right then you won't need this stuff most the time but if you do need it it's nice to know you've got it so this particular trigger activates when when a type that you want to link against is on the class pad so let's say jdbc i'm using a this one right the trigger activates when this class is on the class path or as happens to be any case here when this auto configuration is you know perceived to be true at compile time right so here we would test for the presence of data source and jdbc template dot class if these types are if this condition is true then this trigger will be true right it doesn't the uh gravium compile time analysis can't determine all of the conditions that springboot is aware of but certainly the presence or lack of a presence of a class we can detect and act on and for any other class that's not an auto configuration the mere presence of it which will result in this trigger being true and if that's true then all the annotations all the attributes that you specify here are also applied and there's a lot of other things that you can specify here now you could specify them in this annotation right you could say okay i want all my serializables to be in this attribute right etc all these attributes all the serializables would be in this attribute and so on and so you can have these things activated based only on this condition and and that's fine right these things can also be provided uh on top of the class and it can be any configuration class any native configuration class and of course your your springboot application so we're going to use some of these hints directly we're going to take a look at some of these use cases that weren't their use remember that there are several things that growl vm does not like things that will cause it to to compile applications that we cannot use at runtime things like sterilization things like jdk proxies and aot proxies things like uh reflective type access things like jni etc so these things are things that you need to care about if you want to have your application compile successfully so let's take a look at some of these use cases the first thing that we're going to look at is loading a resource uh when i say a resource i mean something like you know application.properties or internationalization bundle or something like that that lives in the jar that isn't a class file it turns out this is pretty hard to demonstrate because spring native does a pretty good job uh in in most cases so if you have something in resources and you've got a file in there called you know application.properties or if you've got an image or whatever all of that gets baked into the native heap and so it'll load correctly no matter what right you don't have to provide a custom hint um it only gets useful if you have stuff outside right right noise this is useful as a if you have a custom resource located in the resources directory of some other jar right or if it's a at the root of some other jar so let's go to our build here and i'm going to add a dependency that i created earlier it's called library with resources and it's just a library that i have on the class path in my local machine i just i went to start the spring i o created a new project and all this project has when you open it up is a folder at the root called data and another one called airlinesafety.csv this airline safety i think came from an online data set it's it doesn't really matter right it's just a it's just a file in another folder and so if i wanted to read that well then i'd get into some trouble right so let's let's look at that as an example okay so we have this library now on the class path uh let's read it so we're going to create a runner to read the data application runner resource runner and we're going to read the data as a class path resource so class path resource equals new class path resource and we're going to then read it into a by the way did you know you can have this injected so class path data resource csv and i'm going to say try csv dot get input stream var n equals okay so we're going to read that data i'm going to say new file input stream reader new input stream reader contents dot split there are you know lines dot length lines okay so let's run this and clearly on the jvm this should work okay there are 57 lines and if we go down here 57 line so it checks out let's now turn to the native image compilation maven clean package spring boot build image and you can see it complains because it says the file data ln safety.csv cannot be open because it does not exist all right now we can fix that with a very easy resource hint and it asks for the pattern and we're going to just give it the actual path you know and recompile with that in place [Music] okay the build is finished let's try running it and you can see that that time we got 57 lines so it's worked the next hint we're going to look at is the serialization hint here we're going to create a simple example that serializes some objects to do their bytes and then writes them out why would you want to do this i can't even i can't even imagine i don't know why uh i wouldn't do it i haven't done this in years actually the only time i've recently encountered this uh was with quartz i i helped write the quartz uh support for spring native and one of the things it does is it actually persists it does you know serialization javascriptization um so it's a thing it's a thing people will do i hopefully you don't encounter it uh you won't you won't encounter at least you shouldn't with quartz because we already built that support for you but um if for whatever reason you have a thing that's doing sterilization then you're going to care about this hint so let's create a simple example here application runner serialization uh runner turn new application runner and we'll say output equals and we're going to write this we're going to write out the bytes right we're going to say user.home output okay and this is going to be a file so there it is resource output directory okay i'm going to say output directory dot get file and here we'll say try var out equals new object output stream and new file output stream output okay so we're going to create an object to write out to the file and so here i'm going to create a custom type just so we have something to serialize okay so we'll say class customer implement serializable and we we wanted to have getters and centers and all that so i'll bring in lombok okay i'm going to give it a integer id a private string name okay so we've now got our basic serializable object let's try this out so we'll say var customer to write or written there you go new customer one jane okay so i'm going to say out dot write object written and that's it that's the that's the entirety of the the write logic uh we're gonna then read it just to prove that it works end to end right so bar n equals new object input stream uh new file input stream output and here we want to read the same data i want to read uh red equals in dot read object there we are customer and want to say system out red red wrote written okay good so there's our our basic serialization silly example though it is let's try it out okay so now if we go to the console cat output you can see i've got this customer here jane string etc okay so now you know it's working i'm able to write the data and read the data let's now compile this using grav vm [Music] okay so we've built the application let's run it and clearly it's failing because we can't serialize uh this particular type right and it's even kindly given us the hint that we should add something to the serialization configuration which we're going to do in a roundabout way using the serialization hint once added we can recompile [Music] the application is built let's run it now and there we go you can see it's worked all right in this section we're going to look at a different hint this one's supporting reflective type access now again you're probably not going to use this you're probably not going to need to know about it but if for whatever reason you want to do reflective type access against a type without ever having an instance of that type or ever importing that type in your code and again the use cases for which that is a scenario are vanishingly small and the use cases for which that's a scenario for what there isn't already out something out there that can do it for you like spring uh again almost nothing but should you have that use case or you want to integrate a third-party library that does some such things then it'll be very useful to know about this particular hint so called a type hint so we're going to create a type here simple uh customer service and we'll have a method called find by id that'll take an integer and you know i'm not going to talk to a database here just something very simple math.random.5 john john okay so there's my simple class let's create a reflective runner and the goal here is just to get an instance of the class but of course that's actually a lot harder than you would think because what i need is to not import that type right what i want to do is i want to demonstrate that uh this won't work but the thing is it might work actually right the graveyard is really smart i could be wrong i could be mistaken but i'm pretty sure that when i um pass in a string class name into a method like class.forename i get you know like if i just pass in the string literal just directly but i'm pretty sure that actually works graphem is really smart it understands that that string couldn't possibly be anything besides a type sim you know a type path a fully qualified name for a type so so typically in order to make this demo work i obviously skate a little bit i introduce string concatenation and that kind of thing you know maybe even an intermediate variable like you know period or whatever uh and again i could be wrong i i confess i haven't given this the rigorous scientific examination it deserves um but again it who cares right you shouldn't have to break gravim that's not the goal the fact is that it'll work a lot for you out of the box um but sometimes it won't and so we use it and you can use it as well so let's take a look at how to use it we've got a class now let's create an instance of that class get uh declared or get constructors let's get all the constructors get the first one create a new instance and that doesn't require anything in the constructor and then i want to get a method method equals uh class dot get declared method find by id into dot class okay oops int dot class and then finally uh we're going to invoke that method on the instance so instance i'm going to say let's say eight okay of our result i'm going to print out the results so result let's go ahead and run this and see what we get oh index out of bound i bet that's this constructor's business declared constructors oh yeah that's what i needed to do it's not public okay so that's worked clearly i've gotten the result id a name is john okay good so now i want to see if that works in gravium so we're going to go back to our command line and recompile [Music] all right our application is run compiled let's go ahead and run it and you can see it failed me couldn't manage the reflective axis for that type it even has a nice error analyzer that gets printed out native reflection configuration 4 is missing and it suggests that we might contribute that configuration using one of the you know supported mechanisms which we're going to do so thanks for the hint what we want to do is add a type hint okay and the type hint has a lot of fields here you can be very specific you can say i want to give it a class literal you know if you have a class path that's on a type on the classpad that may not be there at runtime or you can give a type name right which is what we might do here so in start simple customer service you can also be very granular you can say okay i want access access bits dot all right you can see there's lots of options for particular fields full reflection you know whatever just to be safe i'm choosing all for this type you can also be even more granular you can say that you want for this field right this field expects a field hint and that could be field hand and you know allow unsafe access is true allow right is true name equals foo and that would be a field or name or something you know but obviously we don't have a particular field that we care to open up it's just we want to call methods and stuff so we don't care about that but we have lots of granularity lots of support there and remember this can be used in conjunction with other things so with that in place let's go ahead and recompile the application [Music] all right our application is finished compiling let's go ahead and run it and there we go it works as we expect so one little hint brings back reflection great the next two hints that we're going to look at are related the first is jdk proxies now the jdk has a mechanism whereby you can create a type that looks like a type basically it's a wrapper or a thing that has a facade and you might you've seen this before you've seen lots of great uses of proxies before it's a design pattern right from the gang of four book it's a design pattern that is designed to give the client of a type a face a facade that i can use to interact with something that may not actually be a live object it might be something else so you could create a proxy that that when you invoke methods on it it actually translates that into web service calls or r socket calls or whatever so if you've ever used netflix's fane in spring cloud or if you've ever used a retro socket with our socket then you know what these things are right so a facade is a very useful design pattern used all over the place proxies are very useful as well and the jdk even has a mechanism that you can use to create your own interface-based proxies so let's take a look at that so we're going to create a interface based proxy and we're going to create a invocation handler and the invocation handles like an event handler to call back for method invocations against our interface and so we're going to need to then have an interface that we want to use so let's create a order service interface okay we can say void cancel order okay so that's our interface and we want an object that influences the interface but does something that we can control so we'll say once that's done let's create a a proxy for it but we need to have the class name and again you know the graphimo compiler is pretty smart so i need to do a little bit to do something so that it actually breaks right because otherwise it might actually work which is not what we want in this case so order service okay and we're gonna good var equals class dot for name class name um and now we want to be able to create a proxy out of that so proxy equals proxy dot new proxy instance class loader new class class and of course here's the invocation handler okay so that's our proxy object that if we wanted to we could cast to the interface to the order in order service interface let's uh let's actually use that so we can go we can go through the interfaces for vi bar i in proxy dot get class get interfaces i guess you know let's print out interface found i dot get name okay and we can also call methods on it so we can say var method equals class dot find sorry get declared method cancel order and we say method.invoke on the proxy instance and we should see something happen now of course what happens is up to what we program here in the invocation handler and so we want to cancel we want to handle that one particular case that very special case of when somebody invokes that method then system out you want order args oh you know what we need a parameter for this to be meaningful let's actually add an int order id okay so here now it's into the class and now you want to say you want order number args cancelled too bad let me just return null okay otherwise we just forward it on to the underlying thing which is you know it's not gonna work there's no there's nothing there but we don't have any answer for that you know but you can see how this would work you get access to the name and you can provide the response so you can in theory you can just forward the invocation for everything and then wrap it with some cross-cutting kind of concern okay okay so that's our uh our jdk proxy let's see if that works so what we've done is we've dynamically looked up the uh the name of a type an interface on the um in the class path then we've created an invocation handler that listens for a particular method on that interface we've gotten the current system class loader and we've used the jdk type called proxy to create a new proxy instance then we're just sort of using that proxy reflectively we're invoking methods on it that we assume will be there because it implements order service and we're visiting the interfaces that are on that proxy instance let's see what happens wrong number of arguments oh yeah right i'm going to pass under an argument here so let's say 29 okay it says you want order to number 29 cancel too bad now let's compile this as a graph vm application [Music] all right the application is compiled now let's run it and you can see that native reflective configuration is missing and of course if we had that we still wouldn't be able to proxy it so in order to fix that we'll add a proxy hint for the jdk a jdk proxy hit so we'll say at jdk proxy hint and you know you can just put the type as a string as always now we've got our proxy hint we also need to add a type in so we can reflectively access the resulting type so same as before all right so these two actually these two could go together right you can actually say that we have types and then we have proxies voila so with that done let's go ahead and compile the code [Music] okay the code is compiled let's go ahead and run it there you go so now we've got a interface based proxy working in graviem there is another kind of proxy however and it's not natively supported by graviem because it's not something that's natively supported by the jdk and that is to say we have a class-based proxy this is a thing that changes from one framework to another but the basic idea is instead of having the jdk use proxy. new proxy instance to proxy the the an interface what if we could proxy a class a concrete already existing class you know so that we could decorate it with some some extra functionality right maybe each in method invocation gets logged or a transaction gets started and then closed and they finished the method or maybe um you each method results in a network call or whatever you know these these are the kinds of things you want to do when you have um a framework right and so it's useful not to not just to have a facade or a proxy for an interface but also for concrete classes such as when we apply aspect-oriented programming to concrete classes and this is a spring specific thing the the approach is based on the idea that we're going to sub class dynamically a type at runtime so we basically create a new class by sub-costing an existing one and then in that subclass we dynamically write the code that introduces the cross-cutting concern before and after the invocation of the of the parent class right so it's like you're calling super but before you call super you do something else and then and then you know you finish it right so you can actually decorate the invocation you can actually intercept uh the invocation get the arguments process it etc so this class-based proxy is supported through things like c delay or by buddy or whatever and the point is we were looking for a way to support that there is no json file that supports class-based proxies in grav-vm but we do have a hint that you can use so let's take a look at that we're going to create a new runner here called aot proxyrunner and we're going to create a concrete proxy right so here we have a class called concrete order service so this is actually a class with um types you know methods and so on um we'll have public void cancel order into order id canceling order all right so there's our concrete type and let's go ahead and set it this line up so same as before class name equals all right so there's our class name class and then we'll create a class instance and then we want to actually create a we want to create an aot proxy so we're going to need a target and the target is the concrete instance that you want to decorate right so the fundamental business logic is there but we want to create a class that subclasses this that wraps that instance so um we're going to use the spring framework proxy factory bead the proxy factory bean allows you to introduce advice and we're going to get back to this in a second but it looks very similar to our invocation handler uh for jdk proxies we're going to introduce the target which is this instance here and we're going to say we want to proxy the concrete class yes please all right um and then finally we can actually get an instance so get object var proxy and we'll get a method equals class dot get declared method cancel order and we're going to call method.invoke on the the proxy instance right and pass in you know 42. okay so we should see method invocation delegating to the underlying thing that's what i'm doing here as i'm saying go ahead and forward the invocation to the underlying target but before i do that or rather as i do that i want to i want to decorate the result okay so we're gonna have a little bit of logic here we're gonna say okay when each method gets called we're gonna print out equal signs one two three four five six okay after this has happened we want to print out minus signs so one two three four five six okay so there we go and um all we're doing is we're deleting to the underlying thing but we're decorating the invocation right so now we can say we turn result and of course with a void method there is a result so this would just be no but that's okay that's part of the contract it's just return okay so there's our advice our invocation handler if you will but it's basically going to delegate to this underlying concrete type very convenient huh so now we need to run it let's just see if it works okay see i called the i called cancel order on the underlying thing and it printed out uh the it wrapped the method invocation with these framing lines it's a silly example but but you get the picture right we're calling methods on the object the object doesn't know about this thing and now we're able to introduce it this is the benefit of a concrete class of a proxy like this and we use it all the time in spring so for example if you do pre-advise or pre-authorize and things like that on on web endpoints you know rest controllers that kind of thing with spring security that uses class-based proxies right when we do java configuration we use class-based proxies aop just if you want to write an aspect and you're not using low-level stuff like the proxy factory bean with that with uh or jdk proxies that will use this kind of class-based proxy so this is a useful thing to have but it's one thing for which there is no like i said there's no equivalent json configuration for uh gravium itself we support this as a hint so in this one regard it's asymmetrical let's go ahead and compile this application as a graphim native image and see what happens okay the application is built let's run it and you can see it blows up catastrophically but it is nice enough to tell us what we need to do class proxy missing at runtime hint required at build time alt proxy hint so let's just go ahead and plug that right in and you can even tell you can see that there's some nuance to the kind of proxy that it is until we support different things here but uh you know it's fine you can determine what which works best for you it's a whole discussion but we're going to leave that there we might because we're doing reflective access we might also want to have a type hint for this just as before okay so there's this it's called concrete order service isn't it let's go ahead and rerun it's compiled let's go ahead and run it and there we are it worked we got our proxy our class-based proxy our aot proxy working out of the box thus far everything that we've seen is using static fixed annotations we're going to go ahead and introduce the native configuration api which is a dynamic programming model that you can use to write programmatically your hints for your spring native applications you can then package that up and distribute it in your organization and it can get involved into any of the code you want in the same sort of automatic way that springboot's auto configuration works through the smart use of a service loader so let's go ahead and create a new spring native application i'm going to call this external or reusable hints so reusable hints i'm going to add in the native support and that's it we're going to hit generate we'll open this up okay it's open we're going to add one more library to this and we need the spring aot module as well not just spring native and now we're going to build our own hints library but it's a regular jar not a spring boot so called fat jar so we need to disable all of this actually don't need any of it really so let's go ahead and get rid of all this i'm going to create a hint that programmatically registers the uh order service proxy hints okay extends native configuration and this type doesn't exist because i have neglected to refresh my maven build let's reload voila it implements native configuration which has this method called compute hints and in that you can return a list of hints hint declarations so i'm going to return list dot of hd i'm going to create hd to be a new hint declaration and there we will add the dynamic behavior we want now what i'm going to do is i'm going to create a hint for the order service class name on the example.hints.org service and when i say hd dot add dependent type um and we want to you know the order service class name and want to say that this has the new access descriptor of access bits.all we want to add the proxy descriptor new jdk proxy descriptor and here's the list of types so list of order service class name uh and then finally let's just print it out adding or you know registering reflective and proxy hints for order service.class name okay so is this doing anything special no we've already done this with the annotation but the point is you can do a lot of different things here right you have access to this type system you can say resolve this type called you know whatever this we can keep this up here and you can actually use that you can order type this is kind of like the reflection api in java except it's at gravium compile time so there is no runtime reflection in this case but it's a meta meta model that lets you kind of check things like you know which packages does it belong to find annotations you know give you the methods that kind of thing on a given type and you can then inspect it and this is great if you want to do really dynamic frameworky type things for example you know that whatever framework you're using looks for all these types based on this particular combination of things maybe an annotation maybe an interface whatever and you can do the same trick here you can find all those types and then treat them in a certain way you know give them the the right hints so that they do the right thing at runtime so this is very convenient let's uh package this up for reuse now we need to create a new directory in the resources directory called metamph services and in there we're going to put a file named for the kind of service loader that we want to use so here it's org dot spring framework dot native x dot type dot native configuration and so this of course is our native configuration class i'll put that in there all right so there's our configuration and with that done we can now package this up we can compile it maven clean install remember this isn't going to be a native image this is just a regular jar but it needs to be on the clasp path for the gravium compilation uh we have no tests we should but we don't okay so now we can return to our hints project and i'm gonna add our gravium native uh dependency there so we we call this reusable hints and we can go to the aot plugin here we are dependencies okay now let's rebuild this after we've commented out uh the order service hints that we explicitly added earlier so we're gonna get rid of this one and we're gonna get rid of actually that's it we're gonna get rid of that right that's all uh okay let's restore this let's rebuild it even [Music] okay the application's compiled let's go ahead and run it and it worked right same thing except that uh you'll notice that we ran the application and we compiled it you can see it says generator class 4. it's uh when we compile that we can see our custom hints in play registering reflective and proxy hints for com example hints order service so it invoked that compile time to decide how to register these hints dynamically and those get fed into the application and everything worked just as though it registered the annotations directly this is nice because now you have a way for your modules for your libraries for even if you have an organizational standard and it's some third-party thing and it hasn't been already uh supported in spring native now you can do the same thing you can support it yourself in a way that's consistent with what people are expecting right it just works you don't have to do anything so hopefully that'll get you to where you need to go there's a lot of other spis in the spring native ecosystem that you can use above and beyond native configuration i just find this one to be the most generic and useful and with that my friends we've come to the end of our journey we've looked at most of the hints but not all of them and there's certainly a lot of other possibilities here but hopefully you've got a feel for how you can help spring native take your applications uh to gravel vm and help them and to help make that process easier not just for your own applications but through the use of dynamic uh native configuration and other types that we didn't get a chance to look at like component processors uh to make it easier for everybody else to also take their applications to growl vm obviously this has just been a superficial sort of what you need to know to understand uh spring native kind of look there's a lot more that could be said and i suspect will be said hopefully you got something out of this thanks so much for watching as always and we'll talk to you next time
Info
Channel: SpringDeveloper
Views: 16,184
Rating: 4.9802957 out of 5
Keywords: Web Development (Interest), spring, pivotal, Web Application (Industry) Web Application Framework (Software Genre), Java (Programming Language), Spring Framework, Software Developer (Project Role), Java (Software), Weblogic, IBM WebSphere Application Server (Software), IBM WebSphere (Software), WildFly (Software), JBoss (Venture Funded Company), cloud foundry, spring boot, spring cloud
Id: JsUAGJqdvaA
Channel Id: undefined
Length: 85min 3sec (5103 seconds)
Published: Mon Jun 14 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.