I've encountered a problem in my application and somehow i need to force one bean A to be initialized before another bean B. These beans are provided by external dependencies (different dependencies) so I cannot simply do it with #DependsOn annotation.
Is there any solution available?
I set up a simple (modular, maven) project and did experience no issues using #DependsOn annotation on an "external component". Only few things to accomplish:
Ensure A is in the (runtime) classpath of B.
Ensure B's application context #ComponentScans (also) A.
Find out/guess the "logical bean name" of A and use that value in your #DependsOn annotation.
..then #DependsOn works as expected (also on "external dependencies").
The sample uses spring-boot, but (I am sure) the configuration details are applicable to a "boot-less" environment/context.
And considering the javadoc of DependsOn:
Used infrequently in cases where a bean does not explicitly depend on another
through properties or constructor arguments, but rather depends on the side
effects of another bean's initialization.
#Autowired would give you the same effect (with similar effort), but with type
safety/without "name guessing" - I added it to the sample.
REWORK:
Regarding this from a third project (baz), you have again several options to force A initialization before B.
If A and B initialization is trivial (e.g. default constructor), you can proceed in your configuration/application like this:
import ...A;
import ...B;
...
#Configuration// #SpringBootApplication ..or idempotent
public class MyConfig {
...
#Bean("a")
public A a() {
return new A();
}
#Bean
#DependsOn("a")
public B b() {
return new B();
}
...
}
If initialization (of A and B) is not trivial, but you can refer to existing (external) Configurations, then this works (redefining autowired beans, adding dependsOn annotation):
#Configuration
#Import(value = {BConfig.class, AConfig.class})
class MyConfig {
#Bean("a")
public A a(#Autowired A a) {
return a;
}
#Bean
#DependsOn("a")
public B b(#Autowired B b) {
return b;
}
}
You rely on component scan, and redefine the beans (with dependsOn):
#Configuration
#ComponentScan(basePackageClasses = {B.class, A.class})
class MyConfig {
// same as 2.
}
...
If the external config (2., 3.) defines the beans with the same "name", then spring.main.allow-bean-definition-overriding property must be set to true (in application.properties or idempotent).
Related
Spring 5, Java 8
I have multiple configuration files, one of the configuration file has #Autowire dependency. it does not complain on run time and works fine but intellij warns can't find those beans.
wondering if thats ok to have #Autowire or #Inject in configuration class.
why i have it is b/c its my websocket configuration and my handlers need dependencies.
It's OK.
#Configuration indicates that a class declares #Beans which might require dependencies. #Configuration itself is meta-annotated with #Component and "therefore may also take advantage of #Autowired/#Inject like any regular #Component".
I would recommend that you pass dependencies as method parameters rather than inject them into fields. It keeps the configuration class clear and emphasises the required dependencies for each #Bean method.
I prefer
class C {
#Bean
public A a(B b) { new A(b); }
}
to
class C {
private final B b;
#Bean
public A a() { new A(b); }
}
Let's say I have a library (I cannot change it). There is a class Consumer that uses a spring component of class A.
#Component
public class Consumer{
#Autowired
private A a;
}
In my configuration class I want to define two beans of same class A depending on the profile.
#Configuration
public class Config {
#Bean
#Profile("!dev")
A a1(){
return new A();
}
#Bean
#Profile("dev")
A a2(){
return new A();
}
}
But when I start the app, I get next exception
Parameter 1 of constructor in sample.Consumer required a single bean, but 2 were found:
I can't get how to fix that. I've tried to create 2 separate configs for that with profile annotation and single bean there, but it also did not work.
Marking one bean #Primary also does not help.
Do you guys know how to fix that? Thanks!
UPD.
Let me make it more specific. That class is a part of dynamodb spring starter. Consumer - DynamoDBMapperFactory. My bean - DynamoDBMapperConfig. So I want to have 2 versions of DynamoDBMapperConfig in my app.
You need to stop scanning package where Consumer declared.
#Profile behaves differently if it's applied to the #Bean annotated method.
From Profile doc.:
Use distinct Java method names pointing to the same bean name if you'd like to define alternative beans with different profile conditions
This means, you should do:
#Configuration
public class Config {
#Bean("a")
#Profile("!dev")
A a1(){
return new A();
}
#Bean("a")
#Profile("dev")
A a2(){
return new A();
}
}
Note the same bean names.
My code is working but I'm not able to figure from where I'm getting dependency injection. As Spring documentation mentions nothing about default dependency injection.
package org.stackoverflow;
#Component
public class A {
private final B b;
public A(B b) {
this.b = b;
}
}
package org.segfault;
#Configuration
Public class Config {
#Bean
public B b(){ return new B(); }
}
As in above code component scan is running on the path com.stackoverflow and imported org.segfault class config. But as you can see there no constructor injection in class A.
I suspect it must be documented somewhere. But I'm not able to find out. Anyway, it's working :)
Can someone help with documentation or Is there anything that I'm missing?
The Spring documentation, chapter 17. Spring Beans and Dependency Injection says:
If a bean has one constructor, you can omit the #Autowired
Since Spring 4.3.*, specifying the #Autowire annotation above the constructor isn't needed anymore, provided that there is a single, non-private constructor for the class.
6.1 Core Container Improvements (news)
It is no longer necessary to specify the #Autowired annotation if the target bean only defines one constructor.
Why does this work:
#Configuration
public class MyConfig {
#Bean
public A getA() {
return new A();
}
#Bean // <-- Shouldn't I need #Autowired here?
public B getB(A a) {
return new B(a);
}
}
Thanks!
#Autowire lets you inject beans from context to "outside world" where outside world is your application.
Since with #Configuration classes you are within "context world ", there is no need to explicitly autowire (lookup bean from context).
Think of analogy like when accessing method from a given instance. While you are within the instance scope there is no need to write this to access instance method, but outside world would have to use instance reference.
Edit
When you write #Configuration class, you are specifying meta data for beans which will be created by IOC.
#Autowire annotation on the other hand lets you inject initialized beans, not meta-data, in application. So, there is no need for explicit injection because you are not working with Beans when inside Configuration class.
Hi Jan your question is marked as answered over 4 years ago but I've found a better source:
https://www.logicbig.com/tutorials/spring-framework/spring-core/javaconfig-methods-inter-dependency.html
here's another article with the same idea:
https://dzone.com/articles/spring-configuration-and, it also states that such usage is not well documented which I found true. (?)
so basically if beanA's initialization depends on beanB, spring will wire them without explicit #Autowired annotation as long as you declare these two beans in the application context (i.e. #Configuartion class).
A class with #Configuration annotation is where you're defining your beans for the context. But a spring bean should define its own dependencies. Four your case B class should be defining it's own dependencies in class definition. For example if your B class depends on your A class than it should be like below:
public class B
{
#Autowired
A aInstance;
public A getA()
{
return aInstance;
}
public void setA(A a)
{
this.aInstance = a;
}
}
In above case while spring is building its context it looks for the bean which's type is A which is also defined as a Bean at your configuration class and Autowires it to B at runtime so that B can use it when required.
On a project I'm working on we have some old dependencies that define their own spring beans but need to be initialized from the main application. These beans are all constructed using spring profiles, i.e. "default" for production code and "test" for test code. We want to move away from using spring profiles, instead simply using #import to explicitly wire up our context.
The idea is to encapsulate all these old dependencies so that no other components need to care about spring profiles. Thus, from a test`s point of view, the application context setup can be described as follows:
#ContextConfiguration(classes = {TestContext.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class MyTest {
//tests
}
TestContext further directs to two classes, one of which encapsulates the old dependencies:
#Configuration
#Import(value = {OldComponents.class, NewComponents.class})
public class TestContext {
//common spring context
}
To encapsulate the old components` need for profiles, the OldComponents.class looks as follows:
#Configuration
#Import(value = {OldContext1.class, OldContext2.class})
public class OldComponents {
static {
System.setProperty("spring.profiles.active", "test");
}
}
The problem here is that the static block does not appear to be executed in time. When running mvn clean install, the test gets an IllegalStateException because the ApplicationContext could not be loaded. I have verified that the static block gets executed, but it would appear that OldContext1 and OldContext2 (which are profile dependent) are already loaded at this time, which means it is too late.
The frustrating thing is that IntelliJ runs the tests just fine this way. Maven, however, does not. Is there a way to force these profiles while keeping it encapsulated? I've tried creating an intermediary context class, but it didn't solve the problem.
If we use the annotation #ActiveProfiles on the test class, it runs just fine but this kind of defeats the purpose. Naturally, we want to achieve the same in production and this means that if we cannot encapsulate the need for profiles, it needs to be configured in the web.xml.
If your configuration classes inherits of AbstractApplicationContext you can call:
getEnvironment().setActiveProfiles("your_profile");
For example:
public class TestContext extends AnnotationConfigWebApplicationContext {
public TestContext () {
getEnvironment().setActiveProfiles("test");
refresh();
}
}
Hope it helps.
It definietly seems that OldContext1 and OldContext2 are being class-loaded and initialized before the static block in OldComponents is executed.
Whilst I can't explain why there is a difference between your IDE and Maven (to do so would require some in-depth knowledge of some, if not all all, of : spring 3.x context initialization, maven surefire plugin, SpringJunit4ClassRunner and the internal IntelliJ test runner), can I recommend to try this?
#Configuration
#Import(value = {UseTestProfile.class, OldContext1.class, OldContext2.class})
public class OldComponents {
// moved the System.setProperty call to UseTestProfile.class
}
and
#Configuration
public class UseTestProfile {
static {
System.setProperty("spring.profiles.active", "test");
}
}
If I am understanding your problem correctly, class UseTestProfile should be loaded first (you might want to investigate a way to guarantee this?) and the other two classes in the import list should have the system setting they need to initialize properly.
Hope this helps...
You need make sure, environment takes effect at first.This is how I do:
#Component
public class ScheduledIni {
#Autowired
private Environment env;
#PostConstruct
public void inilizetion() {
String mechineName = env.getProperty("MACHINE_NAME");
if ("test".equals(mechineName) || "production".equals(mechineName) {
System.setProperty("spring.profiles.default", "Scheduled");
System.setProperty("spring.profiles.active", "Scheduled");
}
}
}
In scheduler add annotation Prodile and DependsOn to make it work.
#DependsOn("scheduledIni")
#Profile(value = { "Scheduled" })
#Component
Use #profile annotation in the class to load the configuration like below
#Configuration
#Profile("test")
public class UseTestProfile {
}
and set the value for the property spring.profiles.active either in property file or as a runtime argument