Spring DeferredImportSelector run before any Configurations - java

I am trying to create something like #Enable... autoconfiguration. I wanted to create such an annotation for a custom library that has an extensive spring config, yet needs like 2 beans supplied, and based on those initializes various contexts. It indeed initializes beans in all #configuration classes that are returned in an array, but I also want to do some custom configauration logic based on already registered beans. Now the javadoc for that
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ImportSelector.html
States that
ImportSelectors are usually processed in the same way as regular #Import annotations, however, it is also possible to defer selection of imports until all #Configuration classes have been processed (see DeferredImportSelector for details).
So I turned to DeferredImportSelector cause that Selector is said to be run after all #Configuration beans so I can do conditional Beans. Now javadoc is quite clear here (https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/DeferredImportSelector.html)
A variation of ImportSelector that runs after all #Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are #Conditional
So this is perfect for me. Well until it turns out that no matter what I do, the import selector selectImports method is run always as first before all #Configuration beans.
I figured out that maybe the main #Configuration bean is always last, and the javadoc actually mentions all imported #Configuration beans. It appears that is also not the case. I have been checking precedence using the debugger, but here is the test code I did, and it is pretty simple:
The import selector that does nothing except system out:
public class TestImportSelector implements DeferredImportSelector{
#Override
public String[] selectImports(AnnotationMetadata arg0) {
System.out.println("ImportSelector");
return new String[0];
}
}
The configuration class (the imported to check if any of mentioned ideas work)
#Configuration
public class ImportedTestContext {
#Bean
public String testBeanString2(){
System.out.println("bean2");
return "string2";
}
}
The main context class (also tried swapping the imported classes)
#Configuration
#Import({TestImportSelector.class, ImportedTestContext.class})
public class TestMainContext {
#Bean
public String testBeanString(){
System.out.println("bean1");
return "string";
}
}
And finally my main class
public class Test {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(TestMainContext.class);
}
}
Sooo, as You run the main. You always get the same output
ImportSelector
bean2
bean1
The import selector is always first no matter what. Additionally I tried to mess around with Ordered interface as the javadoc says
Implementations can also extend the Ordered interface or use the Order annotation to indicate a precedence against other DeferredImportSelectors
But it appears that getOrder method is not even called. Well that might be because it says that it only checks it agains other DeferredImportSelectors (and there were none but was worth trying)
I have done this with spring-context 4.3.2.RELEASE as this is what is used in my project, but just to make sure also tested with 5.0.5.RELEASE.
Exactly the same result.
So I believe I do not understand something with regards to ImportSelector, DeferredImportSelector, spring or there is a little chance that the javadoc is not saying the truth or I misunderstood it....
I would appreciate any help or advice....
Just to make it clear: Based on that DeferredImportSelector, I want it to implement BeanFactoryAware (this part works, Spring indeed injects the BeanFactory) that would check what beans were already defined (like those funny test string beans) and based on that will tell spring what additional configurations should be loaded. Based on javadoc this is what it was made for.....

It looks like DeferredImportSelector has a little bit unclear documentation. After running a few tests and checking the code, it turns out that what is deferred is Import, not ImportSelector.
So, if you make use of DeferredImportSelector, you can select a configuration class(es) which import will be deferred.
A selectImports method will get executed normally - during configuration files resolving/parsing, so - using BeanFactory to check if other beans' definitions are already loaded would be certainly a bad idea (as some might not yet be).
The best approach would be to put this logic into #Conditional annotations family (within a target configuration class) and make sure it will be processed after all user-defined configurations.

Related

Advice is not executing for custom annotation used at method level and it seems problem with poincut , any suggestions?

My advice in the following aspect is not getting executed:
package com.xxxx.logging.core.config;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class LogingAspectHandler {
#Autowired
CustomAppender lCustomAppender;
//#Pointcut("execution(#com.xxxx.logging.core.config.CustomLogged * *(..))")
#Pointcut("#annotation(com.xxxx.logging.core.config.CustomLogged)")
public void customAnnotated() {}
#Around("customAnnotated()")
public void customLogger(ProceedingJoinPoint lProceedingJointPoint) {
System.out.println("Get it "+lProceedingJointPoint.getSignature().getName());
lCustomAppender.logEntryLevelInfo(lProceedingJointPoint);
}
}
I have created a custom annotation named CustomLogged with Target(ElementType.METHOD) and using it at method level in a class within package com.xxxx.logging.core.config. Now I want a pointcut for all those methods which are annotated with #CustomLogged.
Please find the source code link
https://github.com/ssnarvariya/July_Projects.git
Thanks for the MCVE on GitHub. There are so many problems in your application, without the full project I would never have been able to find out what is wrong with it. Among other things, you have these problems:
Your aspect has not one, but two #Around advices. None of them proceeds to the original method. This has the effect that only the first advice gets triggered (whichever Spring AOP finds first), but never the second one. And also, your original method is never called. Therefore, both advices need to call lJoinPoint.proceed(). In #Before or #After advices, this is not necessary, but in #Around it is, hence the parameter type ProceedingJoinPoint (not a simple JoinPoint like in the other advice types).
Spring AOP only works on Spring-managed components or beans, but you instantiate your EVMLogger instance by simply calling a static factory method outside the control of Spring. Therefore, Spring AOP can never create an AOP proxy for this class and never intercept any method calls on it. The #CustomLogged annotation on method public void debug(Object lMsg) has no effect.
Also in EVMLogger, you save a Logger instance in a static variable, but actually if you want a different Logger for each class using EVMLogger, you cannot overwrite this static field. You have to make it an instance field and set it whenever you create a EVMLogger instance.
That you need multiple EVMLogger instances, also means that you cannot use a singleton bean but should use a prototype bean. There are several ways to instantiate a parametrised prototype bean dynamically. I am showing you how to auto-wire the application context into target class constructors needing the EVMLogger and there to get a bean instance from the context. This applies to two different classes in your example.
One more thing that looks weird to me, is that your aspect logs what EVMLogger does, i.e. a logger loggging infos about another logger. But maybe your aspect does something else in reality and logging was just for the sake of the MCVE. I am assuming it is so.
I created a pull request for your convenience. The key commits are:
https://github.com/ssnarvariya/July_Projects/pull/1/commits/c8ed9aab
https://github.com/ssnarvariya/July_Projects/pull/1/commits/597d8ccd
The console log when calling curl http://127.0.0.1:8080/getreq looks like this:
Inside logEntryLevelInfo for method ::showSampleData
Inside logEntryLevelInfo for method ::tryService
{"#timestamp":"2021-07-02T19:49:54.879+07:00","#version":"1","message":"Debug Msg...","logger_name":"org.ssn.app.config.CustomLogService","thread_name":"http-nio-8080-exec-1","level":"DEBUG","level_value":10000,"app_msg":{"mthd_name":"tryService","mtd_args":["Message1"]}}
Inside logCustomAnnotatedInfo for annotated method ::debug
{"#timestamp":"2021-07-02T19:49:54.879+07:00","#version":"1","message":"Custom Debug Msg...","logger_name":"org.ssn.app.controller.TestController1","thread_name":"http-nio-8080-exec-1","level":"DEBUG","level_value":10000,"app_msg":From Controller---}
{"#timestamp":"2021-07-02T19:49:54.879+07:00","#version":"1","message":"Debug Msg...","logger_name":"org.ssn.app.config.CustomLogService","thread_name":"http-nio-8080-exec-1","level":"DEBUG","level_value":10000,"app_msg":{"mthd_name":"showSampleData","mtd_args":[]}}
You see that
there are two distinct logger names CustomLogService and TestController1 and
both aspect advices fire now.

Spring create list of beans based on properties

In a simple Spring boot application I have my component like this:
#Data
#Component
public class GenericHandler {
private String path;
private HandlerType type;
}
And my properties might look like this:
my.handlers[0].path='/vol1/abc'
my.handlers[0].type='Single'
my.handlers[1].path='/vol1/dora'
my.handlers[1].type='MultiSequence'
I tried decorating with the GenericHandler-class with #ConfigurationProperties(prefix="my.handlers") and getting a list of all component instances in a service using
#Autowired
private List<GenericHandler> handlers;
But that created just one component, ignoring the property values at all.
How can I get one component instance per my.handlers property-entry?
You need a wrapper class
#Component
#ConfigurationProperties(prefix="my.handlers")
#Data
public class GenericHandlerWrapper {
private List<GenericHandler> handlers;
...
}
Then you can autowire the GenericHandlerWrapper
Update
As #zoolway pointed out in the comments, for the properties in the question to work as it is, #ConfigurationProperties(prefix="my.handlers") should be changed to #ConfigurationProperties(prefix="my")
That's not possible. What can be done is this:
#Data
#Component
public class GenericHandler {
private List<String> path;
private List<HandlerType> type;
}
I dealt with a similar issue in a different manner. I created a factory and an interface. The factory would hold different implementations of that interface In your case, GenericHandler would be your interface. Then you write any number of implementations of your interface and each implementation is declared as a Component. So, Spring will instantiate it as bean upon a startup (you might use #Lazy(false) to force the instantiation at startup) using some infrastructure that I wrote each bean of that interface will self-insert itself into its factory. Then at any part of your code in any bean, you can use the factory to access concrete implementation (base on your property "type" for example). The beauty is that you don't need to inject all the implementations in your bean at the time of writing but access needed implementation dynamically at run-time. I found this to be a useful pattern and created an infrastructure that does most of the work for you and published it as an Open Source library called MgntUtils. The detailed description of the idea (including reference to the library) could be found here. Also detailed explanation with examples of how to use it can be found in library Javadoc here. The library is available (with source code and Javadoc) as Maven artifacts and on the Github. Also a general article about the MgntUtils library could be found here

Interface to concrete class conditional instantiation in Spring

I have a Spring based Java application where a lot of classes use the following autowired interface.. they work off this interface at all places.
#Autowired
private IOperatingSystemManager m_operatingSystemManager;
Right now, there is only one implementation of the interface as follows:
#Component
public class WindowsManager implements IOperatingSystemManager
{
// Windows based shenanigans
}
And the application works as expected. Spring is happy. Everybody is happy.
Alright, not everybody...
So, I want to add another concrete implementation of IOperatingSystemManager ..
#Component
public class LinuxManager implements IOperatingSystemManager
{
// Linux based shenanigans
}
What we want is the auto wiring of IOperatingSystemManager conditionally based on a properties file setting. (say.. os=windows.. basically something that is an arbitrary string and cannot be derived from system properties etc. simply because this is a dummy example. the actual managers are not OS related.)
I don't want to change any of the classes who have autowired to the interface and are working off the interface. All I need is for Spring to look at some logic that will dictate the Autowiring of the variables and wire up the right concrete instance for:
#Autowired
IOperatingSystemManager m_operatingSystemManager
at all the gazillion places.
The documentation & web search talk about profiles, condition, bean factory, qualifiers etc.. but we don't want to use Profiles; and Qualifiers seem to be needing changes to all the interface variable annotations.
Factory methods look promising, but being new to Spring, couldn't find a crisp answer.
What is a simple and recommended way to achieve this?
Instead of scanning the WindowsManager class, create one concrete instance that implements the IOperatingSystemManager interface or another one, depending on the your logical conditions.
First, remove the #Component annotation from the WindowsManager class.
Then, create and scan this #Configuration class, which will act as a factory for your beans:
#Configuration
public class OperatingSystemManagerFactory {
#Bean
public IOperatingSystemManager getOperatingSystemManager() {
if ( /* some logic that evaluates to true if windows */ ) {
return new WindowsManager();
} else {
// Linux default option ;)
return new LinuxManager();
}
}
}
With this solution, you shouldn't need to update anyone of your classes that reference the IOperatingSystemManager interface.
I dont know which version of spring you are using but you have options for this
http://www.intertech.com/Blog/spring-4-conditional-bean-configuration/
Here, as you can see, you can create a bean based on a condition that you can decide. It actully gave your example, Windows and Linux :), so i believe thats what you are looking for.
Edit:
If you are using spring-boot, you have some other Conditional annotations
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html#boot-features-condition-annotations

How to use eclipse 4 DI in classes that are not attached to the application model?

I have created a OSGI service with declarative services to inject an object that implements an interface. If I inject the object in a class that is attached to the application model (handler,part,....) it is working fine. If I inject it in a class that is not attached to the application model it is always returning null.
Is it possible to use DI in classes that are not attached to the application model? I looked in the vogella tutorials but somehow I don't find a solution.
I know of three ways of how Eclipse 4 can inject objects in your classes:
During start-up the Eclipse runtime looks for relevant annotations in the classes it instantiates.
Objects injected in 1. are tracked and will be re-injected if changed.
Manually triggering injection using the ContextInjectionFactory and IEclipseContext.
What you want may be possible with the third option. Here is a code example:
ManipulateModelhandler man = new ManipulateModelhandler();
//inject the context into an object
//IEclipseContext iEclipseContext was injected into this class
ContextInjectionFactory.inject(man,iEclipseContext);
man.execute();
The problem is, however; that the IEclipseContext already needs to be injected into a class that can access the object that needs injection. Depending on the number of necessary injections, it might be more useful to use delegation instead (testability would be one argument).
#Inject
public void setFoo(Foo foo) {
//Bar is not attached to the e4 Application Model
bar.setFoo(foo);
}
Therefore, a better solution is probably using the #Creatable annotation.
Simply annotate your class, and give it a no-argument constructor.
#Creatable
public class Foo {
public Foo () {}
}
Using #Inject on that type as in the method above, will let Eclipse instantiate and inject it.
The disadvantage is that you cannot control the object creation anymore, as you would with ContextInjectionFactory.inject(..).
I refactored out some part of e(fx)clipse in order to achieve that. Have a look at this. Sorry for the shameless plug...

Configuration class - best practice with Guice

Background: I'm using Google Guice and so it's easier to pass through the configuration class but I think this is not the best way.
I have a configuration class which stores some paths:
class Configuration{
String getHomePath();
String getUserPath();
}
Also I have a class "a" which needs the "homepath" and a class "b" which needs the "userpath".
Is it better to pass the configuration class through the constructor of class a and b or only pass through the specific path?
If you're really using Guice correctly all your configuration like this should appear in modules' configure method. So:
Remove the configuration class.
Create annotation classes, probably called HomePath and UserPath.
Where class a uses getHomePath() replace that with a String field member named homePath.
Where class b uses getUserPath() replace that with a String field member named userPath.
Modify the class a and b constructors to be #Inject annotated (should already be) and take in a String parameter, respectively annotated with #HomePath and #UserPath and assign the String field member that injected value.
Create bindings in your module's configure method use .annotatedWith() which define correct values; if they're only available at run time, bind a provider.
E.G.
class a {
private String homePath;
#Inject
public a(#HomePath String homePath) {
this.homePath = homePath;
}
public String tellMeAboutHome() {
return "We live in a nice home called " + homePath;
}
}
class customModule extends AbstractModule {
public static final String userPath = "/home/rafael";
public void configure() {
bind(String.class).annotatedWith(HomePath.class).to("/home/");
bind(String.class).annotatedWith(UserPath.class).to(userPath);
}
}
If creating annotations is too much work for you, use the #Named annotation Guice ships with.
There's no single answer to your question, there are only options to choose from, based on your specific situation.
If you know your Configuration class is going to grow AND if it's likely for your A and B classes will use more from it, then pass the whole Configuration object to their constructors. NB: I know this is against the YAGNI principle but sometimes you may know you're gonna need it ;-)
Otherwise, you can consider using #Named injection of your paths so that you reduce A and B classes dependencies to their minimum, which is a good design practice.
The general rule is code to make the dependency graph (which classes know about or depend on other classes/ interfaces) as simple, regular and fixed as possible.
If not passing the Configuration class makes a or b have zero dependencies on on user-written classes, or is necessary to avoid a dependency loop, then use the individual path strings. Otherwise, if it makes more sense to say 'this class has access to configuration info, in a way that may change in the future', pass the class.
I'd avoid the singleton approach, especially if you already have Guice set up.

Categories

Resources