Why We use WebMvcAutoConfigurationAdapter class - java

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

Related

Using mongo and redis cache with both repositories in Spring Boot

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.

Issue executing Zuul proxy in Spring (bean type not found)

Spring Boot Starter : 2.1.4.RELEASE
i am trying to use spring-cloud-starter-netflix-zuul to setup service proxy in spring boot, however the jar throws the below exception during startup
***************************
APPLICATION FAILED TO START
***************************
Description:
Field server in org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration required a bean of type 'org.springframework.boot.autoconfigure.web.ServerProperties' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=false)
Action:
Consider defining a bean of type 'org.springframework.boot.autoconfigure.web.ServerProperties' in your configuration.
Below is my Main
package com.xxx.serviceproxy;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
#Configuration
#ComponentScan
#EnableAutoConfiguration
#Controller
#EnableZuulProxy
#SpringBootApplication
public class ServiceProxyApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ServiceProxyApplication.class).web(WebApplicationType.NONE).run(args);
}
}
This happens because you are putting the annotation #EnableAutoConfiguration.
This annotation causes the class ZuulServerAutoConfiguration to be searched for.
You can see here:
https://github.com/spring-cloud/spring-cloud-netflix/blob/master/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration.java
which has an autowired from
#Autowired
protected ServerProperties server;
And in the header of the class you can see the comments:
// Make sure to get the ServerProperties from the same place as a normal web app would
// FIXME #Import(ServerPropertiesAutoConfiguration.class)
public class ZuulServerAutoConfiguration {
which means it's not auto-configuring.
If you remove the annotation #EnableAutoConfiguration, it will no longer look for zuul autoconfiguration and you can configure zuul paths and other features in your application.yml (or application.properties), example:
zuul:
routes:
users:
path: /myusers/**
serviceId: users_service
Here the documentation to configure zuul:
https://cloud.spring.io/spring-cloud-netflix/multi/multi__router_and_filter_zuul.html

Spring cannot find a bean

I'm learning Spring as it seems to be a very powerful framework. I've already did many getting started guides and now I'm trying with this tutorial. In it, all classes are put in the same package, but to make it more interesting I tried using different packages according to the class (entity, controller, etc.). I was about to test it before the Testing a REST Service section but got an error building the application. This is how my project is structured:
The only difference with the classes in the tutorial is the marked ServletInitializer which comes with the initializr utility (actually I used the one that comes with STS, but it's the same). As far as I understand, it has nothing to do with the problem so the content of this class is irrelevant.
Another minor difference with the tutorial is that the Application class here is called RestServicesApplication but the content is the same.
When I try to build the application (using Gradle's bootRun instead of Maven) I got the following error message:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method init in com.example.restservices.RestServicesApplication required a bean of type 'com.example.repository.AccountRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.repository.AccountRepository' in your configuration.
:bootRun FAILED
So I tried to annotate AccountRepository with #Bean but it gives me a compilation error saying that the annotation is disallowed for that location. Next I tried with the #Component annotation (also on BookmarkRepository) and adding #ComponentScan("com.example") in RestServicesApplication. After that the error remains but the message changed to
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.example.controller.BookmarkRestController required a bean of type 'com.example.repository.BookmarkRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.repository.BookmarkRepository' in your configuration.
:bootRun FAILED
I added #Component annotation to BookmarkRestController but the same error message remains. What am I missing here?
Thanks in advance for your answers.
Edit #1
The classes involved in the problem are the following (copied from my project, not the ones in the tutorial, although the differences are minimal):
RestServicesApplication
package com.example.restservices;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import com.example.entity.Account;
import com.example.entity.Bookmark;
import com.example.repository.AccountRepository;
import com.example.repository.BookmarkRepository;
#SpringBootApplication
#ComponentScan("com.example")
public class RestServicesApplication {
public static void main(final String[] args) {
SpringApplication.run(RestServicesApplication.class, args);
}
#Bean
CommandLineRunner init(final AccountRepository accountRepository,
final BookmarkRepository bookmarkRepository) {
return (evt) -> Arrays.asList(
"jhoeller,dsyer,pwebb,ogierke,rwinch,mfisher,mpollack,jlong".split(","))
.forEach(
a -> {
final Account account = accountRepository.save(new Account(a,
"password"));
bookmarkRepository.save(new Bookmark(account,
"http://bookmark.com/1/" + a, "A description"));
bookmarkRepository.save(new Bookmark(account,
"http://bookmark.com/2/" + a, "A description"));
});
}
}
BookmarkRestController
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.repository.AccountRepository;
import com.example.repository.BookmarkRepository;
#RestController
#RequestMapping("/{userId}/bookmarks")
public class BookmarkRestController {
private final BookmarkRepository bookmarkRepository;
private final AccountRepository accountRepository;
#Autowired
public BookmarkRestController(final BookmarkRepository bookmarkRepository,
final AccountRepository accountRepository) {
this.bookmarkRepository = bookmarkRepository;
this.accountRepository = accountRepository;
}
// #RequestMapping methods...
}
AccountRepository
package com.example.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import com.example.entity.Account;
#Component
public interface AccountRepository extends JpaRepository<Account, Long> {
Optional<Account> findByUsername(String username);
}
BookmarkRepository
package com.example.repository;
import java.util.Collection;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import com.example.entity.Bookmark;
#Component
public interface BookmarkRepository extends JpaRepository<Bookmark, Long> {
Collection<Bookmark> findByAccountUsername(String username);
}
Note: I added the imports so you can see where the classes and annotations come from
Edit #2
I tried another thing: I refactored my Project to fit the tutorial and I put everithing in the same package (com.example.bookmarks) and removed the extra annotations. The Project compiles but when I run the Project I get a 404 HTTP status when trying to accesss a REST service. I'm still interested in make it run with my original structure but I want to let you know that this refactoring makes the Project work.
To have Spring create a bean that implements JpaRepository interface, you need to use Spring JPA namespace and activate the repository support using the appropriate element. In xml:
<jpa:repositories base-package="com.example.repository" />
In annotation:
#EnableJpaRepositories
See this docs
This scans all packages below com.example.repository for interfaces extending JpaRepository and creates a Spring bean for it that is backed by an implementation of SimpleJpaRepository.
I think you have to create package again. You packing looking not right. Recreate your package

How custom Account Repository become a bean without any annotation ?

I customized the spring security for my spring boot app.
I use a custom Account Repository:
import org.springframework.data.jpa.repository.JpaRepository;
import com.boot.cut_costs.config.security.CustomUserDetails;
public interface AccountRepository extends JpaRepository<CustomUserDetails, String>{
public CustomUserDetails findByUsername(String username);
}
As you can see, I don't use any annotation for it. But in other classes, I can access it as a bean ? how is it possible ?
When you are using spring boot and your main application class in a root package above other classes then the bean classes will be scanned and detected automatically and you can look here on this.
The #SpringBootApplication annotation is equivalent to using
#Configuration, #EnableAutoConfiguration and #ComponentScan.
Also, #EnableAutoConfiguration annotation implicitly defines a base
“search package” for certain items.
Check out this annotation #EnableJpaRepositories. You may be using it as a class level annotation on one of your #Configuration class.
From the docs
Annotation to enable JPA repositories. Will scan the package of the annotated configuration class for Spring Data repositories by default.
Example:
#Configuration
#EnableJpaRepositories(basePackages = {"xxx.xxx.xxx.core.dao"})
#EnableTransactionManagement
public class DatabaseConfig{
}

How to declare ExceptionHandlerExceptionResolver in Java code?

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.

Categories