Devnexus 2021 Graeme Rocher Micronaut Advanced DI & AOP

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] welcome to this presentation on advanced dependency injection and aspect-oriented programming with micronaut my name is graham ortega i am an architect at oracle labs where i lead oracle's contributions to the micronaut framework i am also the creator of micronaut and another framework called grails do connect with me on social at the links on this slide so on the agenda for today we are going to do an introduction to micronaut um an introduction to growl vm i'll talk about micron and dependency injection i'll talk about micron and aspect oriented programming and the main part of this presentation is going to be lots and lots of demos so microdot is essentially a build time framework what that means is micro hooks into your compiler and a compilation time computes your dependency injection configuration injection any annotation metadata aop proxies and it does so in a completely reflection runtime proxy and dynamic class loader free manner so that literally your your application is statically wired together at run time this means that a micro node is great for a variety of use cases including things like microservices and serverless applications but also message driven applications with kafka rather than q or even jms in the recent versions command line applications as well is also a great way to use micronaut as i'll demonstrate today you can even use it in mobile contacts on android anything that has aesthetic void main is pretty much a good shot target for micronaut and java cotton and the groovy language are the supported languages as of this time as micron hooks into each of those each of those is compilers in different manners for the java it's based on annotation processes micronaut also has really great integration with growl vm so growl vm is a new virtual machine out of oracle labs that inject mode is able to increase your application throughput by 20 to 30 percent um and it's also able to do a closed world static analysis of your application to produce a self-contained native binary using the native image tool which significantly benefits cloud deployments and things like serverless with its super fast startup time and load memory consumption you can also use multiple languages together with the truffle language framework and things including java and ruby and python and so on and so forth so to get started with micronaut we have a bunch of really great build plugins uh one of them a build plugin for gradle there's one from maven in this demo i'm going to be demonstrating the gradle plugin and it's a simple matter of declaring the gradle plugin which is the ira.mic.library plugin or the io.application plugin depending whether you're building an application or a library and declaring the version of micronaut that you're using the current latest release at the time of this talk is micronaut 2.3 and which is what i'm going to be demoing in this demo so in the context of this demo we're going to be doing a lot of work with dependency injection and aspect-oriented programming we're going to look at how you can get set up quickly how to do some basic dependency injection how to different work with different bean scopes configuration injection and configuration properties environments conditional beans around advice and bean events so it's really quite a bit to go through so what i'm going to do initially is i'm going to create a build.gradle file in terminal and this is an easy way to get going you don't need much you just need a text editor or an ide i'm going to use vcbs code and i'm going to create a build.gradle file and i'm going to open it up in vs code and as per the slide i'm going to add my micromode plugin to it so in the plugins box i'm going to say id is io the micronaut dot application and i'm going to use that version 1.3.2 of the plugin which is the latest version as of this recording and i'm going to specify my micro version which is distinct from the actual plug-in version as the latest version which is 2.3.0 i'm also going to specify test runtime as junit this allows the plugin to automatically configure jq5 as my testing tool and finally i'm going to add an implementation dependency on micro not runtime which is the only library the minimal library needed for working with microbot um at runtime finally i'm going to set a application like a class that's going to contain my main method for my application in this case it's going to be called example.application which i haven't created yet but i will do in a moment once we can say that we can close that and we can open it up inside vs code but before that i'm going to create a couple of directories one for my uh source java sources and one for my test sources in source main java and source test java we're now ready to open that up in vs code and here we have my micro application ready to roll um so the first thing i'm going to do is i'm going to create that application.java file that i referenced in my build in my example package it's going to be an application class and it's going to have a public static void main string array args method entry point the typical entry point for java applications and within this i'm going to call micronaut dot run and pass in the arguments to micronaut and in case it needs them and i'm going to import that class and that's how you basically use micron as an entry point in your application and set up di for testing you can create a separate class for testing and i'm going to create a application test.java file yeah again within the same package example and i'm going to call it application test if i can spell correctly and um i'm going to annotate it with micronaut tests what is micronut test microdot test is the testing framework for micron and lets you dependency inject beams and components into the test itself that you're running so for example i can use the inject annotation from java x dot inject to inject the application context the applications under the micro node is a beam container uh which contains references to all the different beams that get created or like are creatable and i'm going to inject it and i'm just going to write a simple test that that asserts that i'm able to inject it into this test uh using a cert is a not equals i think it's probably the best for this scenario so not equals not null context okay so that's it so that's my test uh so i can now effectively run this test and will only hold the test passes as as expected so i'm able to inject my application context into my test class and we're at the races now to run my um my main method i can do the same thing i can run this main method uh from my ide you'll notice the upwards saying that microphone is starting up uh although i get some slf or j errors this is because by default micronaut uses slf4j for logging so you need at least one dependency in build or gradle uh on an slf 4j provider like logback or something uh in my case i'm going to use sfx4j simple which is just a simple logging framework but we'll just write to standard out which is fine for this demo so once that's in place we can run it again and you'll see that this time i get some better logging output but micro just outputs that this is a cli app and that no container was fine so it's running it as a cli application now let's actually create something interesting so i'm going to create a vehicle class inside my example example package and this is going to represent my domain model so i've got a vehicle class and a vehicle is going to have a engine that it's going to operate with so i'm going to define engine as a constructor argument a required constructor argument of vehicle types i'm going to generate a constructor and and that's it and if need be we can we can also now notice that this class doesn't exist so we need to create it so i'm going to create that class and it's going to generate the necessary class for me i actually want this to be an interface i'm going to alter it to be a interface instead and this is going to be my interface for starting the engine so for the interface i'm going to define a single start method um and then from the vehicle class um i'm going to define another start method that essentially starts the engine every time the vehicle is started so we're going to call engine uh engine dot start and we're off the races okay so that's it so i've got my vehicle calling my engine to start and that's it so how would i make this component so one of the well the first thing you did need to do to make a class uh injectable via dependency injection is to add a bean scope so i'm going to add the singleton scope now what is singleton singleton is from javax to inject and it basically identifies a type that should only be instantiated exactly once so a singleton type by adding it to this vehicle class i've made this component available for dependency injection and we're ready to go so i'm going to try and inject this into my chest i'm going to inject the vehicle into my test and then in my test i'm going to try and call the stock method that we defined earlier so let's run this test and see what happens in this context i'm going to run the test and we're probably going to show my console again um focus console oh dear okay so the test failed uh that failed logically there's foiled with the no such bean exception okay that actually makes sense uh as you remember the vehicle requires an engine and there isn't actually a engine beam that we only have an interface at this stage of time so what we need to do is define a new um engine implementation so i'm going to define a v6 engine um for my interface and it's going to be in the same package once again and it's going to be a gonna be named music and it's going to implement engine um and then what i'm going to do is i'm going to generate an implementation for this engine of the start method so i'm going to go to my source actions and generate implementation and there we go so we have my start method generate generated and i'm going to provide an implementation that for the moment just outputs uh something useful to system dot out saying that this is the v6 engine and we're going to start it and boom there we go there's my v6 engine starting up so now that's that done now the next thing to do is i have to add i should add a scope to this so again we can add the singleton scope we could make a prototype so a new engine is created for each car but in this case we're going to just use singleton and now we can see that the test passes which is great news so that's the test now what what about the case where we want to actually run my application class and get this output so by default micronaut is when you when you call the run method it's just going to start up the micro context it's not actually going to invoke any beans within it to do that we need to make some modifications to my class to make that happen so to invoke the engine and vehicle so i'm going to actually make my application class itself a singleton and i'm going to uh add to add to it a startup method that gets invoked when the application context starts so we can listen for startup event now startup event is an event that's fired by micronaut when the context starts up and we can uh inject my vehicle uh instance if i can type uh into my application class and i can use source actions to generate a constructor for the vehicle again and then like what i can do is i can call um vehicle.start from my startup startup event now the only thing i have to do is make sure i annotate this with event listener to signify that the method is listening for startup events so i add the event listener annotation and now we should be ready to run my application so if i run this now you can see one startup outputs b6 broom so uh it's it's firing the startup event we're listening we're invoking the start method and we're at the races okay so that's that's pretty cool we have some basic uh dependency injection working now what if i wanted to drive you know the whole point of dependence injections decouple implementation from the interface so what if i wanted to drive a different implementation based on configuration so what i'm going to do in this case is i'm going to create a new vehicle configuration which is going to allow me to customize and provide different vehicle configurations based on the environment and configurations i'm going to create a class that is the vehicle configuration and i'm going to annotate it with add configuration properties and vehicles so there you go there it is um and then i'm going to define a um in an enum here called vehicle type or actually you know it's called engine type that's probably more logical um and i'm going to um support engine types of v6 and v8 in my configuration and then i'm going to provide a property of this vehicle configuration that that allows me to to define the engine type fire configurations there we go we're going to define that property and i'm going to generate a getter and a setup for it and there we go so we have a mutable configuration properties b in here that represents my configuration and that's good so now how how would i dynamically based on the configuration create different implementations so one way to do that one possible way to do that is to no longer make this a beam and instead of making exposing this bean directly i'm going to create a new engine factory uh class and i'm going to put in the pack example package i'm going to annotate it with app factory and this is going to be my engine factory and into the engine factory i'm going to inject the engine the vehicle configuration um field type that i defined earlier i'm going to make it a final field once again we can generate a constructor for it and uh and that's it we're at the races for my configuration and now what i need to do is define a method that produces a beam so the way you do that is by finding the scope on the method itself so i'm going to make the engine method return my engine implementation by annotating it with at singleton and i'm going to pass in the engine type so in the case of v6 i'm going to return my v6 engine and uh and otherwise in the default case where you know we can't support that version we're going to throw an exception so we're going to throw a new configuration exception and we're going to say uh that's an unsupported engine or maybe maybe we should actually provide a little bit more detail in the area message underscore the engine type and provide the actual engine type that's been supported okay so um finally if you also want to default default a particular injury type we can make that into a particular engine type in this case b6 as the default value when under specified um and we're ready to go so um with that we should be ready to go so let's go back to my main class and let's run my main method again open my debug console and run vm and you see it's working so it's based it's basically defaulting to v6 because we don't have any configuration let's change that and actually create a main resources directory and create a new application.yaml file why did that come docker and in here we can say vehicle engine type and set it to v8 instead of v6 and let's go back to my application class now and see what happens so actually failed and it fails because because it would get a configuration description and spot it into type b8 v8 engines are not supported at this time by my factory so that's exactly why specifying b8 resulted in an exception so to solve that i could come in here and create my b8 engine java implementation and basically define a new class that represents my v8 engine and again once again you just implement the engine interface um with something reasonable or uh generate an implementation for it and uh you know this time essentially do the same thing system.out dot print line and output the uh v8 engine as my implementation then maybe v8 engines make more noise so we make a louder noise okay so that looks okay now just in my switch statement we need to update that to handle the case where v8 is specified and return uh the new v8 engine and we're at the races okay so that that that's my my factory implementation updated uh let's go to my main class and run that again and as you can see we have it working so v8 is now configured and working so we can change the configuration also the configuration and switch between different engine types and it's completely configured figurable via application.yaml and the injected vehicle configuration now one thing i wanted to show you is that um this is a class and it's mutable so we've got getters and setters as you can see you've got it's for the engine type we've got a cylinder but we can we can also make it into immutable configuration if we don't want my configuration to be modifiable we can change this to be a interface instead and get rid of that field and get rid of the implementation and get rid of the setter and now we just have an interface that defines my configuration and what micronut will do with that we'll add compilation time automatically implement this interface for you and provide an implementation that resolves the properties so if we once again show my console and run this test as you can see we get the exact same behavior exactly the same just a lot more concise by using a vehicle configuration interface with simple getters on that get implemented for you at compilation time in micron 2.3 you can also use records as well which is pretty cool uh for configuration properties okay so um so with that done i'm going to actually clean up here since that it's no longer being being used i'm going to disable my i'm going to show you different technique for doing this i'm going to disable my engine factory and instead i'm going to replace it with at named beans so i'm going to add use javascript named and add named v6 to this engine and i'm going to go to the other one i'm going to add named b8 um so remember i've disabled my engine engine factory so that's not working anymore and instead of my injection point i'm going to inject a named bean here and what i'm going to do is i'm going to use a property expression uh oops it's got to be surrounded by dollar in curly brackets so i'm going to say vehicle.engine.edu type and i'm going to do a user colon which allows me to specify a default value of v6 so if it's present it'll get injected if it's not it'll use v6 and that allows me to dynamically configure the name of the engine that gets injected without even involving the factory so let's run that and see what happens as you can see we get v8 room and if we come to my my configuration we alter that to b6 and we run it again you got a b6 so as you can see without even involving complex factories you can just use dynamic um property expressions to alter how things work so let's clean up and get rid of that factory since we're not using it anymore and and look at a different technique for doing this so sometimes you want to have use different engines based on the environment for example locally or in the cloud so what i'm going to do is instead of using that name we're going to make the singleton and i'm going to say that the va engine requires using the requires annotation the environment to be environment.oracle cloud so the va engine we have more processing power in oracle cloud is going to use the v8 engine and locally singleton i'm going to say requires and i'm going to say not in environment environment oracle cloud so when we're not in the oracle cloud environment use the vxv6 engine and when we when the original environment use the other one we can just remove this property expression and now let's see what happens when you run my application so i'm going to run it and what happens is we get the v6 engine because we're running locally we're not we're not currently running on oracle cloud and so how do we simulate running on oracle cloud so if i just change this to build args and then i use the environments method to tell micronaut that we're actually you know really in miracle oracle cloud and we can start now when i run this application again you will see that the engine is is output um so as you can see it's used when you're in a cloud it's using v8 now you don't normally have to be explicitly specify environments here what micronut will do well micron will automatically detect the environment that you're in in fact there is a way to do that called induce deduce environments you can disable it if you want or you can the default is set to true and what micronut will do is it will automatically detect when you run in oracle cloud or amazon cloud or whatever cloud and will automatically detect the environment and allow you to adapt your application based on whether you're running in the cloud or not which i think is pretty cool so now i'm going to show you a different way to do this so instead of using environments what i'm going to do is i'm going to do say that requires property property equals and the name of that property that we have in application.yaml so if you remember it's vehicle.engine type so i'm going to say at requires property vehicle dot engine type value equals d8 so this one will be active when when it's set to be eight and the other one will be active when it's set to v6 there we go and and we so that we can configure different um beans based on configuration just using bean requirements which is another alternative approach to doing this so let's run that application again and see what happens so when i run this application as you can see we get the v6 output because it's set to b6 in configuration but we can alter that to v8 and run it again and there we go we got the v8 engine in operation okay so what's really powerful about this though is when you start mixing in uh cloud specific configurations so i can create an application. hyphen oracle cloud.yaml or aws.yaml or gcp and i can i can copy that in there so i can say i'm going to use v8 on our cloud and i'm going to use v6 by default and then when i run my application now you'll see the output is v6 which is expected because that's the default but then i can go back and alter my my my startup process again oops if i can type correctly with the build method node okay environments there we go environments environment oracle cloud and start and now we effectively get the same uh results right so vertical cloud is environment and v8 room by activating the oracle cloud environment micronut will automatically detect the micro application oracle cloud.yaml load it and apply that different configuration based on the cloud that you're running in uh which i think is pretty neat um so that's that's uh another way to do it now i'm going to look at um how you apply you can apply some aop advice so we've got this engine interface got the start method and i want to apply some advice to it so i'm going to add a track start.java annotation inside the example package and it's going to be track start i'm going to call it trackstart it's an annotation and i'm going to add the around advice annotation and the iran device notation basically says i'm going to decorate a method indication such that a method can be intercepted by a method interceptor so i'm going to need a method interceptor so i'm going to create a new track start interceptor java file and again in the example package and i'm going to name it the right thing and it's going to implement method interceptor passing a couple of just generic arguments and i'm going to generate a implementation of the only method you need to implement which is intercept and i'm going to make this a singleton interceptor and for the moment i'm just going to proceed with the indication and return the result and split output to system.out the name of the method that's being intercepted so let's output that get method name there we go um and that's it um now the only thing that we have to do is make sure my annotation is retention uh time uh since we don't want to be source retention and apply the interceptor type that which is a context annotation.type that we want to apply every time this annotation is used so i'm going to add the track start interceptor and then we're going to go to my engine interface and add this annotation to the interface now the powerful thing about adding an interface is every time somebody implements this interface they will automatically get this aop advice and additional behavior applied to their new implementation of the engine interface i'm not applying it to the implementation i'm applying it to the interface and so let's see what happens when we run this main method and as you can see we get the method start output which which is amazing so every vehicle implementation that implements the engine interface is getting aop advice applied um which is really really cool now let's make this advice do further decoupling so currently the implementation of the device is you know just outputting the method name um but let's get it to actually trigger an event and a good way a good way to decouple application architecture is through an event system so what i'm going to do is i'm going to add the ability to publish an event i'm going to create a new event here called enginestartevent.java say and this is going to go into the example package and once again every time an engine starts we're going to trigger this event which allows me to publish information about the engine starting which other event listeners can listen for and extend my application without hearing without introducing coupling which is really really critical so i'm going to add a engine event that takes in the engine and inside my interceptor here i'm going to publish the event so what i'm going to do is i'm going to get the target of this event which is going to be the engine like so and um and then i'm going to uh inject into my interceptor the application event publisher which is the event publisher and i'm going to add a constructor argument for this um this event publisher so it gets injected and then i'm going to call an event publisher.publish event and publish a new engine start event so now every time somebody starts an engine that open implementation this event will be triggered by the application um which i can listen for in anywhere or any anywhere i want to so i can add an event listener here or one or many different event listeners to listen for engine start events and act on them as they happen you could for example log them or publish them to back-end systems or metrics or anything like that um and for the moment i'm just going to print it out i'm going to print out this that the you know the engine is starting and you know just output the event the engine we can't get the engine type no we'll just have to update the engine for the moment um i'm still going to send what's wrong here missing semicolons i always forget when i'm in java so we have to add that semicolon and there we go my there's my engine start event listener and now when we run my application you'll see that um what happens is the event gets published and look we get the engine started event triggered so we have this nice decoupled architecture between the implementation publishing event every time the stock method is called when somebody comes along and adds a new engine this behavior is automatically applied and we can even add things like async so i can add an async annotation to the event listener and listen for events asynchronously so they don't block the the implementation of the start method and so we look at the output again you can see that the engine started output actually appears after the broom message and that is the demo so in summary micronut provides awesome build time dependency injection in aop it's fully compatible with graphing native image out of the box because it's not using any reflection any runtime proxies anything that would interfere with the closed wall static analysis and it's flexible enough to be used in a range of scenarios beyond just microservices as you saw i use it for cli applications in this demo some resources come speak to us on gear the user guide for more documentation create your applications today on micron launch at launch.microsoft.io check out the guides the guides on my channel io and follow us on twitter and start us on github thank you very much and it's time for a q a session and i hope you enjoyed this session [Music] [Music] [Music] [Music] [Music] [Music] [Music] [Music] [Music] [Music] [Music] [Music] you
Info
Channel: devnexus
Views: 174
Rating: 5 out of 5
Keywords: Java, Java 13, Java 12, Kubernetes, Spring Boot, Continuous Delivery, Java 9, OpenJ9, Java 8, OpenShift, Open Source, Jakarta EE, Quarkus, Java Conference, MicroProfile, Quantum Computing, Devnexus, Java Programming, Java Platform, Kotlin, Spring Cloud, Docker, Spring Platform, JVM, OpenJDK, Java 11, GraalVM, Software Architecture, JDK, JavaFX, Conference, Micronaut, Microservices, Oracle JDK, Cloud Native, DevOps, Graal, Cloud, Java 14, Java 10, Software Engineering, Continuous Integration
Id: r-ALNF-B3HU
Channel Id: undefined
Length: 37min 33sec (2253 seconds)
Published: Wed Feb 24 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.