Spring Tips: the road to Spring Boot 3: ahead-of-time compilation and GraalVM

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
foreign fans welcome to another installment of spring tips you know spring boot 3 is just around the corner uh not not too far from now in fact it'll be here before the end of the year 2022 and it is packed to the gills with amazing New Opportunities Chief among them is the aot support this new stuff is new and it may very well change but I wanted to talk to you about it now while we are ramping up to the big day the big release some stuff doesn't quite work yet uh and I'm not recording this video as though I won't ever have to re-record or change parts of it I know I will you should know that you might have to as well that said there's a lot of stuff that's already pretty firm and pretty uh solid at this point and it's worth taking a look because there's some big implications for this new introduction into the spring universe obviously we need to have some code right this wouldn't be a spring tips video without code so let's get right into that first and foremost all right let's get right into it we're going to start that spring Lao and we're going to build a brand new application using 3.0 M5 this is spring boot 3.0 this is the latest Milestone just released as of this recording obviously so feel free to use anything later than this uh when you do your or run through this I expect that this will have a long tail uh so feel free to do that we're just going to call this example project aot we're going to use Java 17 for a number of reasons not least of which is that that coincides to the latest stable version of grav VM as well Java 17 is the Baseline required version for spring framework 6 and spring boot 3. it may seem uh quite an aggressive move right now but consider that by the time spring boot through that 3.0 is GA will already have Java 19. Java 17 will already have been more than a year old by this point and the latest version of java could be you know given another five-year window as we had between spring framework 5 and spring framework six it could be another five years before spring frame X7 is released and if we get a new if we get two new versions of spring uh Java every single year well then that's 10 more versions We could be well at Java 29 by the time spring framework 7 drops maybe even later who knows so the by that point the idea that you can be using Java 29 and Java 17 uh and still be supported in the same generation of spring is going to seem a little insane but that's the uh kind of kind of a long tail that spring Endeavors to support uh now for our example we're going to use lumbuck we'll use H2 we use tomcat and spring MVC uh and that's it there's nothing special we need to do to enable the ahead of time gravity I'm support that's just baked into spring framework six and spring boot three so we'll hit generate and that'll give us the zip file that we can open up in our IDE a and we're going to need to we're going to need to set up an instance of uh gravim right and you can do that as well you can say you can use tools like SDK man so you can say SDK um uh install Java and then it's um 22.2 R17 GRL I think so let's see if that works yeah it's already installed that's what I've gotten so Java version and you can see it says open jdk 17. remember galvium is basically just a drop-in replacement for open GDK and we have this build and this runtime environment uh specified here specifically again this is the gravian Community Edition all this is open source you can use it there is a gravia ee Enterprise Edition I think and that version's amazing it's got even more features that make gravity even more compelling and uh truly I truly hope that uh Oracle and the people working on gravia make a lot of money so if you have any interest in that please do check it out probably a use case there for you uh you know I'm a big believer in supporting the people that make production work so good stuff all right so we have a brand new spring boot project if you look at the palm.xml and you come at this from the perspective of uh let's say somebody who's used Springwood 2.x and uh it's spring native you will um notice that a few things are missing obviously we have the snapshot and Milestone Maven repositories and that's just for um the fact that we're not using a ga generally available released code but otherwise it's just a stock standard Springwood app look there's no Maven plug-in here for um Native there's no aot plug-in there's no extra dependencies in the class pack we're just back to a stripped down Bare Bones spring boot application and we've got our H2 dependency we've got Tomcat you know I also want spring data jdbc maybe I'll do some basic Orem so we'll add that in as well um so there we go I'm just a regular stock standard springboard application let's just rebuild and we'll get a simple compile going here Maven D skipped tests uh P native clean package all right now before I do that let's build out the application we're going to call this aot application I'll create a new package here I'm going to call this um uh Basics yeah and we'll have a new configuration here Basics configuration it's just a spring boot class spring configuration class we'll register a listener of type application ready event and we're just going to call this Basics application listener okay and um when the application starts up we'll listen for this event application ready event and we're going to inject a pointer to something called a repository so I'll Define that here customer uh Repository extends uh crud repository this will manage entities of type customer whose primary keys of type integer and this in turn implies an entity of type customer integer string name okay so good and I'll need that here so custom Repository good now let's just write some records to the database okay save all stream dot of let's just get some names A B C Dot map name new customer you know null for the ID name uh and then to list okay so I'm just saving it all and then for each one that comes out um system out print line all right okay there we go good there's this there we are okay and um with that you know we're gonna write some data and we'll see it logged out to the command line uh to the console when the application starts up so let's just run this on the JRE okay um it's complaining because we don't have a database table okay so let's go here Source main resources get a file called schema.sql create table customer ID serial primary key name barcar 255 not no I forgot to annotate this with adid are we start there we have it we have our application uh it starts up it's got three records in the database it starts up in 0.885 seconds that's fine but I'm here for aot so let's kick off one of those builds [Music] all right the application is finished compiling if we go to the Target directory we have our so-called fat jar but we also have the aot binary let's go ahead and run this okay now that took a little while longer than it normally does I don't know why but Ctrl C run again there you go so it's almost 20 milliseconds faster that is to say 21 thousandths of a second faster than the number we had before so this is now at 74 thousandths of a second uh to start the application and be running in in serving traffic um that's interesting but of course one of my favorite things to do is to measure resin memory right so uh sorry keep up aot that's the PID all right and I'm going to measure that by using a script this script will print out they are the resident set memory of the binary given the PID so I'll run that with the PID that we just captured and you'll see it's taking about 103 Megs of ram to run on my local machine amazingly as good as these numbers are I'm running on a Mac and I found them to be consistently better on Linux and I and I just I have to wonder if it's just because they put less resources into the Mac or maybe it's because I'm running on uh Apple M1 I just don't know but it I can actually get a Linux binary running in a Docker container on my Mac M1 to run faster than I can with a native Mac binary natively on the Mac I don't understand how that's true don't ask me to explain it I don't know I've just found anecdotally it's very fast on Linux so this is as good as this is and remember we're running this for uh with 100 Megs of ram most applications I think you'll If we're honest by themselves most applications on the JRE take anywhere from 512 Megs of RAM to a gigabyte of RAM and that's a lot right that's it's a lot if you can get it down to 103 with much smaller Ram Footprints this is what a tenth of your typical uh JRE deployment that's an amazing situation to be in if you're being metered uh and are being charged by uh your resource footprint so far we've created a brand new spring boot application we did absolutely nothing to accommodate the fact that we're going to get a gravity image we are using the new spring boot 3.0 and spring frame X6 Baseline which implies a Java 17 Baseline and we're using grauvium and uh we now have the foundation on which we can explore more of Springs aot system is a new level of integration between your code and the compiler now we'll explore this in great detail a bit later but you should understand that it gives you a way to reify some of the dynamic things that you might have otherwise done at runtime into code generated a compile time this has the benefit of reducing things that can work against you at runtime like reflection I wish that I could say that this support is going to dramatically uh change the game for your application when running on the JRE but you know it's fair to middling right it's not going to be a huge thing it might be whatever 10 or something like that right uh in my experience we're talking about not negligible but not huge amounts either I haven't really checked uh you know transactions per second and so on but for startup speed uh basically it's not going to be a huge win there on the JRE I have no doubt that there's an advantage there of course uh certainly in terms of memory footprint and so on uh but I just don't know off the top of my head uh how big that is instead the aot mechanism really comes into its own when it comes to building code tested for uh a native environment like corral VM looked at a lot of different things in the gravian universe in the last couple of years you've been watching me do spring tips videos introducing the latest and greatest uh in the aot world for at least two years now the bulk of that history has been as we introduced our r d skunksworks project called Spring native which we started building a year or so uh before I started talking about it in early 2020. we've looked at the ever-changing story with galvian and uh and uh spring over the last two years a number of different times the bulk of those videos has been uh in me introducing spring native our r d skunksworks project which we started building a year or more before uh we first started talking about it out loud in early 2020. we've looked a lot at the ever-changing story of gravam and spring over the last few years and the bulk of that history has been in terms of our uh r d project spring native which we started building a year or more before we first started talking about it publicly in early 2020. we created spring native as a way to make spring and its vast ecosystems of integration to work well in a gravity and Native image context that work originally existed as a separate project which integrated with spring framework 5 and springboard 2. but that now lives on in the new spring framework 6 and spring boot 3 generations spring framework 6 and spring boot 3 are new generations of these Frameworks this is a big moment in our history here for those of you that have been living under a rock grauvium is an open jdk distribution that was created by Oracle labs and that features a new just-in-time compiler to replace hotspot it also ships within ahead of time compiler that enforces a closed World perspective on your applications in exchange for incredible games and speed this ahead of time compiler was an extra but now for a lot of people growlvium is synonymous with Native images and so it shall be in this video when I talk about grauvium I'm referring to its native image capability the native image capability is amazing but the closed World assumptions it makes can take some getting used to you need to understand how the gravian compilation process works so you've compiled your Java source code to class files it takes your class files and all the class files on the class path and analyze them finding all the types that are explicitly used basically starting from your main method and working its way outward as it goes through all your types it throws away anything it thinks that you are not using that is to say that it deems to be not reachable and and keeps only what it is what it is able to find and what it deems to be reachable but this is a static analysis done at compile time it can't determine it's okay see that in some places subclasses will be generated at runtime it can't see that in some cases there will be reflection done on various uh types it can't know that your types are going to load resources from the jars class path it doesn't know any of this stuff and when you try to run your application it's going to blow chunks here is a non-exhaustive list of some of the things that grauvium needs to know about beforehand in order for it to work things like resource loading serialization jni proxies reflection Etc you provide these hints in terms of configuration files that live in the meta native image directory of your application or any library on the class path these files are Json files they're stringy you'll need to account for all the things that might go wrong in your application obviously spring will furnish these configuration files for everything that it provides for everything that it knows about and as time goes on and increasing many libraries are building in shipping hints for their release jars as well ideally this code should live in and change with the projects themselves but not all projects have the appetite or the resources to make these available or indeed some are just you know fallow they're just they've reached their useful Evolution and nobody's actively working at it right now even though they continue to serve a purpose thankfully there's there's shared Community reachable metadata that you can use to augment the default metadata that you've got out of the box this is pre-written configuration for various libraries it's a collaborative effort to which anybody with pre-written configuration May contribute again the ideal situation here is that the project proper maintains these configuration files but if it won't then it's nice to know that the community could and should consider the maintenance burden if there's no shared configuration and the library that you're using doesn't furnish its own however then it's up to you somebody needs to provide this configuration you need to do this for every place that you intend to use one of these aforementioned features things like serialization and proxies and any place that any Library does so with one of these things also needs to be accounted for as well you could write these configuration files by hand and maintain them as Json files that evolve in your code base but what happens if your if your code changes and you forget to synchronize the Json files spring frame X6 and spring boot 3 are here to help spring framework 6 introduces a component model that can dynamically generate the configuration for you at compile time we'll explore that momentarily let's start at the beginning and the happy path where your code just works without any explicit configuration then we'll dive deeper into the component model and see how it interacts with the compile time phase a factory object contains the complex construction Logic for objects of a certain type you might do this to handle situations for a number of factors May influence the final produced object this is especially useful if you're dealing with more than one possible implementation of a common type and needs some central place in which to decide on the correct configurations Springs Factory beans serve this role letting you feed in the parameters of the objects that you want and getting the result as as you specify a good example of this of course is when configuring a data source a javix.sql.datasource which might connect to any number of databases within a number of different connection pools and have any number of quirks with which to contend for each of these we may need to specify any or all of these factors When we arrive at the final pooled data source that we're going to use in our production application hiding that configuration recipe behind a factory Bean is a great idea let's let's turn now to some fundamental things that you've probably dealt with before just to make sure they still work as we expect first up Factory beans now of course I don't use Factor beans a lot these days but you might and it's just nice to know that they work so Factor beans configuration and we'll create a an interface here avoid speak we'll have a cat implements animal uh and we'll have a dog implements animal uh and again same basic contract speak so um system out yeah okay so there we go two types of implementations and um I'm going to create a Factory Bean that makes it easier for us to create an instance of that right and the reason you do this whatever you create a factory mean in Spring is to hide the recipe for some complex construction right so um if you have an object that has multiple parameters and multiple scenarios that result in different objects being created then you might encode that in this recipe this also used to be very good I've been recording the recipe to create a new object um in the world of XML right so application ready event application for the event okay I'm gonna call this Factory Bean listener turn new application listener and we're going to depend on a bean of type animal okay now what kind of animal well you know I'm going to create a factor being here and have it produce that object for us so uh animal Factor bean and then what are the properties that we'll use in deciding to you know how to vend it right and you know obviously these are completely arbitrary um but uh you get the idea right thanks frisbees good so there's some construct arguments you might have Setters instead it doesn't matter you get the idea and so we'll say true it likes yarn it does not like frisbees okay and so we'll say okay the type is this uh for the object type it's a type of animal and we'll say Okay return this that likes yarn and doesn't like the frisbees then return new cat otherwise new DOT okay as a silly example now obviously this is a toy but you can imagine a more realistic example like for example data sources right um data sources you know there's a data source Factory being that you can use to create a new um data source a connection to a database and this can be driven by any number of things uh for example the driver class name uh whether you have load balancing enabled whether you have SSL enabled whether you're encoding username and password directly in the URL or you want to derive it from some other place all these things come into play before you can get a jobx.sql that data source so you want to hide that logic inside of a inside of a factory let's go ahead and run this code so there you go the application started up and it printed out meow so that clearly works I defined an animal Factory being but I consumed an animal the product of the factory Bean string beans have different Scopes most objects are created but once per application but some are created and new for every new request for every new session for every new thread or indeed every injected use of the beam spring beans are bound to a scope by default that scope is Singleton which means there's only one of them per jvm per lifetime of the application context next up we'll look at Scopes now Scopes are a very old feature so you shouldn't be surprised by their use but let's uh take a look or create a configuration class and we're going to create a bean that let's just say context HTTP controller okay I'm gonna call this at controller at response body and it's going to in turn make available a pointer to a spring Bean called um request context I can't think of a more imaginative useful thing and this will have a scope and this scope will be scope request now of course it could also be scope session right you could do that lots of different options here scope application Etc about this for our demo let's just make it scope request and we'll have a field here equals uu dot two string okay and we're gonna have a getter so when somebody wants to request that state it's created once the application is The Bean Is initialized Right so I'm going to inject a pointer to a request context object for the current request but of course for every request you'll have a different instance uh and then here we'll just say Scopes context okay string uh uuid return this dot context dot get uuid okay now I'm injecting a pin into the Constructor but I don't want to initialize it until we're actually in an HTTP request so let's just uh leave it at that we'll go ahead and restart the application [Music] all right there it is let's go ahead and curl that endpoint now so localhost 8080 forward slash Scopes forward slash context there's one there's two three four five so again for all our appearances I'm just creating one spring Bean and I'm injecting a final reference to it uh and I'm just referencing the same get you you ID method from that injected beam but because it's got a scope it's bound to the current life cycle of the current HTTP request and so this is great for example if you have a session being you could keep your user's application you know a shopping cart or something like that whatever the point is this just works and there's some insane stuff required to make this work there are other scopes for things like threads for sessions for you know um whatever in fact spring has an extensible SPI that you can use so I've implemented a couple Scopes in my life the spring batch has a scope uh spring um you know Scopes are found in external projects as well so for example uh the the flowable workflow engine has a scope as well uh for processes lots of different options here um but the point is it just works what happens when you have more than one implementation of a given type suppose you do have an interface of uh of some important you have different implementations qualifiers work as a way to disambiguate which being you want injected when you'd otherwise get conflicting versions of the beam let's take a look another very uh commonly used feature in the spring component model uh is qualifier so let's just go ahead and create a qualifier example here qualifiers configuration and you know we're just going to create an application that manages uh an app store uh contract okay so let's say we've got interface app store or you know um whatever mobile Marketplace or something like that right um and then interface IOS app store influence uh I guess we could say class implements mobile Marketplace uh class Play Store implements mobile marketplace right so now we've got different beans you know uh doing different things here at service and I want to know what I get so let's create one listen right here for uh application ready someone say iOS and I'm expecting the mobile Marketplace implementation here okay system out uh mobile Marketplace you know that gig class dot two string right that's the generic version and I want a similar thing for Android right so we'll say Android so I've got two beans that both have the same interface normally this would be a problem for spring because it can only inject by type or by name or by some combination thereof um but you can actually uh you can actually disambiguate which one you want by using the qualified annotation so I can add add qualifier and this would be iOS and this would be uh and right and I could actually use those qualified notations here so Android now you can go a step further you can actually create meta annotation so for example um at interface Android tension is uh what is what do we care about we want run time at qualifier Android okay and then iOS or apple or whatever right iOS I don't know whatever I like Apple better okay so there we go there's our two annotations and we can use the so you know as long as the beams themselves have a qualifier and the injection site has the same qualifier they'll match and that's true even if for example we use Apple here but we don't over there right as long as it's as long as they ultimately resolve to the same call for annotation then it works so Android and Apple so when we run this we should see uh the different classes reflected in the output here but let's go ahead and compile this and find out [Music] so there you have it the application is started up and you can see that one callback uh injected the Google Play Store and the other callback injector that the app store right and that just worked and there's no errors and no no problems about there should be at most one being of this type or whatever that said it is also worth noting that spring has a mechanism for you to inject you know more than one being of the same type so Mobile Market Place listener here you can actually inject a map of The Bean names to their implementations right so like this or I could places and and do whatever you like alright so so mobile marketplaces dot for each key being system out key equals in dot get class dot get name okay let's restart this and rebuild there you go so you can see we've got the bean name on the left and the implementation on the right and that also works in the aot native image context let's take a look at Spring boot configuration properties moving slowly up the stack Springs environment object makes it trivial to configure applications with configuration which it sources from any number of property sources these property sources understand things like hash console environment variables it actually called Vault jndi the spring Cloud config server application.yamo Etc right basically they're away their strategy to resolve configuration from an external place and when I say configuration I mean values keys and values strings like that ionite Properties or properties files of your this is all of course well supported in Spring framework 6 and spring boot 3. moving up the stack a little bit spring boot has this mechanism for uh binding property values from the spring environment to uh job objects so let's check that out okay I'm going to create a um you know properties configuration class and we'll Define some properties here so we're going to call these demo properties very boringly um I'm going to say configuration properties prefix equals blue to full and we're going to enable configuration properties for this so we'll say demo properties dot class and add in application listener application ready event properties application listener demo properties properties dot name okay so the name is and of course for this to work we need to bind these properties to the object by providing a value for them so application of properties beautiful dot name equals world right or spring boot 3 aot so beautiful dot name should be bound to that property and it should be available uh so that we can do reference it as a job object here let's go ahead and compile that and see if it works [Music] all right when we ran the application yep it compiled just fine nothing required to make that work spring boot Auto configuration Properties or configuration properties uh work just fine XML XML is an amazing thing our goal is to make it as easy as possible to take an application built on Spring upgraded to the latest Incarnation and make that work in the new and Native spring boot 3 world a long time ago people used XML to wire their spring applications together and springs aot engine supports that too so yes XML works too let's check out a simple XML example I went up the stack a little bit let's go way back down and look at an old friend XML configuration we'll create a new package here called XML and I'll create a new uh config file that really we're not going to use at all right it's just it's just going to serve to import the XML okay so import uh resource I'm going to call this XML you know app.xml I'll get a new file in there called app.xml beans spring blah blah okay utf-8 and then and then beans or paste those namespaces in because I don't remember how to do this it's been a long time and I'm going to define a bean well what kind of bean you might ask well let's go here and create a logging XML logging application listener that implements application listener application ready event and we're going to need a pointer to something that we're going to create here called a message producer implements supplier okay just as a arbitrary example here and we'll say return hello world okay so these are both beans and we want one to have a reference to the other so message producer producer and we'll say um this Dot producer Dot get right and we're going to print it out the message is message okay now of course this we're going to inject in the Constructor and we need to configure two beans one that has a diff dependency on the other you know we could do Constructor injection but I kind of miss uh Setters you know so private um uh message producer producer nullable right and we'll have a um Setter here and then in our XML we're going to Define this relationship so go here we'll get a bean of type uh XML logging application listener property ref producer ref equals and then we'll need a bean of type um message producer and we'll point it to that so we'll call this a I you know name is going to be message producer message producer is it ID or name I never remember maybe it's like you know lexically scanned I don't know let's see uh back please yeah that looks right right doesn't it kind of looks right yeah it IntelliJ thinks it's okay okay good so we've got this message producer and we've got this Bean but notice that neither of these beans has any annotations on it and they're not explicitly registered here so the only way spring will discover these beams is if it's correctly reading in the spring XML application configuration uh file let's go ahead and try this out okay the message is hello world and what does message producer give us Capital H hello world Capital H hello world thus far we've been taking a tour some of the fundamentals in Spring framework and in Spring boot three uh to kind of remind you that this spring component model is Rich and dynamic and supports all sorts of different use cases and everything has worked flawlessly so far and that's certainly our goal that the vast majority of spring applications have a path to upgrade to Spring boot 3 and then enjoy the benefits of gravity and Native image compilation but sometimes things don't work as expected sometimes they don't work because one of the libraries on the class path or indeed your code itself has done something to run a file they'll form engine use cases where we'll need to furnish configuration files or Json files foreign you know it's important that we look at ways to identify where you're going to need to provide configuration so let's introduce a simple example I'm going to call this migration so we'll create a class here called migration's configuration going to need that and um I'm just imagining a scenario where we have some data in a CSV file let's say okay so if data.csv and in this data I'll just have some names so you know my name hi uh well people awesome people on the spring team um and me right in no particular order it's awesome people and me um uh uh Madura Olga Violetta who else um Spencer nine uh but we 10 um you know whatever right I mean there's a lot of different uh names here uh Dr sire you know some names okay now um I want to read these in so uh that's a CSV file right so I'm going to create an application listener listening for application ready event people listener right new application and of course I'll create a dto for a person string ID string name and what else I'm gonna get the content so try bar I need the contents of the resource you know the data of the file so I'll use spring to give me a class path reference to data.csv as a resource file as a resource object right a spring framework resource um and I'll just inject that I'll say try bar uh n equals new input stream reader uh CSV dot get input stream and I've got that and of course this is a try catch kind of scenario isn't it so add a sneaky throat from lombok so that it basically quiets that it's it's a runtime exception now and with that I'm going to copy the data into a string so CSV data and I'm gonna then I'm going to turn each of the lines uh into each of the you know I'm going to turn the string into Lines by dividing by its lines here by splitting by that and then for each line I'll turn it into columns and then for each row um I'll turn it into a new person all right so a new person row zero Row one and then you know just just so we have an example of reflection let's Now log those out using the object mapper from Jackson right so for each of those people will use object mapper dot write value as string passing in person of course this also requires a um a uh protect exception so I'll do this out here okay and so now I've got this um method decorate it with sneaky throws and we don't need this anymore rename it to you know Json okay um so let's turn it into Json and then for each Json we're just going to print it out okay so all I'm doing is it's pretty pointless I'm reading in the data as CSV and then turning it you know turning right back around and writing it back out uh as as Json data and this is going to require naturally um oh I shouldn't have done that I want my checked exceptions to go away okay so I'm turning that back around into um into Json right so I'm logging it out okay good so let's you can see the problems here right first of all I'm gonna run a foul of gravian because I'm loading data from the class pad uh the resource you know as a resource I'm trying to do the equivalent of class.get input stream um and then also I'm I'm doing reflection right or rather Jackson the object mapper is doing reflection to take this person and turn it into a Json string both of which are uh bad right well they're not bad they're just problematic you need to account for that so what tools do we have at our available you know disposal to kind of figure out what needs to be done well obviously we can guess but let's just take a look at some tools that you might use or in a particular one tool that you might use for larger scale efforts or you've got a lot of code that's untested unknown uh and whose uh configuration you just don't know about yet okay all right so we've got the application code ready to go let's plug in this Java agent palm.xml and then we're going to paste this here this is going to set up an agent lib called the native image agent which has a parameter called the config output directory which has as a value Target native image that is to say in the Target directory under native image it's going to dump out the configuration files that we would then need to make our application work and so this has to you know you need to run every interesting code path for this agent to pick it up and help you out right so a good place to put this kind of thing is in the build pipeline all right let's go ahead and run this so we'll do rmrf Target and we even skip tests clean package spring colon boot run foreign started to run um and it's done everything that we wanted to do so I wanted Ctrl c um and then we can go inspect the target directory and you can see there's the native image folder and you know all the configuration files that we might conceivably need uh now of course this is going to be a lot more than you actually need right because because again spring Boot and spring framework will take care of a lot of this if not most of this for you all we really care about is the stuff that's unique to our code uh and so we care about the person being declared there right calm example aot migrations person uh we care about anything you know with DOT aot migration so obviously this stuff is going to be unique per run right this is all CG lip stuff generated randomly uh by spring so you don't want to write that down you don't want to provide the configuration for this because this is not going to be the same name as you get next time the memory will be different Etc so what we care about is this stuff that we created that that spring won't otherwise know about so it doesn't it's not going to know about this type because we're not returning it from any controllers we're not using it in any spring data repositories uh indeed there's no connection between that and any of our spring means at all and certainly even if there were it may not be the case that those things are requesting uh has fully featured uh permissions for reflection as we require here we want all these things right so there's one type that we care about we also care about the data source the the CSV right that's a resource and um you can see that you know spring has already registered a bunch of other things and uh you can you know you can see if spring does class that forename or if something does cost that for name that's a resource you're loading a file turning into a class right uh so you have to care about that you have the XM oh we loaded the XML earlier so that worked um but what we care about of course is anything that we added uh like data.tsv so there's that so we need to teach our application about those two things uh let's start with the reflection that's the easiest we can just use an annotation register for reflective binding the first from that class okay so that'll actually just for your application for any business level applications whatever this might be enough maybe you have a couple types here and there that just need reflection and that's it everything's fine otherwise okay good just use this annotation uh it's easy as can be and you're done um but for if you want to register any of these other hints and you will need to have more granular control and you need to have more granular control then you can just if you need to have more granular control then you can create a runtime hints registrar so migrations runtime hints registrar implements runtime hint registrar and this is just a callback interface and you get a pointer to the hints type and the current class letter so here I could re I could duplicate what I did for a person that class like this and I can specify what kind of uh axis I want right do I want all the classes the fields Etc there's and you can do more than one of these obviously um I like to just to dot values that gives me everything and that's that's more than likely going to be too much right you probably don't need that but in that first through I like to just get everything so I can I can make sure it works I'd rather have everything and then move on knowing that I've gotten everything I need out of that and I can face the next issue then if I have time when I have time I come back and I'll try and reduce the uh the uh the things I'm asking for here you know the surface area of my request okay so this is duplicative of the register for runtime hints I don't need it but now you know you could do it if you wanted uh what we do need however is to register that resource so here I can register a string or if I have access to the shared resource remember that I used a class path resource actually I used a resource here right this is a add value annotation I have type of resource Well Spring is going to look at the schema of the URL and it's going to create an instance of this specialized instance of this called classpath resource so if I wanted if I had access to that same type that same variable I could just pass that in right and this is effectively the same thing but there's no need in this case to um to uh do that I don't have access to it so I'll just register the pattern instead it's the same thing right it does absolutely the same thing and then finally I have to register this I have to import these runtime hints okay and this is you know you put you put both the register for reflective binding and the import runtime hints uh on configuration classes or Bean provider methods and that's right you actually you could actually have runtime hints that live on this beam definition instead so maybe for this bean and all the things I'm going to do there in I need this runtime hints class but for another one I have a different one right and again if you're building your own organizational Auto configuration again you might use this as well you might have that there for cleanliness of your names of your workspace you know your namespace maybe you make this a um static inner class and that works just fine too right you don't have to make it public and all that stuff either it's just a regular class like like you've always done so now from the opposite perspective your API hasn't changed only the implementation details and that's hidden or it is very clearly not meant to be used from the outside okay so let's go ahead and compile this application uh and uh and use it so so let's go ahead and compile and run this application so we'll do maven P native clean package okay it's built let's run it see what we get ah there we go it worked so we have our Json reflected here on the console uh with very little pain indeed so you can see the Java agent and the surface level component model provided by things like runtime into star and register for binding are great ways to identify and plug the holes in your gravia native image for a lot of developers these two tools the Java agents in the hints may be enough to get the job done and this is hopefully as much as most application developers will need to know things get more interesting however when you start to imagine infrastructure level concerns and we start looking at purpose-built Frameworks built on top of spring boot let's put our uh infrastructure hats on here and take a look at some of the meta model programming that spring has supported since day one and then look at how all this new aot compilation stuff interacts with all of that we looked at XML earlier if you're new to Spring boot then you've probably never dealt with XML configuration format of your more typical of applications built before springboot it's interesting in this context because it demonstrates that spring doesn't care how you define the wiring of its objects you can Define that wiring the relationships the ontology of those objects in XML or using component scanning or using Java configuration or functional configuration if you've ever used spring Foo then you know you could also Define the wiring explicitly did you know that there was a way to define the wiring using ini files like a like a long time ago before spring window that's true right it's because the source of this configuration all gets canonicalized into a meta model spring read some sort of input and then arrives at a meta model containing uh beam definitions these beam definitions contain information about your objects how they relate to each other their dependencies on each other their properties their Constructors Etc at this point you have a bean factory full of beam Nations this is the primordial soup of your application you can register beans of type being Factory post processor or its various subclasses to act on the bean factory replacing beam definitions or contributing new beam definitions as you go as an example the original use case for spring Cloud before we turned it into a microservices framework that we all know and love was as a set of connectors for cloud platforms you would add a appropriate binder to your class path and then deploy the application to your Cloud infrastructure of choice like Cloud friendly or Heroku or whatever and it would see that you've got an local uh you know localhost H2 instance for example as a data source and you would see that in that cloud Foundry context you've got access to a Heroku or a cloud Foundry uh data source and it would dynamically replace the beam definition for your data source with the one in the cloud provider in the in the context there it's also possible to create a whole new beam definitions at this point before spring boots Auto configuration and and the functional configuration in Spring framework 5 working with beam definitions was one of the easiest ways to analyze and hybrid object graphs based on cues from the developer as you might want to do for example with a framework like spring integration or spring security let's look at an example of a bean factory post-processor let's look at bean factory post processors or creditor directory here called pfpp bean factory post processor and we'll create a new class in here called uh the fpp configuration and it's just a configuration class as all of our examples have started off and you know everything we're going to do in this example centers around everything we're going to do in this example centers around a listener okay so an application listener application ready event and this in turn will implement the on application event callback and it'll use a Jackson object mapper okay I'm going to inject that into the Constructor like this and it's going to work with let's say a product okay just to type that we've got and I'm just going to log out some products okay list dot of new product uid.random.2 string good and you know just for each one of those um four of our p product law uh you know system out and I'm going to use our friend the object mapper to write this value as a string and of course this will throw an exception we'll make it a runtime exception okay so I've got this bean and you can imagine what it's gonna I'm just gonna create two objects and reflect over them using the Jackson object mapper to write them out so we need to register this bean factory post processor the you know obviously the most logical way to do is just to create a configuration class and create a bean and and so on or just put at uh at component right like that would be fine too um but there is a way to remix the application context the bean factory early on right and uh and so we're gonna create one of those um we can create a let's say BF you know it's a um you know maybe this maybe we just call this listener and we just call this um bfpp um we're just gonna call it listener bfpp or listener bean factory post processor implements bean factory post processor okay so this is a very early callback and you don't get a fully formed context yet you get a bean factory and you can do things like register um a bean in that bean factory and you can inspect it you can say B Factory give me beam definition blah or you can enumerate all the bean names uh so bf.in definition names and then for that you're able to get a pointer to a beam definition so being definition equals the F get bean definition being names uh Etc maybe that shouldn't be names just singular very good and I can inspect the mean definition and get the mean class resolvable types they depends on the initialization method Etc um I get the beam class name as a string right the class may not even be ready yet right we don't even know that for sure um so all this stuff is kind of like stringy but it's it gives you enough to to do what you need to do at the early startup of your Java application on the JRE um and you can actually you know programmatically register things and so on so I can I'm not going to log out every beam definition but you can do that uh we could also say um I want to get I want to get a bean definition I want to register custom types Singletons Etc but if you want to programmatically add a beam definition there's a subclass of bean factory post-processor called Bean definition registry okay post processor and this Bean registry uh is you get a logical chance here a lot of a logical opportunity uh in this implementation to post-process the bean factory and basically this is downcast to a bean definition registry okay and in our case we don't actually need the bean post processor uh Behavior anymore like we're not doing anything interesting in there we're gonna do our work in here but we do have to implement this because again this implements it extends being Factor post processor so I've just got this empty thing here and what I want to do is I want to programmatically register an instance of our of our type right so this gets changed here all right so so I'm gonna uh register Bean um of type listener what should we uh what B name should we give it well how about we just put that here string bean name equals my bfpp listener okay whatever it's the same uh as long as it's you know relatively unique you could do that just to be sure just make sure you use the same random one per uh per Run Okay um uh but yeah we don't even need that we can just leave it like this whatever and we are going to register being if it doesn't exist so if registry dot contains Bean definition being name right good if it doesn't contain it then registry.register Bean definition pin name and we'll use root definition sorry beam definition Builder dot root beam definition and the type let's just keep things interesting here we'll be a fully qualified string name of the type right so uh as far as the compiler is concerned this class is never used you can even see my IDE is saying it's never used well of course it's used spring is going to start up and it'll create a programmatically it'll create an instance of this being and that will exist uh and in the application context so this will actually all still work spring you know you don't have to do anything to make this work um but we do have these products being used and we're reflecting on those types and so we can infer that if this Bean is present in the application context that therefore we're going to need to register some hints so there's a second interface here um called bean factory initialization so listener Bean Factory uh initialization aot processor okay Bean Factory initialization aot processor and the Callback there is kind of analogous to the bean factory post processor hierarchy it's very early on in the initialization at compile time of the application context so you get a chance here to modify the bean factory um and to uh write hints dynamically based on it okay um so I've got a bean factory there I need to return an instance I've been factoring the initialization aot contribution and there I'm given some pointers to some interesting stuff one thing that's really kind of powerful is code right and we'll talk about that in a second but basically you can do code generation you can generate new classes as a result of your analysis of the mean Factory at compile time you can say remember what I just said you can actually get the bean definition names right at compile time for VAR you know mean definition name Etc you can do all the kind of stuff you just did you can also check to see if something is registered which is interesting to us so here we'll say hey if this contains this mean definition for b name then we want to return this otherwise we turn null so we don't you know we don't care to register or do anything with the uh the aot context unless that bean is present right that's this live this this bean factory initialization ao2 processor uh is only concerned with this particular component obviously you can register as many of these as you want so here if that beam exists then I know that there's going to be trouble at compile time I'm sorry at runtime uh because of the products right these these things down here that we're reflecting on so we need to make sure that we contribute hints and we can do that by getting the um runtime hints here right so you've seen this runtime hits before it gives you a chance to uh call you know register for type for product.class memory category.values there we go so I'm just registering hints but I'm only registering hints if the bean is there in the first place if it's not then you know why bother registering these hints uh they'll just load the runtime keep the native memory of the application okay good now we've got this uh bean factory post processor spring you know this is a you can you can register these a number of ways but uh if you use Bean configuration remember they have to be static okay these get invoked like I said really early on before spring has got live references to actual objects it's just that it's just got these static methods uh and so here you're telling it look I'm going to register this interface it's going to be involved in creating you know sculpting out of the Ooze of our uh of our application context a a really you know a real uh fully formed application right um and so that gets invoked so this way this is very convenient um what about our aot processor well you know it's just a regular spring mean this doesn't require anything special you can just do this if you like right um that works there is another way however and that is to create a folder here and registered in the service letter So Meta INF spring new file aot.factories and here you can put any of the common aot interfaces as entries just like you would for auto configuration and the like so aot dot factories put that there and then our implementation is here okay so this is our listener beam Factory initialization aot processor um and you know there's only one entry obviously if you wanted to you could have comment unlimited list there's um our aot processor and our bean factory post processor you know there's nothing stopping you from putting these in the same component right obviously uh this this one class could Implement both beam definition registry post processor and being Factory initialization aot processor in which case you wouldn't need to register them twice you could just use the static beam definition and that would well that they would both be involved at the right life cycle phases right and that would be fine and that way you can actually share the beam name right I've got the B name in a single common place so that both of these individual components uh can get access to them but um you know maybe you want that parameterized maybe you put that in the Constructor here whatever the point is uh you can register these types as in a number of different ways so let's go ahead and compile this application and run it and uh you know we'll see it's going to create an instance of this it's going to then programmatically reflect over these types and then hopefully that'll work Maven skip tests P native clean package okay let's go uh to the Target directory aot and there we go everything has worked just fine um do we see the products customers there it is two product skus okay so that has worked mercifully right all because we're able to to write these uh Bean definition uh registry post processors in conjunction with often but not necessarily these bean factory initialization aot processors when you compile an aot application with a springboot maven and Gradle plugins a few things happen your Java code is compiled into that class files your dot class files and all the libraries on the class path are then ready to use in the JRE the spring boot build plugins then create a version of a bean factory with all the correct themes and components in place but it but it doesn't start that bean Factor it doesn't start the beams themselves so at this point you have a bean factory full of bean definitions with no beans no actual concrete running beans a bean factory initialization aot processor runs at this point in the life cycle at compile time before the objects are congealed into fully formed and running applications you still have access to the beam definitions but just as with the bean factory post process accessor you should not access or try to eagerly initialize the beans themselves it is here you have an ideal chance to inspect the bean factory and contribute whatever hints that you'd like by looking at the beam definitions in the context remember at this point you don't even have actual objects or beams just being definitions that'll eventually become beans uh it's still enough for you to inspect the types and find out the class and they reflect to metadata about the class to get your job done however maybe you have many objects that will benefit from the same set of hints you can register them here you know and be sure that the hints Will Be contributed only once per bean factory now in truth this gets normalized it gets deduped by gravity of itself so multiple redundant additive uh contributions of the same hints only result in one actual hint which you can use to your advantage actually because you can actually have different things that add to the definition of a single uh a single set of hints remember these beam definitions are what result after all the configuration classes in XML configuration files and component annotations and all the component scanning in the functional configuration has all taken place these channels use a lot of reflection which we want to reduce the aot support in Spring goes to Great Lengths to capture the state of the beans once they're created in that compiled time bean factory and to preserve them to reify them at compile time so that when the application starts up it can skip all that reflection and resolution and configuration and searching and so on it does this with code generation the bean factory initialization aot processor can process beams in whatever way you'd like adding whatever beam definitions you'd like including new classes that you've written out dynamically using the code generation API a ton of what spring does at runtime can happen at compile time by this source code that you generate remember that we saw the bean Factor being assembled with a static method earlier on that was all thanks to this Dynamic code generation capability this approach of capturing the build time State and preserving it for runtime and short circuiting so much of the runtime Recreation of that state with proxies and reflection and all is super useful and as I've said a number of times it has some interesting implications the bean graph is Frozen at compile time this means that nothing new can be contributed at runtime it's fixed now before we continue our tour of Spring's component model let's take a brief look at the library that powers some of the code generation facilities in Spring boot 3. a project from Square called Java poet now let's take a brief look at the library that powers some of the code generation facilities in Spring boot 3. Java poet from Square in the last example we looked at uh the bean factory initialization aot processor and we saw that you can register hints at the very early compile time phase well I also alluded to the possibility that you could do code generation code generation is supported as we talked about through a project called Java poet and before we move on I want to take just a quick beat and explore some of that right so let's create a little test here and you can see some of the possibilities okay so Java poet and we'll create a new Java poet test and it'll just be a regular old test nothing too special about it and what I want to do is um subclass something dynamically and compile time okay and I'm going to use Java poet to write the code to to do that for me basically so we'll say um that we have a method that does a subclass so private uh type spec right and this is notice that we've entered it it's now part of spring framework itself um we uh we we packaged it basically uh Target okay so there's the type one a subclass um and we'll return null for now okay and uh then we'll have a uh Java font so we're going to say okay actually type spec equals subclass uh sorry type spec and we're gonna we're gonna subclass what well how about this one let's create our own customer service and uh or have a record called customer integer ID string name whatever right um and let's add some methods so you know customer save string name return null who cares another one to um I don't know customer delete no get by ID there you go just by ID or not who cares Okay so integer ID so we've got two methods here and we want to create a class little subclass that for us okay so customer service dot class and this type spec I'm gonna have to write it out into a file so Java file equals javafile.builder customer service dot class dot get package name uh and then we want the type spec okay and then want to build it okay what else do we want now um and I guess we just want the code right so uh we'll say a Java file to string and we're just going to print it out okay so code good so that's the basic harness of what we're trying to do let's actually make it work okay the what I'm expecting is is subclass of this that overrides all these methods all right all right um let's do that so for the type spec implementation itself uh we need to you know we need to um start thinking about some practical limitations of java right I mean all languages have things like this one thing uh is well we want to make sure that we're not trying to subclass the final class because obviously that will never work so John Lang uh reflect uh reflect dot uh what is it Matt modifier dot is final right target dot get modifiers okay that class can't be final oh good okay so good moving on uh we also want to get all the methods so methods equals stream Dot of reflection utils get unique declared methods Target um and uh unique declare methods and then we want to filter right we've got the method so filter a method based on certain qualifications like for example General Lang dot reflect dot modifier uh is private so if it's not private then we're okay you know we'll go ahead and try um okay good uh and what else we want to make sure that uh isn't it's not an object method and there's a special method here in the spring framework utilities class so we'll just use that to make short work of that because there's no point in trying to like you know we don't care about overriding two string or something and let you know you can do it if you want but I'm not going to override that there's nothing I can do there usefully in a way uh and then I want to map each method into a method I want to actually generate the subclass for that you know for the method the method spec so I'll say um uh we have a method here private method spec method spec and this will be for a doubling reflect method okay return no for now and we'll say method spec m okay and I think I think that'll do so to list and that gives us our methods and then uh we obviously we have to implement that method this back in a second but let's keep going before we focus on that so we got all the methods now and we now want to generate the new type itself to contain all this method so type spec dot class builder Target dot get a simple name Plus subclass you know whatever we're going to build a dynamic subclass and give it a sort of infrastructury name there so that most people won't have any collisions with it will make it public um I'm going to get all the interfaces I'll say stream of target.get interfaces um for each interface right for each interface I want to add that to the definition so uh new type Dot add super interface I face good okay um and then new type the superclass of course will be the target and you would type I guess I guess that's it then we want to add the methods right so method just add four each um let me type add method so we're going to add the method gets returned for that method to our new type okay and then that I think is it so our new type dot build now we have to actually go through and implement the um the methods themselves the actual method specs Okay so we've got this method now here uh we're going to create a new method VAR U method definition equals method spec dot method Builder with a method.get name adding modifiers for um public adding modifiers you know I guess with anything else returns uh method.get return type and adding annotations okay and The annotation is just to overwrite that class obviously uh okay good so that's the basic definition let's add some parameters right it's a Prem names equals a stream dot of method.get parameters dot map um Ram name equals param get name okay um so new method definition and parameter and P A dot get the type with parameter name okay good and return param name as a string right we get this whole thing as a list so we'll have a list of strings as well good and the actual body of the method we have to write out this is a little bit of string templating or whatever it's not gonna hurt us statement is string.format you know super dot l okay so what have I done I've got um I'm using Java poets uh nice uh template template format so this is actually a string format mechanism but there's actually this these dollar sign L's get uh get processed and honored by Java poet later on okay um so here I'm going to say method dot get um return type Dot equals void.class um if it's void and we say that otherwise we say return so the last line is either going to return something while calling super just call Super and not return anything if it's void okay um so so it's going to say either space space super law or return super block right okay it's just a very simple return statement there and we'll say new method definition dot add statement um return statement and we're going to have the method dot get name and that's I think oh we need the parameter names two rank so spring utils that collection to delimited string param names good okay so I'm adding a statement where I'm calling I'm returning or I'm calling super dot l l is being is the method name here right that's the first thing I'm being uh that's being formatted into this string by the add statement method here so string format template goes in there first daughter cell download sign L which stands for Lex I think it means lexical like it's just a regular it doesn't doesn't get uh and translated you know um you put that in there and uh and then the second part is the parameter name which is this so I'm going to give it a list of the parameter names so basically I'm going to call Super taking in the parameter names that have been given to me and then passing them into the Super version right um and uh and I think that's basically it right on that auto too so new method definition dot build okay all right let's see what this gives us I think we're close cool so there's our our class customer service subclass that extends customer service and you know it's it's a implementation method for implementation method uh version of it it's just calling super for all the default ones but now you can put yourself in between you could do something before do something after you could do all sorts of things if you have the ability to write code at compile time to augment what you've already got there so imagine uh you know grpc or something where you want to like create uh you know converters or whatever maybe any kind of encoding I think would be very useful for this kind of stuff um you know any kind of code that does anything with uh binary binary serialization is going to be useful to that to have this Dynamic uh capability right where you don't have to do this at runtime infrastructure kind of code right stuff that we would normally do with jdk proxies at runtime you could probably get away with doing it here at compile time now don't get me wrong you're not going to have to do this a lot but it is nice to know that you can Java poet was a code generation API that was created by square in 2015. they uh assert rather usefully that so much of the interesting things that square is able to do particularly in their mobile space uh with things like dagger and auto value is thanks to the magic of code generation however that code generation can be a little clumsy and so they put together a program an API a library called javapod that gives you a clean uh you know fluid DSL style way of writing Java code using Java code it also takes care of things like if you reference a type to make sure to automatically add that type as an import at the top hopefully this project uh can you know seems useful to you because it makes it possible for us to very quickly write code that extends or the code or you know works with other types in some interesting way now of course you shouldn't need to but sometimes it'll be useful to know that you're running inside of a gravia native image there are some properties that you can look at let's take a look at that let's look at a very convenient way to detect if you're running inside of a native image okay so we're going to create a new package here called detection in which I'll create a class called detection configuration and you know it's just a regular spring configuration class uh Bean application listener ready event detection listener and you know we're just going to print out the result native detector dot in Native image is native image okay and behind the scenes all this is doing is looking up this property or or graphium native image image code if it's not null then we can reasonably assume we're in an uh in a native image context okay let's just run this application on the JRE before we turn into a native image and we'll see what it gives us it says is native image is false okay good now let's compile it and see what we get [Music] all right the application is compiled and it's run and now it says it is actually in the native image so you can use this this Boolean to tell your code to inform your code that something is happening uh inside a Native image context now you shouldn't do this very often right you shouldn't need to know about that but there are some slight differences obviously particularly as infrastructure is concerned around what it means to run inside of the JRE versus the run in gravitym Native image format so this is a a good thing to keep in your tool belt these properties are available when you're running and a lot of things in Springs aot support work by evaluating at compile time I mention that these properties exist so that you might use them if you need though we should hope that you don't need them after all you don't want to write code that works one way in one environment but differently in another that'll be a Divergence from the principles of the 12-factor manifesto after all which promotes the idea that you should have one binary that gets promoted from one environment to another but we appreciate that the need may arise unfortunately to have code that is unique to your gravian native image version or your JRE version unfortunately a few of the mechanisms that a spring developer might reach for to create environment specific build or behaviors don't work in an aot context consider spring profiles the profile mechanism works by letting you associate a bunch of objects you know zero or more with a given tag which is basically just a string like a label uh and then like activating all the beans in that given tag when you start the application up at runtime the objects only exist at runtime if they're associated with the default unspecified profile or with a profile that's been activated explicitly at runtime this won't work in an aot World remember there are distinct phases and the beam graph is fixed after the compilation so unless the profile is active during compilation the beams specify and specified they're in don't exist at runtime in the native binary and thus can't be activated so just avoid profiles altogether right again you can achieve a lot of what you need to do there by having things like like uh you know bits of indirection abstractions things like that in your code or just using the same properties and overriding them with environment variables uh and so on and avoiding any changes to the code itself the Java objects itself the same constraints hold on for things like at conditional annotations the same constraint the idea that the bean graph is fixed after combination holds for things like add conditional and it's conditions conditions are evaluated during compilation so unless the condition is true when the application is compiled it won't ever be true thereafter for most applications that's fine right and we can say that conditions work in aot since there's no useful distinction and outcomes when evaluating the conditions at compile time versus runtime for most of the conditions that we know and love so think of The Usual Suspects testing whether a class is present or has another being been defined that's as true at compile time as it is at runtime it'll always in fact be true at runtime if it's true at compile time in an aot context right but other things that rely upon ambient state uh that's going to be a little bit more tricky isn't it right um let's suppose you're testing the presence of uh some context in a cloud platform and you've got the at conditional on cloud platform annotation that activates of being exclusively when it's running in a particular Cloud platform like kubernetes or Cloud Foundry or Heroku that's not going to work here right unless you can trick your application by using for example the spring main Cloud platform property uh into thinking it's running on that platform during compilation just bear that in mind ambient state that can only be usefully determined at runtime should not be used for add conditional annotations in your aot world which is why by the way you shouldn't um you know I love dravvium but one thing that can be a real kick in the pants are the wait times I get why it's happening it's not hard to understand gravvium does a lot of work and doing a full analysis of our code and all that stuff on the class path can take time and memory it's no wonder that it takes as much of either as it does and it has already improved dramatically even since I first started using gravity I'm internist less than three years ago it's a marvelous piece of software but those compile times are idiots they interrupt my sense of flow and it's almost a curse that they've improved so much as they have because the compile times are now fast enough that you don't have time to go pour a cup of coffee or eat some yogurt or do a crossword puzzle but they're slow enough that you feel like you're being interrupted that you're being kicked out of the Zone this reminds me of that classic XKCD cartoon where it's explained that the number one program excuse from legitimately slacking off is my code's compiling which I suspect has more to do with uh you know C and C plus and large builds like that Java and go and other modern languages have immersively quick compile times these days in this one sense it feels like grauvium has taken us a step backwards if I kick off a compile my mind starts to wander I've been doing so many of these now that I'm starting to hear elevator music when I do a compilation so I I was thinking I would like it if everybody can hear elevator music and so I asked it doesn't hurt to ask people so I went to the official gravian project and I filed an issue I was excited to see that people responded with support and enthusiasm and one person even you know suggested some elevator music somebody else responded that it might be useful to have beeps too which would be nice as well and then I was very happy to see that uh Fabio nipaus a researcher on the gravian team at Oracle labs and a doctor a PhD along with my favorite other doctors doctors who strange Pollock sire and subramaniam responded with a very promising prototype a dash dash Josh hyphen long hyphen mode that would not only play some elevator music but also print out this ASCII artwork that says music brought to you by Josh long and they would be an ASCII encoding of uh of yours truly he said I regret to inform you that he doesn't think that the pr will be accepted perhaps because of the copyrighted music uh I'm sad I would love to see that PR get accepted but he did promise that they're also working on fixing the uh the core problem here which is the compile time it itself rather than just the symptom which is the weight uh and so that's promising I think we'll get a fix one way or another working on a bean by being basis is very powerful if you want to work with actual live fire beans however you can use Springs Bean post processor this beam this interface puts you in a position to act on to transform actual objects not just beam definitions before they're finally live and handling requests this is great for infrastructure code such as Frameworks where you need to note or retain or Reserve or observe instances and references to instances of objects of a given shape and when I say shape I mean anything really you know objects that have a certain marker interface or objects that have a certain annotation or whatever you whatever you want really and whatever you could decide given reflection I want to implement a proxy for example using Springs proxy Factory which makes it trivial to to use the proxy design pattern a proxy in its most general form is a class functioning as an interface to something else so for example spring uses it to handle things like declarative transaction management auditing security logging concurrency and and so much more there's a great way to decorate an existing object with cross-cutting concerns like starting and committing a transaction before and after a method invocation spring uses these all the time for things like a transactional at scheduled at async and at authorized and countless others there are two types of proxies in the spring world jdk proxies and cglib proxies there are two types of proxies in the spring World Siege lib and jdk proxies jdk proxies are built using the invocation Handler API it makes it trivial to create an object that implements an interface type of your choice and then forwards the actual request to a concrete instance of that interface in this case the word interface used in this description above is quite literally a Java interface type but what if the contract what if the surface area the interface of your class isn't a Java interface but just some sort of API signature that you expect everybody to observe what if it's a Concrete class for which it makes little sense to extract out a separate Java interface jdk proxies require an interface and they were only supported in Spring after 1.1 I can't be sure but I suspect this is one of the reasons why so much of the early literature suggests using interfaces with spring means and why you'd see things like food service and default food service or food service and food service implant or whatever nowadays that's a bit of an anti-pattern and highly discouraged unless you actually plan to have more than one interesting implementation of that interface in this case Food Service that is suppose you're implementing an uh an interface for a Online Marketplace and you might have more than one version of the interface for IOS and Android as we saw earlier in the qualifier demonstration avoid the knee-jerk reaction to create an interface for an object that has the same shape as the object it only complicates things spring supports concrete proxies too using Siege unit which is a third-party Library that's used to dynamically generate bytecode uh and in this case byte code to subclass an existing type at startup time the constraint thus is that the type be subclassable so obviously you have to be aware of things like final and sealed Springs concrete proxies are unique to Spring you're using them every time you use a configuration class they're everywhere naturally creating a dynamic subclass of a given type registering it in the class order and then swapping out your interface for the instance that delegates to yours implies a lot of funny business that we'll need to account for at compile time in the aot context mercifully spring can do a lot of this for you so let's take a look at an example let's look at how we can create a proxy using a bean post processor and have it just work with spring uh aot support we'll create a new package here called uh BPP dot proxies okay I'm going to create a proxies configuration class and you know before we get into the aot stuff let's just consider the use case Okay so order service whatever avoid uh increment whatever add to a price something like that double amount right and we're just gonna you know adding amount okay and just uh very lazy piece of code here and it's a service and let's suppose I want now to have some Behavior where whenever somebody calls a method on this let's create a beam that actually uses that right application listener application ready event it'll be called order uh you know logged listener whenever somebody uses our object we're going to uh to have some code that gets run before and after each method invocation okay so here's the order service I'm going to say service Dot service dot add to price and just call it seven okay good so there's our invocation we're not you know it's nothing fancy we're invoking a method there now in order to make this work in order to add some behavior before and after they have a precondition and post post condition kind of thing we need to create um you know we need to inter intervene we need to intercept that method location and one way to do this is to create a proxy so let's first create The annotation that will demarc where we want the proxy to be applied okay so let's look at the uh let me just say add qualifier okay we don't need qualifier I'm just using this for a template for the code I hate running annotations it's too boring okay get rid of that and we don't really need we want this to be called logs I suppose and you know we don't really need all these other ones we just want for the type so good so there I've got my custom uh annotation this isn't a spring annotation I could you know I can create meta annotations for lots of different use cases but there are no spring annotations uh on this one this is all Java Lang so spring doesn't know about it it doesn't care about it um okay good so I want now to annotate this Bean with that and whenever it's involved whenever spring compiles the application or when it runs the application it's going to find I'm going to create some code to find this annotation and then and then to override the definition of this beam to wrap it in effect uh to Delhi you know to to create a dynamic subclass of it and insert my logic before and after each one of these methods gets called okay and the way you do that is you create a post-processor bean post processor um and this you know the contract here is pretty simple you have two callbacks uh that you can choose to implement one is called before the spring Bean one is called before the spring Bean has had its um after property set and add post-construct you know an initialization methods honored right that's the before initialization the second one is after initialization okay and that's the one we're going to use right we want the fully formed Bean um because we don't want to have to wait for any other side effects or anything like that so we've got a bean post processor we've registered it here and we want to make some code to instead of taking this beam if this Bean is a type of if this being has our annotation on it then we want to create a proxy that does what we want otherwise we return the the bean right so you know our default implementation if we look at the super version of this all it does is it returns beam so we could just do that right that would be the default version but what if this class is something that we did decide we want to act on so private Boolean matches class class name right and we and we can say Okay um if bin class is not null and last name dot get annotation logged dot class does not equal null right so if it's not null and the class is not null and the annotations that no then yeah this is a beam we want to be interested in we need to look at okay okay great so we've got that now the thing is we're just returning a regular old object given an object and we're going to create a proxy so the proxy is actually going to have extra signatures extra types in its signature that aren't compatible with this one right this is going to be it's going to be extra types that we might need to do reflection on and so on so we need spring to know about that in particular we need spring aot to know about that so we could implement this for a lot of use cases but for this particular use case the one we're creating a proxy instead of just some side effects where we note things or something instead we're going to use a sub class called a smart instantiation being a smart intention aware being post processor and here it behooves us to override a particular method called determine beam type our job then is given the bean class and the bean name to determine the target Bean class name okay so the same rules apply we can see that this just Returns the beam class so we'll just do that for ours our first one unless this matches okay so being class if it matches then we need to return the concrete proxied class of the um of the of the type okay so good so let's we need to do that for both right so if matches being that get class then do something otherwise just return the bean same basic approach here now what we in order to get the concrete class here remember this gets go this gets called before we actually create the object so we're giving it the we're giving it The Giving the framework away to kind of look ahead and say okay here's the shape of the type that will be created should you create a proxy but we're not actually quitting the proxy at this point that only happens in the post process after initialization and in order to do in order to create this proxy we're going to use the spring framework proxy Factory object okay so this is going to be pretty straightforward we say private static proxy Factory and we're going to create a proxy Factory instance um if there is a target object that is to say if we actually have a live beam as we do here then we pass this in and it'll be not null otherwise no matter what in either case we'll have the the target class won't we so we can do a lot but not everything with just the target class so let's do that so VAR PF equals new proxy Factory okay PF dot um set target class is target class PF dot add advice and we're going to come back to this in just a second PF dot set interfaces so target class dot get interfaces PF dot um set proxy target class right so set proxy is true okay good oh no true there we go so we want to now implement this part uh we'll come back to in just a second and then we say return PF yeah okay good so let's let's the only thing that we haven't done you know is the Target and that's only because that could be Noble so if no does not equal Target then um PF dot uh set Target okay Target good there's our there's that now here's the actual implementation of our our around aspect you know so I'm going to say before let's see VAR method name equals invocation.getmethod.getname uh and then we'll say system out uh before method name and here's after okay and we're gonna actually get the results now of the the invocation itself so invocation.getmethod.invoke on in uh on the Target on the target object there which at this point we assure we are sure is not null right um because this wouldn't get invoked otherwise um and so on we're going to invoke the Target and uh we're going to um use the parameters right so here's to get arguments there you go good so we're invoking that and we're returning the result of the method invocation so this is like a callback like an event listener for when somebody actually calls a method on an object on the proxy that we're creating and so here you know we determine that the beam type we say return create proxy or proxy Factory I guess uh null for the actual concrete one because we don't have that yet and then we can create the proxy right get the proxy class given the bean class.getclass loader um or in our actual runtime thing we do have the real thing don't we so Bean bar in class equals beam dot get class and beam class and Bean and yeah good so that's basically the same code in both places the only path it's a little different is the fact that the target might be null or not but basically that extra that extra callback that determined mean type gives spring enough information about the shape of the object that'll be registered at runtime okay I think we're almost ready to run it let's just make sure we uh look after everything that I screw anything up uh let's move this up to a separate variable that way I can just have one definition I don't have to worry about repeating it um we're returning the actual object here so we don't need the proxy class we need the actual proxy uh but otherwise that looks basically good to me let's go ahead and kick it off okay the method has been logged so before I add to prices in this example we're using the cohesive design of Springs aot support to transparently create proxies that just work if we've done our job right and we've written our code correctly to leverage the right spring interfaces then you can just trust that spring will do the right thing for your cglib proxies sometimes however you need to intervene and explicitly furnish hints for the things that you do at runtime if you're using jdk proxies then you can explicitly register hints for that with proxy hints registrar you might also have stuff that you want to do at runtime that requires upfront compile time processing code generation for example is a very powerful dimension of Springs iot engine we use it all over the place in springform X6 let's take a look at a simple example this time we'll revisit the code generation to register a spring boot actuator endpoint of type compilation endpoint which we're just making up for this example it'll contain useless information captured at compile time like the time and directory in which the code was compiled let's take a look we've already seen that the bean factory there is callbacks in the bean factory world give us access to the bean factory the very early stages of the construction of the application context uh both at compile time and at runtime we've also seen how uh the bean post processor gives us access to beans basically when they're just about to be put into production but you can still change the shape of those beans or those objects in the application context there's another interface that compile time called Bean registration aot processor and it can be very useful uh in the same way as being Factor initialization alt processor is useful to act on objects in the application context and change them perhaps with code generation let's take a look at that possibility or create a new thing here called code gen or just code why not and we're going to create a new class here called compilation endpoint configuration in the use case uh you know far-fetched though it might seem is I want to capture information about the actual applications compilation so I'm going to create a spring boot actuator endpoint I need to add the spring boot actuator to the class path though so let's just copy and paste that command V actuator good and then just make sure I do Maven reload project and good now um this endpoint you know I'm going to register it's going to be a spring boot actuator endpoint and um compilation and of course if I want to see this I need to actually enable it in my properties don't I so uh management Point web exposure includes everything and show the health details we don't need that one okay so there's my compilation endpoint um it's gonna have some data in it and I'm in a map okay I'm going to enrich that map we're going to replace this object at compile time right so map equals new concurrent hash map so there'll be default version of the endpoint that exists at runtime that's empty right obviously um so you know default runtime version and we'll have another one that exists at compile time that contains the time when it was compiled in the directory of the code yeah good okay and um I'll say map.put all mapped out of uh instant instant dot tostring or whatever instant we'll just leave that there directory directory very good okay so that'll that'll uh change that and then we'll create an endpoint here called read operation uh public map of string object um compilation okay return map dot of compilation and we'll just have this type map and we'll have a current time as well so that we can see the Delta okay between the compilation time and the current time okay good so now we've got our endpoint we need to actually sort of exercise it don't we so let's uh first of all register the endpoint here the default one compilation endpoint okay return new good and then we're going to create a bean uh to exercise that so compilation endpoint listener compilation endpoint application listener and we're going to use endpoint dot compilation bar map equals this uh and you know we're just going to log it out right so saying uh four VAR e map dot entry Set uh system out e dot get key equals e to get value right okay so just a just a basic example here but it's going to do something useless at runtime right unless something acts on it at compile time and this is where a bean registration aot processor can be pretty useful okay so class compilation in point being registration alt processor implements being registration aot processor goodie okay so there's this and um we get a callback here you can see there's an opportunity here to register a contribution which which is what we're going to want to do now we can register this interface just like we did the bean Factor initialization aot processor as a bean method like this or we can do an entry in aot added factories so um first things first do we care about this Bean right if not sorry sorry if compilation endpoint.class dot is assignable from registered bing.getbean class then then we can right um so if it doesn't then we don't care we'll just return it all otherwise we return a new Veen registration aot contribution and here we are going to write some code to post process at compile time the object that's been uh you know that's going to be passed in at runtime okay we don't have to create a brand new Java poet session we can use code and what the first thing we're going to do is get um you know we're going to get the code and the context we're going to use that to add a new class and they call it a feature um for a an existing component okay so this is basically your chance to contribute a method that gets to post process the bean uh that you're that you're contributing and it's contributed at compile time so you can do anything you want at that point right so VAR generated classes equals um context got get generated classes good got that and I want to generate a new one all right this is ours so we'll say generated classes Dot get or add feature components and we're just going to call this compilation and point at the simple name um feature all right and we'll use the compilation endpoint.class and then we have a builder here and we can use the Builder to describe this new class that we're building we're just going to say builder.add modifier modifier is public all right not a big deal good so now we have a generated class and in the generated class we need to we need to have a method that takes a reference to our compilation endpoint and allows us to either change it or return a new one or whatever it's kind of like we would in an actual beam post processor so here we're going to create a new method right generated method equals generated class dot get methods dot add post process compilation endpoint all right right it doesn't have to be named anything in particular we're just using that okay and um I'm gonna we'll get back to that implementation in a second here but once we have the method reference right we can uh once we have the method the generated method we can actually get the method reference and then turn that into a thing that we add to the code so Bean registration code add instance post processor method reference okay so that's the basic skeleton of what we're trying to do here there's even a there's an API here and again this is gonna we're gonna be able to use Java poet here you know this is what we just saw a little while ago but but basically I'm taking the existing body of generated classes and I'm adding another one right very convenient okay now here uh we need to actually implement this method so we're going to take a we're going to we're going to have a method that has a pointer to the compilation um endpoint uh and then we're gonna we're gonna work on it okay so let's say VAR output Bean variable name output Bean we'll say Builder Dot um add modifiers modifiers public modifiers static dot add parameter Registered mean right so you have to take a pointer to the registered Bean add parameter compilation endpoint and we can call this a input Bean and we'll call this one uh you know registered being sure it doesn't it doesn't matter what you can call it whatever you want these are the names of the parameters um returned compilation endpoint of course because that's what we take in um and then the code looks like this it's just going to be a code block right and uh we could we could just provide actual code right just add a statement with Java string and and be done with it uh or you can use these Builders to programmatically build up your Java code which is kind of interesting and I think I'll do that here um and the I'm only returning one statement it's just a doozy of a statement and that statement is dollar sign t for type dollar sign L for lexical equals new dollar sign T So type again parentheses dollar sign T of epoch mili l okay and then new Java IO file s okay there we go there's the uh there's that's the magic code right that's all that's doing is you know it's going to be a variable of type complication compilation endpoint on the left the output variable name is the L dotted l um the type of the thing again is compilation endpoint so I'm just duplicating that here because it's compilation endpoint output being equals new compilation endpoint right that that makes sense uh and then the type on the left is an instant so I'll say instant dot class uh you know and I can actually do that again where I do the Java i o file I should just do that but it doesn't matter okay anyway instant um and then system current time Milli so I'm capturing this this is running at compile time I'm evaluating evaluating the current time uh and I'm reifying it as a string you know a lexical string um that's going to be passed into the Java code unchanged right um okay good so system current time melee and then finally I've got the wherever this code is being compiled whatever the compilation is happening I'm going to pass that into the string and this daughter sign s gives me an ability to take a Java you know to take a string generated compile time and pass it in as a string correctly in the Java code and yeah going back to this we can fix this as well new T right and uh it'll just be file dot class okay so we're adding that statement and uh we're gonna say add statement uh return l and that's the output variable name right because we're saying that the variable name is equal to Output being variable name and now we're returning the same thing um and then we just want to we want to call builds which is what we've already got done there so good so there's our our whole method body uh add okay that looks to be correct to me let's go ahead and compile the code and see what happens [Music] all right the application is started up there we go so there's the compilation information and it was compiled in users jlong download aot and the time was uh you know 35 minutes after midnight which which is wrong obviously at the wrong time zone but um 34 minutes after midnight uh uh but if you look at the current time right now it's 35 minutes right so it has clearly uh worked right we've captured the compile time state and we've reified it we've kept it in the present in the generated Java code and you can see that here in guess what the generated Java code let's go down here and check it out okay so being post processor code right aop application where's our oh here sources that's gonna be more correct right so um where's our custom processor oh here's the endpoint feature there you go uh so post process compilation endpoint that's our in definition and here you can see it says compilation endpoint output being equals new compilation endpoint instant of Epic merely capturing that and here's the code you know the actual it's capturing off of the file system path right where I'm where the code was compiled and it got baked into the Java class which then got registered right this is called uh compilation endpoint feature look for that in here you can see we're saying create the instance and then post process it you know using that method that we have the static method how cool is that huh you got a way to reify to change the code dynamically and we testing is very important to the spring boot developer experience obviously there are going to be some inconsistencies between the way an application any application runs in Java on the JRE versus Java in a gravian native image context and so to that end we've made our testing support as solid as possible to help you obviously the basic stuff works so for example uh if I went to like just I wanted to test my business logic right you remember earlier we created this this uh this repository let's create a new test for it uh spring boot test you know ADD test void persist this kind of stuff you know you've got a million of these I'm sure um and we'll just inject it into the Constructor at Auto wired and I just want to make sure that if I call repository.save for a new uh customer that you know the name is the idea is not null so name okay bar saved there's that and I want to just say assertions certain not normal saved dot ID right and I also want to say that I want to make sure the ID the object itself is not null so that'll work that that should work just fine obviously I don't want the whole uh I want to say spring application type is none right okay good good good that'll work just fine there's our business logic test uh let's just compile it and run it and see okay now we can test our applications in the normal way maybe even clean package will invoke the tests on the JRE but if we want to use if we want to compile our code and run it in a native image context then we can activate the native test Maven profile in the same way that we can also access the native profile to get a native binary for the production code okay now we're just going to run this foreign [Music] if you go to the Target directory you'll see a native tests binary that was compiled and contains the results of our tests so there's the test report to Containers found one test found one test started one test successful obviously uh and and so on you know the customer repository test persist is successful so that's cool right now you have the ability to actually do your business logic and make sure that that works and by the way uh you know when you're running these tests you can also run that Java agent and capture information and you can do all sorts of things uh with tests to help make your application movement either migration to Native all that much easier now let's say you've written some uh hints a runtime registrar hints registrar and you want to confirm that it's working as expected well uh earlier we looked at an example where we had a runtime into registrar where we loaded uh where we couldn't hint for a CSV file well I'm going to test this okay so I want to test that so I'm going to create a new test and in it in it I'll say uh okay I'm going to create VAR hints equals new runtime hints and I'll create a reference to the registrar so that'll be new migration dot uh into registrar okay and I'm going to get appointed to the class loader which is uh you know get class dot get class letter we'll say registrar dot register hints okay with a class loader Okay so we've registered the hint now we're going to say assertions uh that and we're gonna have some predicates we'll say runtime hints predicates dot resources for resource data.csv except uh the hints that we've given okay good let's go ahead and run this and see what we get very good so it seems like we did actually successfully register that hint uh let's try the negative you know let's put a case that shouldn't work and yes it fails good so hopefully you you can see that not you you have not just your normal uh means of testing to ensure parity across one environment or another but you also have code dedicated to making it easy to test the components that you have to register specifically to make Native work we've come a long way when I started covering spring native in 2020 a build took 10 minutes and only worked for the smallest number of specific scenarios now here we are in 2022 and I can all but effortlessly get a spring App working in a gravity image context in less than a minute the performance and memory usage implications are hard to ignore and very persuasive in a context where you have to pay by the uh by the use and I think the inertia required to make the move to aot will become easier and easier to overcome in the next days weeks months and years as the community Embraces Springs Newfound aot support I hope that uh this video makes it easier for everyone right I want everyone application and framework developers alike to leverage uh this new mechanism to improve their offerings to improve their applications production prospects and uh efficient just look for pointers to types like runtime hints registrar Bean registration aot processor and bean factory initialization aot processor in the spring boot code base for guidance and these don't even begin to cover the implementations provided by the projects themselves like Spring Security spring data spring Cloud spring shell spring integration spring batch Etc We've Only Just Begun to scratch the service here and I can't wait to see what comes of this now bear in mind these things may change in relatively minor ways in the coming weeks and months leading to Spring boot 3 later this year but conceptually what you've seen here is the future and it's here for you to try out today we'd love to hear from you don't hesitate to reach out to us on Twitter uh or via the stack Overflow forms or the GitHub issues for their respective projects that you care about we're all working very hard to make this work for you let us know
Info
Channel: SpringDeveloper
Views: 22,218
Rating: undefined 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: TOfYlLjXufw
Channel Id: undefined
Length: 128min 32sec (7712 seconds)
Published: Wed Nov 23 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.