EmbeddedKafka kafka streams test with SpringBootTest finds two StreamsBuilderFactoryBeans - spring-boot

Following the advice here, I'm trying to use an embedded Kafka to test my Spring Boot Streams application.
However, simply creating the given configuration
#Configuration
#EnableKafkaStreams
public class StreamsTestConfiguration {
#Value("${" + EmbeddedKafkaBroker.SPRING_EMBEDDED_KAFKA_BROKERS + "}")
private String brokerAddresses;
#Bean(name = KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME)
public KafkaStreamsConfiguration kStreamsConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "testStreams");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, this.brokerAddresses);
return new KafkaStreamsConfiguration(props);
}
}
and a simple test
#RunWith(SpringRunner.class)
#SpringBootTest
#EmbeddedKafka(topics = { "topic" })
public class EmbeddedKafkaTest {
#Autowired
private MyBean tested;
#Autowired
private EmbeddedKafkaBroker kafkaBroker;
#Test
public void loaded() {}
}
fails to run:
Parameter 0 of method kafkaStreamsFactoryBeanConfigurer in org.springframework.boot.autoconfigure.kafka.KafkaStreamsAnnotationDrivenConfiguration required a single bean, but 2 were found:
- &defaultKafkaStreamsBuilder: defined by method 'defaultKafkaStreamsBuilder' in class path resource [org/springframework/kafka/annotation/KafkaStreamsDefaultConfiguration.class]
- &stream-builder-process: defined in null
[...]
Caused by: org.springframework.context.ApplicationContextException: Failed to start bean 'outputBindingLifecycle'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'kafkaStreamsFactoryBeanConfigurer' defined in org.springframework.boot.autoconfigure.kafka.KafkaStreamsAnnotationDrivenConfiguration: Unsatisfied dependency expressed through method 'kafkaStreamsFactoryBeanConfigurer' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.kafka.config.StreamsBuilderFactoryBean' available: expected single matching bean but found 2: &defaultKafkaStreamsBuilder,&stream-builder-process
If I remove the #SpringBootTest from the test class, the problem disappears, but then the actual bean under test fails to autowire.
I don't define a StreamBuilderFactoryBean myself, where are they coming from?
Also: is this this setup even worthwhile for testing a stream that is used to feed a KTable that is later being queried? It's not like I can "use a different topic for each test" since the stream will always use the same topic. My hope is that I can get around that with proper test case design, or am I going to hit a wall I can't see yet?

According to you stack trace, you also use Spring Cloud Stream with Kafka Streams Binder. Please, add an appropriate tag.
Consider to remove an explicit #EnableKafkaStreams since Binder is going to take care about infrastructure for you.

Related

#DataJpaTest loads KafkaConfiguration and fails test

#DataJpaTest
class DataJpaVerificationTest {
#Autowired
private JdbcTemplate template;
#Test
public void testTemplate() {
assertThat(template).isNotNull();
}
}
When I run this test I get the following error:
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'kafkaConfig' defined in file
[***\config\KafkaConfig.class]:
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'org.springframework.boot.autoconfigure.kafka.KafkaProperties'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations: {}
where KafkaConfig class is a part of my application (app read data from Kafka and saves data to DB) and looks like:
#Configuration
#RequiredArgsConstructor
public class KafkaConfig {
private final KafkaProperties kafkaProperties;
#Bean
public Properties consumerProperties() {
Properties props = new Properties();
props.putAll(this.kafkaProperties.buildConsumerProperties());
return props;
}
}
Based on information that I googled about DataJpaTest annotation:
created application context will not contain the whole context needed
for our Spring Boot application, but instead only a “slice” of it
containing the components needed to initialize any JPA-related
components like our Spring Data repository.
So the question is: why Spring tries to load to the context Kafka specific bean for DataJpaTest?

Why do spring tests do not reuse the same Context with Embedded Mongo

I am doing integration tests and unit tests with spring boot. I made three test classes.
ApplicationRunnterTest: to test contextLoads
DecisionTreeServiceTest: to test the save function of the service
DecisionTreeRepositoryTest: to test the methods of the repo
I added the embdded mongo packages (flapdoodle.embed.mongo). What i have observed is that, when all three tests are run in this order 1) => 2) => 3)
What happens is when ApplicationRunnerTest loads the context then the second test fails (the service) and the third is success (repository). The problem is with the context being used or something in the test that fails it says "Error to create bean embedded mongo"
The solution i found is to add the annotation #DirtiesContext() and from what i understand is for each test class, it will make a new context and it worked.
My questions are the following :
Why doesnt spring use the same context for all the classes
if it does, why there is an ambiguity or an error ?
is there a better way instead of #DirtiestContext() since i guess its a lot to load a new context for each test class.
Here are the classes and the erroes.
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration()
public class ApplicationRunnerTest {
#Test
public void contextLoads(){
}
}
Service class
#RunWith(SpringRunner.class)
#SpringBootTest
//#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class DecisionTreeServiceTest {
#Autowired
private DecisionTreeRepository decisionTreeRepository;
#MockBean
private HistorySupervisionService historySupervisionService;
#Autowired
private DecisionTreeService decisionTreeService;
#Autowired
private DecisionTreeMapper decisionTreeMapper;
private DecisionTree[] trees;
#Before
public void init() throws IOException {
// get test data
JacksonMapper jacksonMapper = new JacksonMapper();
trees = jacksonMapper.loadJsonFileToObject("src/test/java/com/almerys/cpm/back/utils/decisionTree/decisionTreeObject",
DecisionTree[].class
);
//clear databse first
this.decisionTreeRepository.deleteAll();
// save object 2 and object 3
this.decisionTreeRepository.save(trees[1]);
this.decisionTreeRepository.save(trees[2]);
}
#Test
public void saveTest(){
// TEST CREATE SCENARIO
DecisionTreeDTO decisionTree = this.decisionTreeMapper.toDto(trees[0]);
DecisionTree savedDecisionTree = trees[0];
savedDecisionTree.setId("1");
savedDecisionTree.setInternalId(4);
// Mockito.when(decisionTreeRepository.findFirstByOrderByInternalIdDesc()).thenReturn(Optional.of(trees[2]));
// Mockito.when(decisionTreeRepository.save(Mockito.any())).thenReturn(savedDecisionTree);
Mockito.when(historySupervisionService.getLastVersionByContext(Mockito.anyString())).thenReturn("13.0");
DecisionTreeDTO createdDecisionTree= decisionTreeService.save(decisionTree);
assertThat(createdDecisionTree).isNotNull();
assertThat(createdDecisionTree.getInternalId()).isEqualTo(4);
assertThat(createdDecisionTree.getFactoryVersion()).contains("13.1");
// TEST UPDATE SCENARIO
}
Repo Class
#RunWith(SpringRunner.class)
#DataMongoTest
public class DecisionTreeRepositoryTest{
#Autowired
private DecisionTreeRepository decisionTreeRepository;
private void initData() throws IOException {
//clean embedded database first.
this.decisionTreeRepository.deleteAll();
// retrieve data from json
JacksonMapper jacksonMapper = new JacksonMapper();
DecisionTree[] createDecisionTree= jacksonMapper.loadJsonFileToObject("src/test/java/com/almerys/cpm/back/utils/decisionTree/decisionTreeObject",
DecisionTree[].class
);
// persist data
Arrays.stream(createDecisionTree).forEach(tree-> this.decisionTreeRepository.save(tree));
}
#Before
public void init() throws IOException {
this.initData();
}
#Test
public void findByFactoryCategoryInternalId(){
DecisionTree decisionTree = this.decisionTreeRepository.findByFactoryCategoryInternalIdAndFactoryVersion(1,"11.0");
assertThat(decisionTree).isNotNull();
}
}
Error
Unsatisfied dependency expressed through field 'mongoTemplate'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mongoTemplate' defined in class path resource [org/springframework/boot/autoconfigure/data/mongo/MongoDbFactoryDependentConfiguration.class]: Unsatisfied dependency expressed through method 'mongoTemplate' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mongoDbFactory' defined in class path resource [org/springframework/boot/autoconfigure/data/mongo/MongoDbFactoryConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.core.MongoDbFactorySupport]: Factory method 'mongoDbFactory' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'embeddedMongoServer' defined in class path resource [org/springframework/boot/autoconfigure/mongo/embedded/EmbeddedMongoAutoConfiguration.class]: Invocation of init method failed; nested exception is java.io.IOException: Could not start process: <EOF>
Why doesnt spring use the same context for all the classes
The simple answer is: because one of the test uses #MockBean.
When using mock bean, you're asking Spring to replace the original bean instance with a mock. Obviously, other beans might depend on that bean, it might be involved in their initialization etc., so any previously created context needs to be scrapped in order for the test environment to be reliable.
Also, do all the test reside in the same package? If not, I think the consequence of using #ContextConfiguration will be that only components residing in the same package and sub-packages are going to be picked up. This implies the test context for each of the tests will not be the same, so obviously the test context cannot be reused.
why there is an ambiguity or an error ?
Hard to say without looking at the actual configuration, but my guess would be the embedded Mongo doesn't cleanup after itself properly.

NUnit 5 Spring MVC test NoSuchBeanDefinitionException for Autowired dependency in submodule

I have a project with two submodules; one is the data access layer the other is the API service.
The data access module uses JOOQ and an autowired DSLContext in a service class. Also, I'm using JUnit 5, and Spring Boot 2.2.4.
The QueryService class in the data access module has a member like #Autowired private DSLContext dsl;
The test class is set up like this:
#SpringBootTest
public class MyServiceTests {
#Autowired
QueryService service;
#Autowired
private DSLContext dsl;
#Test
public void TestDoSomething() throws Exception {
service.selectBusinessEntityRelatedByBusinessEntity("C00001234", mockAuth);
}
}
The tests in this module run correctly. Configuration is read from the application.yaml, and autowire injects either real services or a mock into both my QueryService and the local dsl.
The API service is a different story. If I use the #SpringBootTest annotation with no MVC I can successfully get the tests to inject a local DSLContext with configuration from the application.yaml. Test set up similar to this:
#SpringBootTest
public class CustomersControllerTests {
#Autowired
private Gson gson;
#Autowired
DSLContext dsl;
#Test
public void addCustomerTest() {
}
What I need though is to use #WebMvcTest so that MockMvc is initialized but switching to #WebMvcTest causes injection to fail in the service class implemented in the data access module. The injection fails to find the DSLContext bean within the query service class. I set up the test like this:
#WebMvcTest
public class CustomersControllerTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private Gson gson;
private static final String testSub = "329e6764-3809-4e47-ac48-a52881045787";
#Test
public void addCustomerTest() {
var newCustomer = new Customer().firstName("John").lastName("Doe");
mockMvc.perform(post("/customers").content(gson.toJson(newCustomer)).contentType(MediaType.APPLICATION_JSON)
.with(jwt().jwt(jwt -> jwt.claim("sub", testSub)))).andExpect(status().isNotImplemented());
}
This is the actual error:
2020-02-25 18:14:33.655 WARN 10776 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customersController': Unsatisfied dependency expressed through field '_customersService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customersService': Unsatisfied dependency expressed through field '_queryService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'queryService': Unsatisfied dependency expressed through field '_dsl'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.jooq.DSLContext' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
So, I know the test application configuration is correct because it works when not using MVC annotation. Also, I can create a DSLContext in the API project tests and I can actually run the API service outside the test.
So, why cant the DSLContext be found when using the MVC test setup?
This might be because #WebMvcTest fully disables Spring Boot's Autoconfiguration and only scans in #Controllers and a few other select classes, that you need for your ..well...MVC tests..
The Spring documentation recommends doing this in your case:
If you are looking to load your full application configuration and use MockMVC, you should consider #SpringBootTest combined with #AutoConfigureMockMvc rather than this annotation.

No qualifying bean of type [org.springframework.mail.javamail.JavaMailSender] while deploying service into PCF [duplicate]

Please explain the following about NoSuchBeanDefinitionException exception in Spring:
What does it mean?
Under what conditions will it be thrown?
How can I prevent it?
This post is designed to be a comprehensive Q&A about occurrences of NoSuchBeanDefinitionException in applications using Spring.
The javadoc of NoSuchBeanDefinitionException explains
Exception thrown when a BeanFactory is asked for a bean instance for
which it cannot find a definition. This may point to a non-existing
bean, a non-unique bean, or a manually registered singleton instance
without an associated bean definition.
A BeanFactory is basically the abstraction representing Spring's Inversion of Control container. It exposes beans internally and externally, to your application. When it cannot find or retrieve these beans, it throws a NoSuchBeanDefinitionException.
Below are simple reasons why a BeanFactory (or related classes) would not be able to find a bean and how you can make sure it does.
The bean doesn't exist, it wasn't registered
In the example below
#Configuration
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
ctx.getBean(Foo.class);
}
}
class Foo {}
we haven't registered a bean definition for the type Foo either through a #Bean method, #Component scanning, an XML definition, or any other way. The BeanFactory managed by the AnnotationConfigApplicationContext therefore has no indication of where to get the bean requested by getBean(Foo.class). The snippet above throws
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.example.Foo] is defined
Similarly, the exception could have been thrown while trying to satisfy an #Autowired dependency. For example,
#Configuration
#ComponentScan
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
}
}
#Component
class Foo { #Autowired Bar bar; }
class Bar { }
Here, a bean definition is registered for Foo through #ComponentScan. But Spring knows nothing of Bar. It therefore fails to find a corresponding bean while trying to autowire the bar field of the Foo bean instance. It throws (nested inside a UnsatisfiedDependencyException)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.example.Bar] found for dependency [com.example.Bar]:
expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
There are multiple ways to register bean definitions.
#Bean method in a #Configuration class or <bean> in XML configuration
#Component (and its meta-annotations, eg. #Repository) through #ComponentScan or <context:component-scan ... /> in XML
Manually through GenericApplicationContext#registerBeanDefinition
Manually through BeanDefinitionRegistryPostProcessor
...and more.
Make sure the beans you expect are properly registered.
A common error is to register beans multiple times, ie. mixing the options above for the same type. For example, I might have
#Component
public class Foo {}
and an XML configuration with
<context:component-scan base-packages="com.example" />
<bean name="eg-different-name" class="com.example.Foo />
Such a configuration would register two beans of type Foo, one with name foo and another with name eg-different-name. Make sure you're not accidentally registering more beans than you wanted. Which leads us to...
If you're using both XML and annotation-based configurations, make sure you import one from the other. XML provides
<import resource=""/>
while Java provides the #ImportResource annotation.
Expected single matching bean, but found 2 (or more)
There are times when you need multiple beans for the same type (or interface). For example, your application may use two databases, a MySQL instance and an Oracle one. In such a case, you'd have two DataSource beans to manage connections to each one. For (simplified) example, the following
#Configuration
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
System.out.println(ctx.getBean(DataSource.class));
}
#Bean(name = "mysql")
public DataSource mysql() { return new MySQL(); }
#Bean(name = "oracle")
public DataSource oracle() { return new Oracle(); }
}
interface DataSource{}
class MySQL implements DataSource {}
class Oracle implements DataSource {}
throws
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [com.example.DataSource] is defined:
expected single matching bean but found 2: oracle,mysql
because both beans registered through #Bean methods satisfied the requirement of BeanFactory#getBean(Class), ie. they both implement DataSource. In this example, Spring has no mechanism to differentiate or prioritize between the two. But such mechanisms exists.
You could use #Primary (and its equivalent in XML) as described in the documentation and in this post. With this change
#Bean(name = "mysql")
#Primary
public DataSource mysql() { return new MySQL(); }
the previous snippet would not throw the exception and would instead return the mysql bean.
You can also use #Qualifier (and its equivalent in XML) to have more control over the bean selection process, as described in the documentation. While #Autowired is primarily used to autowire by type, #Qualifier lets you autowire by name. For example,
#Bean(name = "mysql")
#Qualifier(value = "main")
public DataSource mysql() { return new MySQL(); }
could now be injected as
#Qualifier("main") // or #Qualifier("mysql"), to use the bean name
private DataSource dataSource;
without issue. #Resource is also an option.
Using wrong bean name
Just as there are multiple ways to register beans, there are also multiple ways to name them.
#Bean has name
The name of this bean, or if plural, aliases for this bean. If left
unspecified the name of the bean is the name of the annotated method.
If specified, the method name is ignored.
<bean> has the id attribute to represent the unique identifier for a bean and name can be used to create one or more aliases illegal in an (XML) id.
#Component and its meta annotations have value
The value may indicate a suggestion for a logical component name, to
be turned into a Spring bean in case of an autodetected component.
If that's left unspecified, a bean name is automatically generated for the annotated type, typically the lower camel case version of the type name. For example MyClassName becomes myClassName as its bean name. Bean names are case sensitive. Also note that wrong names/capitalization typically occur in beans referred to by string like #DependsOn("my BeanName") or XML config files.
#Qualifier, as mentioned earlier, lets you add more aliases to a bean.
Make sure you use the right name when referring to a bean.
More advanced cases
Profiles
Bean definition profiles allow you to register beans conditionally. #Profile, specifically,
Indicates that a component is eligible for registration when one or
more specified profiles are active.
A profile is a named logical grouping that may be activated
programmatically via
ConfigurableEnvironment.setActiveProfiles(java.lang.String...) or
declaratively by setting the spring.profiles.active property as a JVM
system property, as an environment variable, or as a Servlet context
parameter in web.xml for web applications. Profiles may also be
activated declaratively in integration tests via the #ActiveProfiles
annotation.
Consider this examples where the spring.profiles.active property is not set.
#Configuration
#ComponentScan
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
System.out.println(Arrays.toString(ctx.getEnvironment().getActiveProfiles()));
System.out.println(ctx.getBean(Foo.class));
}
}
#Profile(value = "StackOverflow")
#Component
class Foo {
}
This will show no active profiles and throw a NoSuchBeanDefinitionException for a Foo bean. Since the StackOverflow profile wasn't active, the bean wasn't registered.
Instead, if I initialize the ApplicationContext while registering the appropriate profile
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("StackOverflow");
ctx.register(Example.class);
ctx.refresh();
the bean is registered and can be returned/injected.
AOP Proxies
Spring uses AOP proxies a lot to implement advanced behavior. Some examples include:
Transaction management with #Transactional
Caching with #Cacheable
Scheduling and asynchronous execution with #Async and #Scheduled
To achieve this, Spring has two options:
Use the JDK's Proxy class to create an instance of a dynamic class at runtime which only implements your bean's interfaces and delegates all method invocations to an actual bean instance.
Use CGLIB proxies to create an instance of a dynamic class at runtime which implements both interfaces and concrete types of your target bean and delegates all method invocations to an actual bean instance.
Take this example of JDK proxies (achieved through #EnableAsync's default proxyTargetClass of false)
#Configuration
#EnableAsync
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
System.out.println(ctx.getBean(HttpClientImpl.class).getClass());
}
}
interface HttpClient {
void doGetAsync();
}
#Component
class HttpClientImpl implements HttpClient {
#Async
public void doGetAsync() {
System.out.println(Thread.currentThread());
}
}
Here, Spring attempts to find a bean of type HttpClientImpl which we expect to find because the type is clearly annotated with #Component. However, instead, we get an exception
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.example.HttpClientImpl] is defined
Spring wrapped the HttpClientImpl bean and exposed it through a Proxy object that only implements HttpClient. So you could retrieve it with
ctx.getBean(HttpClient.class) // returns a dynamic class: com.example.$Proxy33
// or
#Autowired private HttpClient httpClient;
It's always recommended to program to interfaces. When you can't, you can tell Spring to use CGLIB proxies. For example, with #EnableAsync, you can set proxyTargetClass to true. Similar annotations (EnableTransactionManagement, etc.) have similar attributes. XML will also have equivalent configuration options.
ApplicationContext Hierarchies - Spring MVC
Spring lets you build ApplicationContext instances with other ApplicationContext instances as parents, using ConfigurableApplicationContext#setParent(ApplicationContext). A child context will have access to beans in the parent context, but the opposite is not true. This post goes into detail about when this is useful, particularly in Spring MVC.
In a typical Spring MVC application, you define two contexts: one for the entire application (the root) and one specifically for the DispatcherServlet (routing, handler methods, controllers). You can get more details here:
Difference between applicationContext.xml and spring-servlet.xml in Spring Framework
It's also very well explained in the official documentation, here.
A common error in Spring MVC configurations is to declare the WebMVC configuration in the root context with #EnableWebMvc annotated #Configuration classes or <mvc:annotation-driven /> in XML, but the #Controller beans in the servlet context. Since the root context cannot reach into the servlet context to find any beans, no handlers are registered and all requests fail with 404s. You won't see a NoSuchBeanDefinitionException, but the effect is the same.
Make sure your beans are registered in the appropriate context, ie. where they can be found by the beans registered for WebMVC (HandlerMapping, HandlerAdapter, ViewResolver, ExceptionResolver, etc.). The best solution is to properly isolate beans. The DispatcherServlet is responsible for routing and handling requests so all related beans should go into its context. The ContextLoaderListener, which loads the root context, should initialize any beans the rest of your application needs: services, repositories, etc.
Arrays, collections, and maps
Beans of some known types are handled in special ways by Spring. For example, if you tried to inject an array of MovieCatalog into a field
#Autowired
private MovieCatalog[] movieCatalogs;
Spring will find all beans of type MovieCatalog, wrap them in an array, and inject that array. This is described in the Spring documentation discussing #Autowired. Similar behavior applies to Set, List, and Collection injection targets.
For a Map injection target, Spring will also behave this way if the key type is String. For example, if you have
#Autowired
private Map<String, MovieCatalog> movies;
Spring will find all beans of type MovieCatalog and add them as values to a Map, where the corresponding key will be their bean name.
As described previously, if no beans of the requested type are available, Spring will throw a NoSuchBeanDefinitionException. Sometimes, however, you just want to declare a bean of these collection types like
#Bean
public List<Foo> fooList() {
return Arrays.asList(new Foo());
}
and inject them
#Autowired
private List<Foo> foos;
In this example, Spring would fail with a NoSuchBeanDefinitionException because there are no Foo beans in your context. But you didn't want a Foo bean, you wanted a List<Foo> bean. Before Spring 4.3, you'd have to use #Resource
For beans that are themselves defined as a collection/map or array
type, #Resource is a fine solution, referring to the specific
collection or array bean by unique name. That said, as of 4.3,
collection/map and array types can be matched through Spring’s
#Autowired type matching algorithm as well, as long as the element
type information is preserved in #Bean return type signatures or
collection inheritance hierarchies. In this case, qualifier values can
be used to select among same-typed collections, as outlined in the
previous paragraph.
This works for constructor, setter, and field injection.
#Resource
private List<Foo> foos;
// or since 4.3
public Example(#Autowired List<Foo> foos) {}
However, it will fail for #Bean methods, ie.
#Bean
public Bar other(List<Foo> foos) {
new Bar(foos);
}
Here, Spring ignores any #Resource or #Autowired annotating the method, because it's a #Bean method, and therefore can't apply the behavior described in the documentation. However, you can use Spring Expression Language (SpEL) to refer to beans by their name. In the example above, you could use
#Bean
public Bar other(#Value("#{fooList}") List<Foo> foos) {
new Bar(foos);
}
to refer to the bean named fooList and inject that.

Setting up Custom HandlerExceptionResolver in Spring MVC

I am trying to override WebMvcConfigurerAdapter.configureHandlerExceptionResolvers() and provide my own ExceptionHandlerExceptionResolver to Spring MVC. The motive behind this is to provide Custom Content Negotiation in such a way that if the user requests for any garbage in "Accept" header, I can return him a JSON response with "media not supported exception". I was partially able to acheive the configuration using the bellow setup.
#Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(new ErrorContentNegotiation());
ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = exceptionHandlerExceptionResolver();
exceptionHandlerExceptionResolver.setContentNegotiationManager(contentNegotiationManager);
exceptionHandlerExceptionResolver.afterPropertiesSet();
exceptionResolvers.add(exceptionHandlerExceptionResolver);
}
#Bean
public ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver() {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(getHttpMessageConverter());
ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = new ExceptionHandlerExceptionResolver();
exceptionHandlerExceptionResolver.setMessageConverters(messageConverters);
return exceptionHandlerExceptionResolver;
}
public class ErrorContentNegotiationStrategy implements ContentNegotiationStrategy {
#Override
public List<MediaType> resolveMediaTypes(final NativeWebRequest webRequest) {
return Lists.newArrayList(Globals.JSON);
}
}
I am getting this exception when the spring starts up.
No qualifying bean of type [org.springframework.web.accept.ContentNegotiationStrategy] is defined: expected single matching bean but found 2: errorContentNegotiationStrategy,mvcContentNegotiationManager
Doesn't work when I add a #Qualifier annotation to my ErrorContentNegotiationStrategy class and give it a unique name. Throws the same Exception.
If I remove #Compoenent annotation and leave the code as is, then ErrorContentNegotiationStrategy() method in ErrorContentNegotiaionStrategy is not getting called.
Did anyone face this issue ?
Add the #Primary annotation to your ErrorContentNegotiationStrategy class:
Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.
This should at least solve the exception during startup.
After debugging the issue, I found that I was trying to load 2 beans of same type ( which is what the error message says. One of the bean was actual implementation and other one was a Mock for test case. Since both resided in the same package #component scanned the base package and couldn't decide which one to load. I resolved the issue by using #profile, which helps in loading beans based on the profile you load. I used 2 profiles, one for testing and one for development.

Resources