Inside Java code:
#Value("${myVar}") private String myVar;
Inside Groovy code:
import org.springframework.beans.factory.annotation.Value;
import groovy.transform.Field;
#Field #Value('${myVar}') String myVar;
return myVar;
returns null.
How to get myVar from Groovy script, assuming the Beans and myVar are not passed in?
If I understand correctly, you want Spring context to be available during your Groovy script execution and perform the usual injection on your script properties.
Groovy scripts are compiled to Groovy classes, and so you should be able to simply use beanFactory.autowireBean(this). However, to obtain a reference to a BeanFactory in the first place, you need to manually start a Spring context like so:
new ClassPathXmlApplicationContext('path/to/applicationContext.xml').withCloseable { context ->
context.autowireCapableBeanFactory.autowireBean(this)
... //the rest of your script goes here
}
(You might need to adjust the new ClassPathXmlApplicationContext("path/to/applicationContext.xml") part to suit your needs, depending on what method you prefer to use to bootstrap your Spring context; also, if the script is part of a larger codebase, you might want to craft a smaller application context than for the rest of the app, to save startup time and avoid the usual side effects. Depends on the use case, though).
The only part I'm unsure of is whether #Field retains the original annotations when transforming a variable to a field, but if you're not getting any errors, I would assume that to be the case.
In case annotations are not retained, you can always declare a simple annotated POJO bean acting as a container for the injectable properties and call def myPojo = new Pojo(); context.getBeanFactory()).autowireBean(myPojo) instead.
You need to make sure the groovy script is managed by spring, you can either add the #Service or #Component annotations on the class. see example below
#Service
class Demo {
public #Value('${valueA}') String valueA
String showValue() {
return valueA
}
}
Related
Hi I'm new to Java and Spring, and currently have a situation that is similar to this:
In Message.java
public class Message {
final String text;
public Message(#Value("${message.text}") String text) {
this.text = text;
}
}
In application.properties:
message.text = "This is some text."
Now, I have a test file where I want to write a test that checks the value of the String text, akin to something like
assertEquals(text, "This is some text.");
How would I go about doing this? It seems I can't manually invoke the constructor by doing new Message("...") because that overrides the #Value injection.
The #Value annotation here is an instruction to the Spring container, so if you're wanting to test that it operates as expected, the only way to do that is to use SpringRunner or equivalent to actually start a context and feed it the value in question. Typically, this annotation isn't directly explicitly tested; rather, when the bean is covered as part of a medium-scale test like #SpringBootTest, its value will be set somewhere and is expected to be used in some business manner, the effect of which is tested.
For example, let's say that in my yml file I had a variable called indicator. And based on what the indicator variable's value was I want the code to do something different. How would I access the yml variable in the regular code and use it accordingly?
You can use this:
#Value("${your.path.yml.string}")
private String x;
YML:
your:
path:
yml:
string: hello
x will be "hello"
You need to use Spring Expression Language which says we should write it as
#Value("${spring.application.name}")
private String appName;
For Default value if key is not present in yaml/yml or properties file
#Value("${spring.application.name: defaultValue}")
private String appName;
The last way you can fetch value is using environment object
#Autowired
private Environment environment;
String appName = environment.get("spring.application.name");
You can add #Value annotation to any field in your beans.
#Value("$(path.to.your.variable)")
String myString;
Works with constructors as well.
public MyClass(#Value("$(path.to.your.variable)") String myString) {
You can use #Value on fields or parameters to assign the property to some variable.
Property example:
#Value("${indicator}")
private String indicator
Parameter example:
private void someMethod(#Value("${indicator}") String indicator) {
...
}
Then you can use indicator as you want.
Note: the class where you use #Value should be a Spring Component
With Spring-Boot, you have the file application.yml automatically provided for you. What you can do is adding a property in this file, for instance:
my.properties: someValue
Then, in one of your Spring Bean (either define with #Component or #Bean) you can retrieve this value using the annotation #Value. Then, do whatever you want with this variable.
For instance:
#Component
public class MyClass {
#Value("${my.properties"}
private String myProp; // will get "someValue" injected.
...
// Just use it in a method
public boolean myMethod() {
if(myProp.equals("someValue") {
// do something
} else {
// do something else
}
}
}
The best way to do this is not to have a tight coupling between Spring and your "normal" code at all, but instead to use the normal Java features like constructors along with Spring #Bean methods:
class MyService {
final String indicatorName;
MyService(String indicatorName) {
this.indicatorName = indicatorName;
}
}
... in your configuration class...
#Bean
MyService myService(#Value("indicator.name") String indicatorName) {
return new MyService(indicatorName);
}
Two notes for Spring Boot specifically:
The #ConfigurationProperties feature allows you to map properties onto structured Java data classes and is typically cleaner than using #Value by hand.
Always namespace properties that you define yourself to avoid future collisions with other libraries, so instead of indicator.name use company.project.indicator.name. I recommend looking at DataSourceProperties in Boot to see an example of how to set all this up.
More broadly, though, when you say that you want the code to "do something different", it sounds like the better option might be to have different classes that get activated under different circumstances. Both Spring profiles and Spring Boot auto-configuration help to do this.
The problem statement can be re-defined as Configuration Management in Java.
You should have a component like ConfigManager that gets instantiated as part of your application start up. That component will read a properties file, a yaml in your use case. Subsequent app logic will fetch these values from the ConfigManager exposed as simple key/value pairs.
All that is left for you to identify how to read and parse values from yaml file. This is already answered here:
Parse a YAML file
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.
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...
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.