Spring addFormatters not called of WebMvcConfigurerAdapter - java

I'm trying to implement a converter in a Spring Boot app but for some reason my override of addFormatters(FormatterRegistry formatterRegistry) is never called. What's confusing is that other overridden methods are called, addInterceptors works just fine. Security is enabled for this app.
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {"com.company.web"})
public class WebMvcConfig extends WebMvcConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
#Inject
private TenantIdentifierInterceptorAdapter multiTenancyInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
logger.info("adding interceptor");
registry.addInterceptor(multiTenancyInterceptor);
}
//THIS IS NOT CALLED
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
logger.info("adding converters");
formatterRegistry.addConverter(new StringToPersonConverter());
}
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {"classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS)
.setCachePeriod(3600).resourceChain(true).addResolver(new PathResourceResolver());
}
#PostConstruct
public void init() {
requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(true);
}
}
For some reason if I add this code to the file the formatter code is hit, but I get an error "A ServletContext is required to configure default servlet handling" and the app won't compile.
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}

Have you configured a bean with super class WebMvcConfigurationSupport?
If you did so, it will disable WebMvcAutoConfiguration Bootstrap.
Check this code:
#Configuration
#ConditionalOnWebApplication(
type = Type.SERVLET
)
#ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
#ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
#AutoConfigureOrder(-2147483638)
#AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {

I'm not 100% sure but did you try to remove #EnableWebMvc annotation from the class? Because according to documentation it shouldn't be there:
If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own #Configuration class of type WebMvcConfigurerAdapter, but without #EnableWebMvc.

M. Deinum's comment was the answer. I've removed #EnableWebMvc and removed the method addFormatters, then added:
#Bean
StringToPersonConverter stringToPersonConverter() {
return new StringToPersonConverter();
}

I have solved a similar error
EnableWebMvc
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Documented
#Import(DelegatingWebMvcConfiguration.class)
public #interface EnableWebMvc {
}
DelegatingWebMvcConfiguration
#Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
#Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
//...
#Override
protected void addFormatters(FormatterRegistry registry) {
this.configurers.addFormatters(registry);
}
//...
}
WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
//...
#Bean
public FormattingConversionService mvcConversionService() {
FormattingConversionService conversionService = new DefaultFormattingConversionService();
addFormatters(conversionService);
return conversionService;
}
/**
* Override this method to add custom {#link Converter}s and {#link Formatter}s.
*/
protected void addFormatters(FormatterRegistry registry) {
}
//...
}
My custom config with multiple WebMvcConfigurerAdapter implements WebMvcConfigurer
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter
{
#Autowired
private ConversionService conversionService;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers)
{
argumentResolvers.add(new RequestTenantResolverComposite(conversionService));
}
}
The method will not be called if the FormattingConversionService implements ConversionService bean created before WebMvcConfigurer injected.
I solved my question using Lazy annotion for ConversionService with spring verions 4.3.6.

Related

#Order annotation vs FilterRegistrationBean.setOrder vs addFilterBefore/addFilterAfter in WebSecurityConfigurerAdapter.configure

I am adding filters in my application and I want to set the order of call for these filters. I can see three ways but I don't know if they're at all related?
Using #Order annotation:
#Component
#Order(1)
public class SampleFilter extends OncePerRequestFilter {
...
}
Register the filter via config:
#Configuration
public class FilterConfig {
#Bean
public FilterRegistrationBean<SampleFilter> perfFilter() {
FilterRegistrationBean<SampleFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new SampleFilter());
registration.setOrder(1);
return registration;
}
}
Via WebSecurityConfigurerAdapter:
public class SampleConfig extends WebSecurityConfigurerAdapter {
#Autowired
private SampleFilter sampleFilter;
#Override
public void configure(HttpSecurity http) throws Exception {
...
http.addFilterBefore(this.sampleFilter, SampleFilterTwo.class);
}
}

#autowired annotation for service class is not working in #configure class spring boot

when i am using #autowire to inject my dependencies in Configuration
class its giving me as null please refer the code below .
#Configuration
public class DataSourceConfig {
#Autowired
AppService appService;
#Bean
public BeanDefinitionRegistryPostProcessor beanPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanRegistry) throws BeansException {
createBeans(beanRegistry);
}
};
}
private void createBeans(BeanDefinitionRegistry beanRegistry,DataSourceConfigService ds) {
appService.getDbDetails();
appService is null here if i will call it using this way
BeanDefinitionRegistryPostProcessor beanPostProcessor(AppService
appService) then in AppServiceImpl class AppDao dependency will be null
}
}
//// Service
#Service
public class AppServiceImpl implements AppService{
#Autowired
AppDao ds;
#Override
public List<A> getDatabaseConfiguration() {
return ds.getDbDetails(); // here ds is null
}
}
//dao
#Repository
public class AppDaoImpl implements AppDao {
#Qualifier("nameParamJdbcTemplate")
#Autowired
public NamedParameterJdbcTemplate nameParamJdbcTemplate;
#Override
public List<A> getDbDetails() {
return nameParamJdbcTemplate.query(SELECT_QUERY, new DataSourceMapper()); // nameParamJdbcTemplate is null
}
// datasource config
#Configuration
public class DataSourceBuilderConfig {
#Bean(name = "dbSource")
#ConfigurationProperties(prefix = "datasource")
#Primary
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean(name = "nameParamJdbcTemplate")
#DependsOn("dbSource")
#Autowired
public NamedParameterJdbcTemplate jdbcTemplate1(#Qualifier("dbSource") DataSource dbSource) {
return new NamedParameterJdbcTemplate(dbSource);
}
}
What i want is when ever my beanPostProcessor()
is executed i want all my dependent beans should be instantiated ie
#Autowired
AppService appService;
#Autowired
AppDao ds;
#Qualifier("nameParamJdbcTemplate")
#Autowired
public NamedParameterJdbcTemplate nameParamJdbcTemplate;
I am new to spring so any help or working examples would be great. Thanks
It is null because this #Configuration class also defines a BeanDefinitionRegistryPostProcessor that forces the context to create that bean very early on.
Because you are using field injection, the context has to resolve AppService bean but it can't yet because the post-processor have to be applied first.
Your configuration looks very complex so you may want to simplify it a bit:
Separate low-level infrastructure configuration from main configuration
Always define such post processor as public static method so that the context can invoke the #Bean method without having to construct the class first.

Why cannot I use #ConfigurationProperties instead of #Value in Spring WebConfig

I want to configure my WebConfig bean to have different CORS origin depending on the profileapplication.properties.
For some reason the value in my application-local.properties is not injected into the field of my WebConfig bean.
When I use #Value here it will work (please check last headline for the working example)
Entry Class
#SpringBootApplication
#EnableEurekaClient
#ComponentScan("com.company.coma")
public class EventWebApplication {
public static void main(String[] args) {
SpringApplication.run(EventWebApplication.class, args);
}
}
WebConfig.java
#Configuration
#ConfigurationProperties("coma.cors")
public class WebConfig extends WebMvcConfigurerAdapter {
private String origin;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping(Resources.Services.APIs.Event.GET_STATUS + "/**")
.allowedOrigins(origin);
}
}
application-local.properties
coma.cors.origin=http://localhost:8080
Execution
starting the application with -Dspring.profiles.active=local
Result
Working example
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Value("${coma.cors.origin}") // this works
private String origin;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping(Resources.Services.APIs.Event.GET_STATUS + "/**")
.allowedOrigins(origin);
}
}

Turn POJO to #Configuration programmatically Spring

Given a class:
class MyConfiguration {
#Bean
String bean() {
return new String();
}
}
as you may notice it does not have #Configuration annotation.
How can I make it behave like it has #Configuration annotation, but not adding it?
#Bean annotation should not work in Lite Mode, https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html
Something like:
#Configuration
class MainConfiguration {
#Bean
MyConfiguration myConfiguration() {
MyConfiguration myConfiguration = do_some_spring_magic();
// myConfiguration behaving like it's having #Configuration here
return myConfiguration;
}
}
I dont understand what is the use case you are trying to achieve here.. But if you are looking for programatically registering beans into the Spring Context then you can do it as below.
#Configuration
public class MyBeanRegisterFactory implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanRegistry) throws BeansException {
//depending on some condition you can do the below line
beanRegistry.registerBeanDefinition("myBeanClass", new RootBeanDefinition("com.mybean.MyBeanClass"));
}
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
In Your case, the bean definitions inside the class MyConfiguration can be programatically registered as below into to the spring context.
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/support/BeanDefinitionRegistryPostProcessor.html

How to inject session scope bean in interceptor using java config with Spring

I'm developping a full Spring 3.1 and servlet 3.0 application. But my interceptor has a private properties scoped as proxy:
public class MyInterceptor extends HandlerInterceptorAdapter {
#Ressource
private UserSession userSession;
}
The user session is defined as:
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserSession implements Serializable {
....
}
When using this user session in the interceptor, java throws a NullPointerException for userSession.
I think the problem comes from my javaconfig oriented spring configuration:
#EnableWebMvc
#ComponentScan(basePackages = {"com.whatever"})
#Configuration
public class WebAppConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
...
}
The problem is that interceptor is manually instantied, so without instrumentation for the session proxy mechanism. Thus, the question is: "how can I let Spring detect and instance the scope session proxy bean in the java config of my application ?
What you posted works, but you aren't taking advantage of Spring's autowiring capabilities. What if you had many services or other beans to inject into your MyInterceptor bean.
Instead just make a #Bean method for your MyInterceptor
#EnableWebMvc
#ComponentScan(basePackages = {"com.whatever"})
#Configuration
public class WebAppConfig extends WebMvcConfigurerAdapter {
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserSession userSession() {
return new UserSession();
}
#Bean
public MyInterceptor myInterceptor() {
return new MyInterceptor(); // let Spring go nuts injecting stuff
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor()); // will always refer to the same object returned once by myInterceptor()
}
...
}
The solution is to use annotation in the java config class:
#EnableWebMvc
#ComponentScan(basePackages = {"com.whatever"})
#Configuration
public class WebAppConfig extends WebMvcConfigurerAdapter {
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserSession userSession() {
return new UserSession();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
MyInterceptor interceptor = new MyInterceptor();
interceptor.setUserSession(userSession());
registry.addInterceptor(interceptor);
}
...
}
And you need to remove #Scope and #Bean annotation from UserSession class.

Categories

Resources