I was reading this article Exception Handling in Spring MVC, and I don't understand why class extending ExceptionHandlerExceptionResolver doesn't have any annotations to it.
It should be a bean, right? So it must be annotated with #Component (or maybe #Service, but I'm not sure if it belongs to a service layer) annotation or something?
So why it does not have any annotations and how then Spring knows that it's a bean and that it should be used?
You have to manually add your handler to the list of handlers (in the example below I use a SImpleMappingExceptionsResolver, but you can use your own implementation):
package eu.anastasis.readingtrainer.configuration;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
#Configuration
#EnableWebMvc
public class ConfigurationAdapter extends WebMvcConfigurerAdapter {
#Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
exceptionResolvers.add(new SimpleMappingExceptionResolver());
}
}
Please note that in this way you override any #ControllerAdvice + #ExceptionHandler you may have set (I don't know how to combine both strategies).
Just as an update to #Andrea Pegoretti answer,
in Spring 5.X.X WebMvcConfigurerAdapter is deprecated, instead use WebMvcConfigurer interface; please refer to this link for more details.
Related
I am a new learner of Spring Boot. As far as I learned by now, we need to use #Component above a class/interface for Spring to store the bean in the Spring container. And we can inject that bean by using #Autowired. I've been working on a demo project where I can't see #Component on an interface but somehow the bean of that interface is being provided correctly. If I add #Component it says multiple beans found.
Post Controller Class:
package com.ashik.jobmarket.controller;
import com.ashik.jobmarket.repository.PostRepository;
import com.ashik.jobmarket.model.Post;
import com.ashik.jobmarket.repository.SearchRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
#CrossOrigin(origins = "http://localhost:3000")
public class PostController {
#Autowired
PostRepository repo;
#Autowired
SearchRepository srepo;
#GetMapping("/allPosts")
#CrossOrigin
public List<Post> getAllPosts(){
return repo.findAll();
}
#GetMapping("/posts/{text}")
#CrossOrigin
public List<Post> search(#PathVariable String text){
return srepo.findByText(text);
}
#PostMapping("/post")
#CrossOrigin
public Post addPost(#RequestBody Post post){
return repo.save(post);
}
}
The Post Repository Interface:
package com.ashik.jobmarket.repository;
import com.ashik.jobmarket.model.Post;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface PostRepository extends MongoRepository<Post, String>{}
No Class implemented Post Repository.
I tried adding #Component myself but it's saying that I have multiple beans of the same name. I am trying to understand the process, and how the bean is being delivered without #Component annotation.
Spring boot uses #EnableJpaRepositories which all interfaces/classes that extend/implement a spring data repository. In turn spring then provides an implementation which is added to the container.
As MongoRepository is spring JPA repository, the extending interfaces are being picked up and provided as autowireable dependencies. So when you annotate your PostRepository with #Component it is picked up twice by spring, causing the multiple beans found exception.
For more info on this topic check baeldung and docs
I want to use both Redis and Mongo with repository manner (I do not want to use spring cache annotations but repository methods).
I annotate the main class with the following annotations.
#EnableMongoRepositories(basePackageClass = PersistencyRepository.class)
#EnableRedisRepositories(basePackageClass = CacheRepository.class)
#SpringBootApplication
Repos
public interface PersistencyRepository extends CrudRepository<Store, String> {}
public interface CacheRepository extends MongoRepository<Store, String> {}
Now, I am getting the following error.
The bean "cacheRepository" defined in com.repository.CacheRepository defined in #EnableMongoRepositories declared on StoreApplication, could not be registered. A bean with that name has already been defined in com.repository.CacheRepository defined in #EnableRedisRepositories declared on StoreApplication and overriding is disabled.
How can I use repos of differenet databases (mongo, redis)?
You extended the wrong repository interface (MongoRepository) on CacheRepository try extending CrudRepository instead.
Also, your mongo and redis entities should be separated to different packages, usually I just went with com.my.company.entity.mongo and com.my.company.entity.redis for each.
After that, you need to update those Configuration annotations. A better package design, instead of putting all annotations on Main is putting them on a separate package, then putting those annotations there. This has an added benefit of clearly splitting each configurations for what they actually do
for example:
package com.your.company.configuration;
import com.your.company.configuration.properties.ApplicationProperties;
import com.your.company.entity.mongo.BaseDocument;
import com.your.company.entity.postgres.BaseEntity;
import com.your.company.entity.redis.BaseHash;
import com.your.company.repository.mongo.BaseMongoRepository;
import com.your.company.repository.postgres.BaseJpaRepository;
import com.your.company.repository.redis.BaseRedisRepository;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableConfigurationProperties(ApplicationProperties.class)
#EnableJpaRepositories(basePackageClasses = {BaseEntity.class, BaseJpaRepository.class})
#EnableMongoRepositories(basePackageClasses = {BaseDocument.class,
BaseMongoRepository.class}, repositoryFactoryBeanClass = EnhancedMongoRepositoryFactoryBean.class)
#EnableRedisRepositories(basePackageClasses = {BaseHash.class, BaseRedisRepository.class})
public class BasicConfiguration {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
The above is only an example, usually I would split them further into one class each within the same package with names that describes what they are actually configuring, for example: MongoConfiguration.java, JpaConfiguration.java, etc. Note if you decide to go with that design, you need the #Configuration annotation in each of the separate classes
I believe the main issue here is that both of your interfaces PersistencyRepository and CacheRepository are in the same package, and your configurations are both scanning the same package for Spring Data Repository interfaces, creating duplicate bean names. You should separate these repositories into their own packages.
It's important to note that basePackageClasses scans the whole package for applicable interfaces. See the docs for EnableMongoRepositories.basePackageClasses (source):
Type-safe alternative to basePackages() for specifying the packages to scan for annotated components. The package of each class specified will be scanned. Consider creating a special no-op marker class or interface in each package that serves no purpose other than being referenced by this attribute.
i want to execute some code during (or rather at the end of) application startup. I found a couple of resources doing this using #PostConstruct annotation, #EventListener(ContextRefreshedEvent.class), implementing InitializingBean, implementing ApplicationListener... All of them execute my code at startup, but the placeholder of the application properties are not replaced at that moment. So if my class has a member with an #Value("${my.property}") annotation, it returns "${my.property}" instead of the actual value defined in the yaml (or wherever).
How do i accomplish to execute my code after the replacement took place?
You can implement InitializingBean which has a method named afterPropertiesSet(). This method will be called after all properties placeholders are replaced.
#PostConstruct is called when bean is created. Ypu have to check if spring found file with properties.
If you have a config class, #Configuration, then you can try explicitly importing your properties file by adding the following annotation:
#PropertySource("classpath:your-properties-file.properties")
Any other non-config resources should load after your config classes and your #Value annotations should work fine.
You should implement ApplicationListener<ContextRefreshedEvent> like this:
#Component
public class SpringContextListener implements ApplicationListener<ContextRefreshedEvent> {
#Value("${my.property}")
private String someVal;
/**
* // This logic will be executed after the application has loded
*/
public void onApplicationEvent(ContextRefreshedEvent event) {
// Some logic here
}
}
You can get it after spring boot start.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
#Component
#Order(0)
class ApplicationReadyInitializer implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
ResourceLoader resourceLoader;
#Value("${my.property}")
private String someVal;
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// App was started. Do something
}
}
package com.ge.hc.gsit.sbom.configuration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#Configuration
#EnableAutoConfiguration
#EnableWebMvc
#ComponentScan(basePackages = {"com.abc.xy.gsit.sbom.controller","com.abc.xy.gsit.sbom.exception"})
public class MvcConfig extends WebMvcAutoConfigurationAdapter{
}
Hi,
I want to know how WebMvcAutoConfigurationAdapter class is working.
If any documents is present please let me know, It will be helpful.
Thanks in Advance.
Comment in the WebMvcAutoConfigurationAdapter states that:
// Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when not
// on the classpath
WebMvcAutoConfigurationAdapter class extends WebMvcConfigurerAdapter and provides default implementation of WebMvcConfigurer interfaces methods that are callbacks to customize the Java-based configuration for Spring MVC enabled via #EnableWebMvc.
So, if you want to changes some behavior you should extends WebMvcConfigurerAdapter.
More details about EnableAutoConfiguration and Spring Boot in general: Understanding Spring Boot
I'm new to Java and Spring, coming from C# and the .NET world, so bear with me - what I am attempting to do may be off the mark...
I am attempting to configure Spring DI using Java configuration and annotations, not XML configuration, however I am having a few issues. This is for a standalone application, not a web app. I have worked through the springsource documentationand as far as I can tell my very basic configuration should be correct...but isn't. Please take a look at the code below:
Java Configuration Annotated Class:
package birdalerter.common;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import birdalerter.process.ISightingsProcessor;
import birdalerter.process.SightingsProcessor;
#Configuration
#ComponentScan({"birdalerter.process", "birdalerter.common"})
public class AppConfig {
#Bean
#Scope("prototype")
public ISightingsProcessor sightingsProcessor(){
return new SightingsProcessor();
}
}
Configure Component implementing the ISightingsProcessor interface:
package birdalerter.process;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import org.springframework.stereotype.Component;
import birdalerter.domainobjects.IBirdSighting;
#Component
public class SightingsProcessor implements ISightingsProcessor{
private LinkedBlockingQueue<IBirdSighting> queue;
private List<ISightingVisitor> sightingVisitors = new ArrayList<ISightingVisitor>();
public SightingsProcessor(){
}
...
}
Configure Factory Component:
package birdalerter.process;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
public class ProcessorFactory {
private ISightingsProcessor sightingsProcessor;
#Autowired
#Required
private void setSightingsProcessor(ISightingsProcessor sightingsProcessor){
this.sightingsProcessor = sightingsProcessor;
}
public ISightingsProcessor getSightingsProcessor(){
return this.sightingsProcessor;
}
}
Wire up the AnnotationConfigApplicationContext and test:
#Test
public void testProcessingDI(){
#SuppressWarnings("resource")
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
context.refresh();
ISightingsProcessor processor = new ProcessorFactory().getSightingsProcessor();
System.out.println(processor);
Assert.assertTrue(processor != null);
}
The SightingsProcessor is not being setter injected and the assert is failing as the returned object is null. Hopefully I have missed something very obvious.
Thanks in advance.
Edited in Response to Meriton:
Thanks for the answer Meriton.
Why would Spring not know about the newly created object? Does Spring not maintain dependencies throughout the application lifecycle and inject as appropriate when new objects are created that are configured as beans?
I don't want to directly use context.getBean(ISightingsProcessor.class) if I can help it to be honest, I would like the dependency injected in the setter method without having manual intervention - it just seems cleaner.
I am using the ProcessorFactory as the ISightingsProcessor interface extends Runnable - the implementing object is to be started as a thread. The application will be configurable to have n* threads, with each being started within a loop iteration. I don't think it is possible (I may be wrong, please advise if so) to have #Autowired annotations within method declarations, hence I use the factory to supply a new instance of the injected ISightingsProcessor concrete class.
Yes I've just had a look regarding the #Scope annotation - you are right, that needs moving to the AppConfig #Bean declaration (which I've done in this edit), thanks for that.
ISightingsProcessor processor = new ProcessorFactory().getSightingsProcessor();
This calls the constructor of ProcessorFactory, and then the getter of the instance the constructor created. Spring can not know about that newly created object, and therefore not inject its dependencies. You should ask Spring for the ProcessorFactory instead, for instance with
ProcessorFactory pf = context.getBean(ProcessorFactory.class);
ISightingsProcessor processor = pf.getSightingsProcessor();
That said, I don't know why you need class ProcessorFactory at all. You might just as well get the ISightingsProcessor directly:
ISightingsProcessor processor = context.getBean(ISightingsProcessor.class);
Additionally, "Java Based Configuration" and component scanning are independent ways to declare beans. Currently, you are therefore declaring the ISightingsProcessor twice: Once with the #Bean-annotated factory method, and once with the component scan and the #Component annotation on the class. Doing either of that will do. In fact, doing both might cause one bean definition to override the other.
Oh, and the #Scope annotation is for bean definitions (those you annotate with #Bean or #Component). It will likely be ignored on injection points (#Autowired).