I am new to Spring and would like to convert my existing applications to Spring Boot.
However, I am using a self-written module framework that allows me to add or remove components or additional functions of the application dynamically at runtime. The whole thing can be compared to plugin frameworks like PF4J or the plugin mechanism in Minecraft servers.
The advantage of this is obvious. The application is much more dynamic and certain parts of the program can be updated at runtime without having to restart the whole application.
Under the hood, a new ClassLoader is created for each module when it is loaded. The ClassPath of this ClassLoader contains the JAR file of the module. Afterwards, I load the respective classes with this ClassLoader and execute there an init method, which contains each module.
Now, I would like of course in connection with Spring that both the dependency injection in the modules functions, and that beans or, for example, rest controllers, which are in the modules, register with the module loading and unregister with the module unloading.
Example: I have a staff module. When I register it, the employee endpoint is registered and is functional. When I unload the module, the employee endpoint is removed again.
Now to my problem:
Unfortunately, I don't know how to implement this with Spring, or if something like this is even possible in Spring. Or are there even already other solutions for this?
I also read something about application contexts. Do I have to create a new application context for each module, which I then somehow "closed" when unloading the module?
I hope you can help me, also with code examples.
This post helped me a bit: https://hdpe.me/post/modular-architecture-with-spring-boot/
In short for each module a new ApplicationContext (e.g. AnnotationConfigApplicationContext) is created. If you want to share beans between the modules, you have to publish them to the main application context.
Beans can be registered at runtime by ((GenericApplicationContext) applicationContext).registerBeanDefinition(name, beanDefinition); at the main Application Context.
Another problem is that additional configurations are required, for example for #RestController or similar, in order for them to work. See other questions on StackOverFlow from me.
Related
I am working on 2 projects, one web app (Spring MVC) and one standalone backend service application (Spring boot) that heavily interact together. I am using hibernate for both and they are both coded using the Netbeans IDE.
My "issue" is that i end up with duplicate code in both project, mainly in the Repository and Service layers. My entities are obviously also duplicated since both projects use the same database.
Is there a way to make some sort of class library (a third project maybe?) and put all the common code in there? If that is indeed possible, how do you then change each project so they can still access this code as if it were part of them? I was thinking of putting all my Repositories, Services and entities in there to avoid code duplication and greatly reduce the risk of error.
Thank you!
Separate those Repository and Service classes to a submodule.
The structure looks like:
-- your app
-- api (dependent on `common` module)
-- webapp (dependent on `common` module)
-- common
Then the problem is to initialize beans inside common module. AFAIK, you have two options:
In #Configuration class of api or webapp module, add base packages of common module to component scan packages
In api or webapp resources folder, add Spring configuration factory
/src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=your.path.AutoConfiguration
Define service/repository #Bean inside AutoConfiguration class
I am assuming in this answer your projects are connected to each other
You can set multiple properties within one Spring project, where you store your database connection parameters etc. with the help of multiple property files.
For example:
application-web.properties
application-backend.properties
You can use these in your project, by activating the needed properties file per application. The profile names will be web and backend in these cases.
When using maven, this is the command line I am using:
mvn spring-boot:run -Drun.profiles=<<profile>>
Now, back to your java code.
If there are classes only one of your application is using, you can specify this by 'profile'. Example:
#Controller
#Profile({ "web" })
public class WebEndpoint {
}
This way you can make the shared code available for both applications, without duplicating most of the code.
Here I am again seeking enlightment. The situation is as follows:
We have a task engine running in Wildfly 11 that depends heavilly in dynamic loading through reflection. Don't need to say that we have an abstract "base" class for everything. It depends on database registration that uses simple Class.forName() to load the implementation. For usual work, it runs smoothly.
Since our engine has different modules, we already work with jboss-deployment-structure.xml to tell Wildfly that the "Process" module depends on "Core" module and so on.
But now, we need to work with "plugins", so our customers could create their own implementations inside another package and simply register them in our database.
The question is: How do I tell Wildfly, or the classloader, or the divinity that manages it, to load the class inside the plugin without using .xml dependency files, since it would create a "regretable" circular dependency (the plugin already declares it's dependencies to the Core package)
Is it possible without hard changes in the implementation (like make the plugin init to declare itself to the core or something else)? we have a LOT of code and this kind of refactoring is not feasable at the moment.
Best Regards,
Fernando Augusto.
I am going to create Java Application that can load external jar files at runtime by FileChooser. I am using Spring Framework, and I want to load jar file and its applicationContext.xml file and inject its dependencies dynamically. I tried to achieve this by OSGi, but it seems very complicated so that I am searching another appropriate variants.
I want to make something like Intellij IDEA plugin installation from the disk.
How can I do this? (After the jar file chosen restarting an application also accepted)
I realy like your approach, unfortunately spring has lifecycles that are strict. As you might know, spring autowires "beans" only. Exactly one lifecycle registers the different bean candidates. After that lifecycle spring (by default) does not accept new classes.
You must use the spring-osgi.
If you only need the CDI part out of spring, you might like to use a different CDI like red hat's jboss server.
One of the main advantage of using spring dependency injection is for testing the functionality using same interface with different implementation without making any changes in the code, that is through injecting these different implementations(dependencies) in configuration file.
Lets take an example where we have developed our application with java configuration/annotation based (No .xml files at all).
We have done a code freeze and have deployed the code in server.
Now for a QA team to perform testing they need to inject different implementations for the interface by making changes in configuration file without touching code.
If its a .xml file, devOps team can inject the different implementation by injecting that bean name and can restart the server.
But since we have used the annotations based/java based configuration, How can we achieve this ?
Thanks in advance.
One of the main advantage of using spring dependency injection is for
testing the functionality using same interface with different
implementation
One of main advantages of Spring is indeed the dependency injection facility.
But you will also find very often cases where you have beans with a single implementation :
beans that rely on an interface but there is only one implementation for it.
bean that don't rely on any interface but are straight classes that you want to turn into injectable beans.
We have done a code freeze and have deployed the code in server. Now
for a QA team to perform testing they need to inject different
implementations for the interface by making changes in configuration
file without touching code.
Spring and more generally dependency injection pattern/frameworks are not designed to perform hot swapping or implementation modification of a deployed component without repackaging the component.
At startup, Spring creates its context and loads all required beans for the application in its container.
If you want to change configurations of some beans, the most clean and side effect less way is destroying the spring context/container, repackage the application with the needed changes and restart it.
If its a .xml file, QA team can inject the different implementation by
injecting that bean name and can restart the server.
Ideally, the QA team should test the implementation that you deploy in QA env and that will be used by final users to stay the closest of the real functioning of the application.
Now, if because of some specific constraints, some components to test by the QA should be mocked/stubbed in a some way, just create a different build for that.
Spring Boot Profile and Maven Profile features can help for.
I have a couple of Singleton classes in a Liferay application that hold several configuration parameters and a ServiceLocator with instances to WebServices I need to consume.
I have put these classes in a jar that is declared as a dependency on all my portlets.
The thing is, I have put some logging lines for initialization in theses singleton classes, and when I deploy my portlets I can see these lines multiple times, once for every portlet, since each portlet has its own class context.
For the AppConfig class it might not be such a big deal but my ServiceLocator does actually hold a bunch of references that take a good bit of memory.
Is there any way that I can put these Singleton references in some kind of Shared context in my Liferay Portal?
The problem is that every Portlet runs in its own WAR file and aech war file has its own classloader.
Usually when I had to achieve a requirement like this, I had to put the Singleton classen in a JAR file and this JAR file in the common class loader library instead of packing it into each WAR. (In Tomcat: <tomcatHome>/common/lib or something like that)
Then you'll also have to put all dependent libraries into that common lib dir, too. Don't know how to do that in Liferay, though. For tomcat see this thread: stackoverflow.com/questions/267953/ and this documentation: http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html. Depends on the Servlet container.
Alexander's answer gives the general answer that's true with or without Liferay in mind.
Liferay (as you mention it) adds another option to this: ServiceBuilder. You'll end up with the actual instances contained in exactly one web application, and you'll have an interfacing jar that you can distribute with every dependent application. This way you can more easily update your implementation: It's easy to hot-deploy new and updated web applications to your application server - it's harder to update code that's living on the global classpath.
The global classpath (Alexander's answer) however brings you immediate success while ServiceBuilder comes with its own learning curve and introduces some more dependencies. I don't mind those dependencies, but your mileage might vary. Decide for yourself
With maven portlet you can make a common Spring component and import in the pom of each portlet.
Another solution is to use service builder.
Spring MVC portlet would be the most recommended for this.