I have this configuration class in two different kotlin projects.
In one it works just fine. In the other one, I get an error and I am asked to make the class open, and when I run the program, the bean cannot be found.
I am really confused as the same configuration seems to be working fine in a different project. Could you please suggest a way that would allow me to avoid making the class open, as I dont see the reason for doing so since in the other project I did not have to, or a reason why the bean cannot be found?
#Configuration
#ComponentScan(value = ["com.bank.manager"])
#PropertySources(PropertySource("classpath:application.yml"),
PropertySource(value = ["file:\${bank.target.config}"]))
class BankInstanceConfig(#Autowired private val env: Environment) {
#Bean fun bankInstance(): List<String> {
return env.getRequiredProperty("bank-instance").toString().split("#")
}
}
Then on class level I define the bean like this:
#Service
class BankDiscoveryServiceImpl(#Autowired bankInstance: BankInstanceConfig) {}
Related
I was wondering how to do dependency injection in the most effective way inside my code.
I have this code:
#Configuration
public class SomeName {
#Autowired
private Other other;
#Bean
public void method() {
other.someMethod();
// some code
}
}
Can this code be changed into the following code(other will be used only inside this function)?
#Configuration
public class SomeName {
#Bean
public void method(Other other) {
other.someMethod();
// some code
}
}
You should avoid #Autowired if possible and inject using a constructor or method.
Starting with Java 9 and java modules (project jigsaw) there are some strict rules that make it harder for your framework to change the values of a private field.
What Spring is doing in the first example is essentially that - it breaks encapsulation to change the value of a private value. (There is a way to overcome this with "opens" directive in module-info..)
You are also becoming dependent on the framework you are using and your code becomes harder to test compared to when using a simple setter.
You are also not explicitly declaring that your class depends on another class since I can easily instantiate it and "Other" will be null.
Some resources:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-scanning-autodetection (search for jigsaw)
https://blog.marcnuri.com/field-injection-is-not-recommended/
PS: You are probably missing #Configuration on your class
I upgraded spring from version 2.1.1 to 2.2.0 .
Since then I'm facing the following error when I start my app :
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'ParentService' available: expected single matching bean but found 2: MasterService,SlaveService .
ParentService is an interface :
public interface ParentService{
..
}
MasterService :
#Service
#MasterProfile
public class MasterService implements ParentService{
.....
}
SlaveService :
#Service
#SlaveProfile
public class SlaveService implements ParentService{
.....
}
MasterProfile annotation :
#Profile("MASTER")
public #interface MasterProfile {
}
Slave Profile :
#Profile("SLAVE")
public #interface SlaveProfile{
}
I'm passing to my app the profile with the following flag :
-Dspring.profiles.include=MASTER
According to Spring 2.2 release notes, they have done some changes and forks are enabled by default in maven. As a result the only way to pass params is with the parameter -Dspring-boot.run.jvmArguments . I used -Dspring-boot.run.jvmArguments=-Dspring.profiles.include=MASTER but it still fails..
Passing a profile as a parameter depends on how you run your app. Be careful, the doc you mentioned is referring to the maven spring-boot plugin.
With maven plugin : mvn spring-boot:run -Dspring-boot.run.jvmArguments=-Dspring.profiles.include=MASTER
Classic java app : java -Dspring.profiles.include=MASTER -jar ./myapp.jar
In both cmd line, you can pass more than one parameter, if separated by a ,. See the documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-profile-specific-properties
Since the upgrade, you now have to define your custom profile annotation like this :
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME) // Only this one is really needed
#Profile("SLAVE")
public #interface SlaveProfile {
}
Explaination:
In java, an annotation has a RetentionPolicy, which is similar to a scope. (See this: https://docs.oracle.com/javase/7/docs/api/java/lang/annotation/RetentionPolicy.html).
Without any RetentionPolicy set, the default behavior is an annotation not visible for the JVM (i.e at runtime).
When you want to run your application, you first compile it, which implies converting your .java files into .class file. Your class is only a bunch of byte code, converting your human readable file into a computer language.
Then, when Spring is loading the ApplicationContext, what it does under the hood, among many other things, is reading your .class files. During this process (see class name: org.springframework.asm.ClassReader) Spring loads the annotations that you declare. With what I've said above, during the Runtime, you end up with "two kinds" of annotations :
InvisibleAnnotation: #Retention(RetentionPolicy.COMPILE)
VisibleAnnotation: #Retention(RetentionPolicy.RUNTIME)
To conclude and understand why it was working before:
Spring-boot 2.1.0uses spring-core-5.1.2, which interprets at runtime the visible and invisible annotations, which explain why your #SlaveProfile and #MasterProfile have the expected behaviour.
Spring-boot 2.2.0uses spring-core-5.2.0, which interprets at runtime ONLY the visible annotations, which explain why your #SlaveProfile and #MasterProfile haven't the expected behaviour.
Let's say that Spring "silently" fixed a bug that was reading Invisible Annotation when they shouldn't, but didn't mention it.
Hope it helps!
Adding #Profile will not stop the bean from being instantiated. This is causing the exception. Add #Primary to any beans that the application should not default to.
Ex, add #Primary to the MasterProfile bean.
I´m setting up a Spring Boot application where certain configurations are being read from my application.yaml-file. I´ve done this a few times before and it works well, but I wondered whether there is a better way to access this configuration during runtime or whether I´m creating possible issues by not following some best practice.
Right now the class that extracts the configuration is simply defined as a Component like this:
#Component
#EnableConfigurationProperties
#ConfigurationProperties("myPrefix")
public class MyExternalConfiguration{
private HashMap<String, Boolean> entries= new HashMap<String, Boolean>();
public Boolean getConfigurationForKey(String key) {
return this.entries.get(key);
}
}
And then autowired to several other classes that need to access this configuration like this:
#Component
public class MyClass{
#Autowired
private MyExternalConfiguration myExternalConfiguration;
public void doSomething(){
//...
Boolean someEntry = myExternalConfiguration.getConfigurationForKey(someKey);
}
}
Now, this does work just fine. It´s just that I have seen examples of where configurations like this are handled as a singleton for example (although not in a Spring-Boot environment). I would just like to ask, whether there is some commonly accepted way to access external configurations or whether you see an issue with the way i access it in my project.
Thank you in advance!
There is a whole chapter about configuration in the Spring Boot Reference Manual:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config
Simply said there are two options to access configuration:
With the Value annotation:
#Value("${name}")
private String name;
Or typesafe with a configuration class:
#ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
So there is no need to read the configuration file by your own.
So, this is something of a follow-on of this question. My current code looks something like this:
#Configuration
#EnableAutoConfiguration
#ComponentScan(basePackages = {"base.pkg.name"})
public class MyApp implements ServletContextAware {
private ThingDAO beanThingDAO = null;
public MyApp() {
// Lots of stuff goes here.
// no reference to servletContext, though
// beanThing gets initialized, and mostly populated.
}
#Bean publicD ThingDAO getBeanThingDAO() { return beanThingDAO; }
public void setServletContext(ServletContext servletContext) {
// all references to servletContext go here, including the
// bit where we call the appropriate setters in beanThingDAO
{
}
The problem is, it's not working. Specifically, my understanding was that setServletContext was supposed to be called by various forms of Spring Magic at some point during the startup process, but (as revealed by System.out.println()) it never gets called. I'm trying to finish up the first stage of a major bunch of refactoring, and for the moment it is of notable value to me to be able to handle the access to servletContext entirely inside of the #Configuration file. I'm not looking for an answer that will tell me that I should put it in the controllers. I'm looking for an answer that will either tell me how to get it working inside of the #Configuration file, or explain why that won't work, and what I can do about it.
I just ran into a very similar issue and while I'm not positive it's exactly the same problem I thought I'd record my solution in case it's helpful to others.
In my case I had a single #Configuration class for my spring boot application that implemented both ServletContextAware and WebMvcConfigurer.
In the end it turns out that Spring Boot has a bug (or at least undocumented restraint) that ServletContextAware.setServletContext() will never be called if you also implement WebMvcConfigurer on the same class. The solution was simply to split out a separate #Configuration class to implement ServletContextAware.
Here's a simple project I found that demonstrates and explains more what the problem was for me:
https://github.com/dvntucker/boot-issue-sample
The OP doesn't show that the bean in question was implementing both of these, but given the OP is using simplified example code I thought maybe the fact that the asker could have been implementing both interfaces in his actual code and might have omitted the second interface.
Well, I have an answer. It's not one I'm particularly happy with, so I won't be accepting it, but if someone with my same problem stumbles across this question, I want to at least give them the benefit of my experience.
For some reason, the ServletContextAware automatic call simply doesn't work under those circumstances. It works for pretty much every other component, though. I created a kludge class that looks something like this:
// This class's only purpose is to act as a kludge to in some way get
// around the fact that ServletContextAware doesn't seem to work on MyApp.
// none of the *other* spring boot ways of getting the servlet context into a
// file seem to work either.
#Component
public class ServletContextSetter implements ServletContextAware {
private MyApp app;
public ServletContextSetter(MyApp app) {
this.app = app;
}
#Override
public void setServletContext(ServletContext servletContext) {
app.setServletContext(servletContext);
}
}
Does the job. I don't like it, and I will be rebuilding things later to make it unnecessary so I can take it out, but it does work. I'm going to hold the checkmark, though, in case anyone can tell me either how to make it work entirely inside the #Configuration - decorated file, or why it doesn't work there.
Note that the #Component decorator is important, here. Won't work without it.
I need to create tests for some class. This class in main project (src/main/java/..) is injected easily into another classes, since I have custom ResourceConfig class which declares which packages have to be scanned to seek for service classes.
Now I created test directories (in src/test/java/..) and created a class, something like:
public class TheMentionedClassIntegrationTest {
#Inject
private TheMentionedClass theMentionedClass ;
#Test
public void testProcessMethod() {
assertNotNull(theMentionedClass);
}
}
But the problem is that whatever I do the class is always null. In another tests in the project I was using JerseyTest class. So I tried to do the same here, extend TheMentionedClassIntegrationTest with JerseyTest, override configure method, create my private ResourceConfig class which registers Binder (default for whole project) and register TheMentionedClassIntegrationTest as well.
It didnt work. I did many different attempts but none of them were successfull. I think working with HK2 is extremly difficult, there is no good documentation or so..
Do you guys have an idea how to inject TheMentionedClass into the test class? Maybe my approach is wrong?
Thanks!
The easiest thing to do is to just create the ServiceLocator and use it to inject the test class, as see here. For example
public class TheMentionedClassIntegrationTest {
#Inject
private TheMentionedClass theMentionedClass;
#Before
public void setUp() {
ServiceLocator locator = ServiceLocatorUtilities.bind(new YourBinder());
locator.inject(this);
}
#Test
public void testProcessMethod() {
assertNotNull(theMentionedClass);
}
}
You could alternatively use (make) a JUnit runner, as seen here.
For some other ideas, you might want to check out the tests for the hk2-testing, and all of its containing projects for some use case examples.