I run into a code that has some strange #Qualifier behavior. It is not what I would expect.
Let's consider the following code snippet:
#SpringBootApplication
class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
Foo foo1() {
return new Foo("foo1");
}
#Bean
Foo foo2() {
return new Foo("foo2");
}
static class Foo {
private final String name;
public Foo(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
#Component
static class Bar {
private Foo fooOne;
private Foo fooTwo;
#Autowired
#Qualifier("foo3")
void setFoo(Foo foo1, Foo foo2) {
this.fooOne = foo1;
this.fooTwo = foo2;
}
#PostConstruct
void printFoo() {
System.out.println(fooOne.getName() + fooTwo.getName());
}
}
}
The above code doesn't work because there is no Foo bean named foo3. This is exactly what I expect. Now let's change Foo class definition to include a #Qualifier:
#Qualifier("foo3")
static class Foo {
// remaining code not changed..
With that change, the #Qualifier on Bar::setFoo method seems to be ignored and instances foo1 and foo2 are injected. No bean with id foo3 is present in the context. I have hard time understanding the mechanism applied here. Can anyone explain it?
By putting the qualifier on class Foo:
#Qualifier("foo3")
static class Foo {
...
}
Every bean instantiated as a class Foo will have the given qualifier. You are correct that are is not a single bean with id foo3, however there are two beans present with a qualifier foo3.
More information on the topic can be found here.
To inject both beans, remove the qualifier on the static class and add a qualifier on both parameters:
#Autowired
void setFoo(#Qualifier("foo1") Foo foo1, #Qualifier("foo2") Foo foo2) {
To play around with beans in an application context, I'd suggest injecting the application context and retrieving the beans to inspect them. For instance:
#SpringBootApplication
class DemoApplication {
#Autowired
ApplicationContext context
#PostConstruct
public void init() {
// Beans by qualifier
BeanFactoryAnnotationUtils.qualifiedBeanOfType(context.getBeanFactory(), YourClass.class, "foo3");
}
...
}
Related
I'm new about spring and my purpose is crea two object and one object print a string and the second object ( that has dipendency with first object take the value e prints an output). It is an exercise:
Helloworld.java is this:
#Component
public class HelloWorld {
private String message="";
public void setMessage(String message) {
this.message = message;
}
#Bean
public String getMessage() {
return message;
}
}
Person class does this:
#Component
public class Person {
private String person="Jessy";
public void setPerson(String person) {
this.person = person;
}
// I want receive hellowordbean and I want print the first message and join the two message
#Autowired
public String printMessage(HelloWorld message) {
return message.getMessage()+" - "+this.person;
}
}
this is main class:
#ComponentScan(value={"com.example.bean"})
public class TestApplication {
public static void main(String[] args) {
//SpringApplication.run(TestApplication.class, args);
ApplicationContext context = SpringApplication.run(HelloWorld.class, args);
HelloWorld word=context.getBean(HelloWorld.class);
word.setMessage("hi world");
ApplicationContext personContext = SpringApplication.run(Person.class, args);
Person person=personContext.getBean(Person.class);
person.setPerson("Jimbey");
System.out.println(person.printMessage(word));
}
}
I play run and I obtain
Description:
Parameter 0 of method printMessage in com.example.bean.Person required
a bean of type 'com.example.bean.HelloWorld' that could not be found.
Action:
Consider defining a bean of type 'com.example.bean.HelloWorld' in your
configur
I don't know how resolve this problem, anyone can help me?
There are many things wrong :( I recommend you read a tutorial about how to build a hello world app with Spring.
Remove #Autowired annotation from printMessage method in Person class. Here you can see how to use #Autowired annotation https://www.baeldung.com/spring-autowire.
Use #SpringBootApplication annotation on TestApplication class. It creates all beans annotated with #Component annotation.
I have a test utility for with I need to have a fresh instance per test method (to prevent that state leaks between tests). So far, I was using the scope "prototype", but now I want to be able to wire the utility into another test utility, and the wired instances shall be the same per test.
This appears to be a standard problem, so I was wondering if there is a "test method" scope or something similar?
This is the structure of the test class and test utilities:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTest {
#Autowired
private TestDriver driver;
#Autowired
private TestStateProvider state;
// ... state
// ... methods
}
#Component
#Scope("prototype") // not right because MyTest and TestStateProvider get separate instances
public class TestDriver {
// ...
}
#Component
public class TestStateProvider {
#Autowired
private TestDriver driver;
// ...
}
I'm aware that I could use #Scope("singleton") and #DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) but this refreshes more than I need – a new TestDriver instance for each test would be enough. Also, this approach is error-prone because all tests using the TestDriver would need to know that they also need the #DirtiesContext annotation. So I'm looking for a better solution.
It is actually pretty easy to implement a testMethod scope:
public class TestMethodScope implements Scope {
public static final String NAME = "testMethod";
private Map<String, Object> scopedObjects = new HashMap<>();
private Map<String, Runnable> destructionCallbacks = new HashMap<>();
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!scopedObjects.containsKey(name)) {
scopedObjects.put(name, objectFactory.getObject());
}
return scopedObjects.get(name);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
destructionCallbacks.put(name, callback);
}
#Override
public Object remove(String name) {
throw new UnsupportedOperationException();
}
#Override
public String getConversationId() {
return null;
}
#Override
public Object resolveContextualObject(String key) {
return null;
}
public static class TestExecutionListener implements org.springframework.test.context.TestExecutionListener {
#Override
public void afterTestMethod(TestContext testContext) throws Exception {
ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) testContext
.getApplicationContext();
TestMethodScope scope = (TestMethodScope) applicationContext.getBeanFactory().getRegisteredScope(NAME);
scope.destructionCallbacks.values().forEach(callback -> callback.run());
scope.destructionCallbacks.clear();
scope.scopedObjects.clear();
}
}
#Component
public static class ScopeRegistration implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
factory.registerScope(NAME, new TestMethodScope());
}
}
}
Just register the test execution listener, and there will be one instance per test of all #Scope("testMethod") annotated types:
#RunWith(SpringRunner.class)
#SpringBootTest
#TestExecutionListeners(listeners = TestMethodScope.TestExecutionListener.class,
mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
public class MyTest {
#Autowired
// ... types annotated with #Scope("testMethod")
}
I ran into the same problem some time ago and came to this solution:
Use Mocks
I wrote some methods to create specific mockito settings to add behavior to each mock.
So create a TestConfiguration class with following methods and bean definition.
private MockSettings createResetAfterMockSettings() {
return MockReset.withSettings(MockReset.AFTER);
}
private <T> T mockClass(Class<T> classToMock) {
return mock(classToMock, createResetAfterMockSettings());
}
and your bean definition will look like:
#Bean
public TestDriver testDriver() {
return mockClass(TestDriver .class);
}
MockReset.AFTER is used to reset the mock after the test method is run.
And finally add a TestExecutionListeners to your Test class:
#TestExecutionListeners({ResetMocksTestExecutionListener.class})
#Component
class MultiProvider {
public Foo getFoo();
public Bar getBar();
}
#Component
class FooConsumer {
FooConsumer(Foo f);
}
Can I have MultiProvider.getFoo() autowired into the FooConsumer constructor..
without making Foo a bean itself (for example, because Spring should not destroy it, since that is MultiProviders responsibility)
and without introducing a dependency from FooConsumer to MultiProvider (or any other class)?
You can achieve this simply by annotating getFoo() method in MultiProvider by #Bean
#Component
class MultiProvider {
#Bean(destroyMethodName="cleanup") // HERE IS THE TRICK
public Foo getFoo();
public Bar getBar();
}
#Component
class FooConsumer {
FooConsumer(Foo f);
}
if the problem comes from the point that spring can not properly destroy it you can include the logic inside cleanup method declared while annotating by #Bean
public class Foo {
public void cleanup() {
// destruction logic
}
}
Note that #component and #configurable are more or less the same with
some subtle differences but in your case you can use #component if you don't
want to change it. More Info
Spring can only autowire declared beans, a possible workaround can be something like the following:
#Component
class FooConsumer {
private final Foo foo;
FooConsumer(MultiProvider multiProvider) {
// MultiProvider will be autowired by spring - constructor injection
this.foo = multiProvider.getFoo();
}
}
You can include them in your Configuration.
#Configuration
class MyConfig {
#Bean
public MultiProvider getMP() {
return new MultiProvider() ;
}
#Bean
public Foo getFoo() {
return getMP(). getFoo();
}
}
Not sure if that violates your 'not a Bean itself' rule.
I have #Autowired service which has to be used from within a static method. I know this is wrong but I cannot change the current design as it would require a lot of work, so I need some simple hack for that. I can't change randomMethod() to be non-static and I need to use this autowired bean. Any clues how to do that?
#Service
public class Foo {
public int doStuff() {
return 1;
}
}
public class Boo {
#Autowired
Foo foo;
public static void randomMethod() {
foo.doStuff();
}
}
You can do this by following one of the solutions:
Using constructor #Autowired
This approach will construct the bean requiring some beans as constructor parameters. Within the constructor code you set the static field with the value got as parameter for constructor execution. Sample:
#Component
public class Boo {
private static Foo foo;
#Autowired
public Boo(Foo foo) {
Boo.foo = foo;
}
public static void randomMethod() {
foo.doStuff();
}
}
Using #PostConstruct to hand value over to static field
The idea here is to hand over a bean to a static field after bean is configured by spring.
#Component
public class Boo {
private static Foo foo;
#Autowired
private Foo tFoo;
#PostConstruct
public void init() {
Boo.foo = tFoo;
}
public static void randomMethod() {
foo.doStuff();
}
}
You have to workaround this via static application context accessor approach:
#Component
public class StaticContextAccessor {
private static StaticContextAccessor instance;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void registerInstance() {
instance = this;
}
public static <T> T getBean(Class<T> clazz) {
return instance.applicationContext.getBean(clazz);
}
}
Then you can access bean instances in a static manner.
public class Boo {
public static void randomMethod() {
StaticContextAccessor.getBean(Foo.class).doStuff();
}
}
What you can do is #Autowired a setter method and have it set a new static field.
public class Boo {
#Autowired
Foo foo;
static Foo staticFoo;
#Autowired
public void setStaticFoo(Foo foo) {
Boo.staticFoo = foo;
}
public static void randomMethod() {
staticFoo.doStuff();
}
}
When the bean gets processed, Spring will inject a Foo implementation instance into the instance field foo. It will then also inject the same Foo instance into the setStaticFoo() argument list, which will be used to set the static field.
This is a terrible workaround and will fail if you try to use randomMethod() before Spring has processed an instance of Boo.
The easiest way to create a static context is naturally, when the application starts up. This will prevent the need for an unnatural implementation with an additional class.
#SpringBootApplication
public class MyApplication {
private static ApplicationContext appContext;
public static void main(String[] args) {
appContext = SpringApplication.run(MyApplication.class, args);
}
public static ApplicationContext getAppContext() {
return appContext;
}
}
Then, anywhere you need to access a bean statically, you can use the ApplicationContext to get the instance of the class.
public class Boo {
public static void randomMethod() {
MyApplication.getAppContext()
.getBean(Foo.class).doStuff();
}
}
Regards..
It sucks but you can get the bean by using the ApplicationContextAware interface. Something like :
public class Boo implements ApplicationContextAware {
private static ApplicationContext appContext;
#Autowired
Foo foo;
public static void randomMethod() {
Foo fooInstance = appContext.getBean(Foo.class);
fooInstance.doStuff();
}
#Override
public void setApplicationContext(ApplicationContext appContext) {
Boo.appContext = appContext;
}
}
This builds upon #Pavel's answer, to solve the possibility of Spring context not being initialized when accessing from the static getBean method:
#Component
public class Spring {
private static final Logger LOG = LoggerFactory.getLogger (Spring.class);
private static Spring spring;
#Autowired
private ApplicationContext context;
#PostConstruct
public void registerInstance () {
spring = this;
}
private Spring (ApplicationContext context) {
this.context = context;
}
private static synchronized void initContext () {
if (spring == null) {
LOG.info ("Initializing Spring Context...");
ApplicationContext context = new AnnotationConfigApplicationContext (io.zeniq.spring.BaseConfig.class);
spring = new Spring (context);
}
}
public static <T> T getBean(String name, Class<T> className) throws BeansException {
initContext();
return spring.context.getBean(name, className);
}
public static <T> T getBean(Class<T> className) throws BeansException {
initContext();
return spring.context.getBean(className);
}
public static AutowireCapableBeanFactory getBeanFactory() throws IllegalStateException {
initContext();
return spring.context.getAutowireCapableBeanFactory ();
}
}
The important piece here is the initContext method. It ensures that the context will always get initialized. But, do note that initContext will be a point of contention in your code as it is synchronized. If your application is heavily parallelized (for eg: the backend of a high traffic site), this might not be a good solution for you.
Use AppContext. Make sure you create a bean in your context file.
private final static Foo foo = AppContext.getApplicationContext().getBean(Foo.class);
public static void randomMethod() {
foo.doStuff();
}
For a simple POJO:
#Component
public class Foo
{
private final String string;
public Foo()
{
this("Secondary ComponentScan??");
}
public Foo(String string)
{
this.string = string;
}
#Override
public String toString()
{
return string;
}
}
and this configuration
#Configuration
#ComponentScan(basePackageClasses = Foo.class)
public class TestConfiguration
{
#Primary
#Bean
public Foo foo()
{
return new Foo("Primary bean!!");
}
}
I would expect the following test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfiguration.class)
public class Test
{
#Autowired
private Foo foo;
#Test
public void test()
{
System.out.println(foo);
}
}
to print out Primary Bean!! but it returns Secondary ComponentScan?? instead...
How come? Nowhere does the documentation for #Primary say it fails against component-scanned beans!
The reason is that both beans actually have the same name foo, so internally one bean definition is getting overridden with the other one, essentially the one with #Bean is getting overridden by the one being scanned by #ComponentScan.
The fix is simply to give one of them a different name and you should see the correct behavior of the #Primary bean getting injected.
#Primary
#Bean
public Foo foo1()
{
return new Foo("Primary bean!!");
}
OR
#Component("foo1")
public class Foo
{
..