Is it possible to extend WebMvcConfigurationSupport and use WebMvcAutoConfiguration? - java

I need to extend the WebMvcConfigurationSupport class too modify two things:
#Configuration
public class WebConfig extends WebMvcConfigurationSupport {
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
handlerMapping.setRemoveSemicolonContent(false);
handlerMapping.setOrder(1);
return handlerMapping;
}
}
I like the defaults that are registered from the WebMvcAutoConfiguration class but due to the conditional annotation on the class, when I extend the WebMvcConfigurationSupport class it prevents the auto configuration from happening.
#Configuration
#ConditionalOnWebApplication
#ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
WebMvcConfigurerAdapter.class })
#ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
#Order(Ordered.HIGHEST_PRECEDENCE + 10)
#AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
public class WebMvcAutoConfiguration {...}
Is there to have the WebMvcAutoConfiguration class load without having to essentially copy/paste most of the code in that class?
Or is it possible to call RequestMappingHandlerMapping setOrder() and setRemoveSemicolonContent() from somewhere else so I can just use the #EnableWebMvc annotation and have the autoconfiguration class run without any issues?
Thanks in advance!

Extend from DelegatingWebMvcConfiguration instead of WebMvcConfigurationSupport, it will not prevent the autoconfig to take place:
#Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
handlerMapping.setRemoveSemicolonContent(false);
handlerMapping.setOrder(1);
return handlerMapping;
}
}

I managed to customize the RequestMappingHandlerMapping while keeping WebMvcAutoConfiguration using a BeanPostProcessor:
#Configuration
public class RequestMappingConfiguration {
#Bean
public RequestMappingHandlerMappingPostProcessor requestMappingHandlerMappingPostProcessor() {
return new RequestMappingHandlerMappingPostProcessor();
}
public static class RequestMappingHandlerMappingPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerMapping) {
((RequestMappingHandlerMapping) bean).setUseSuffixPatternMatch(false);
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
}
I would be happy if Spring Boot provides a better way to handle that... maybe something could be done around PathMatchConfigurer ?

Your analysis is correct (#EnableWebMvc or directly extending WebMvcConfigurationSupport will switch off the WebMvcAutoConfiguration). I'm not sure what the alternative is, since a) we need a "get-out" clause for the autoconfig, and b) I don't think Spring likes to have two WebMvcConfigurationSupports in the same context. Happy to discuss on github if you want to try and find a way to change it (there might be some middle ground).

I think the best way to do this in Spring Boot now is to add a WebMvcRegistrations component to your context - this solution didn't exist at the time of your question (it's been available since Spring Boot 1.4.0).

Related

about the order of spring bean initialing

I have a custom spring-boot starter project which is used by rest controller, the auto configuration class is used for creating several(according to config value in application.yml) Storage Context instance as spring singleton, so I have to create them dynamically in setBeanFactory method of BeanFactoryAware by :
#Import(StorageContextProperties.class)
public class StorageAutoConfiguration implements BeanFactoryAware {
......
#Override
public void setBeanFactory(BeanFactory factory) throws BeansException {
......
StorageContextProperties config = beanFactory.getBean(StorageContextProperties.class);
config.getProfiles().stream().forEach(p -> {
......
((SingletonBeanRegistry) beanFactory).registerSingleton(profile.name, instance);
the problem is that the method is not been called before the controller #autowired event, so it will complain there is no StorageContext instance, I have also tried BeanFactoryPostProcessor and InitializingBean interface, neither of them works.
But, I notice if I just add some special #Bean method into the auto config class, let's say :
#Import(StorageContextProperties.class)
public class StorageAutoConfiguration implements BeanFactoryAware{
#Bean
public MethodValidationPostProcessor validationPostProcessor2() {
return new MethodValidationPostProcessor();
}
......
#Override
public void setBeanFactory(BeanFactory factory) throws BeansException {
then the setBeanFactory() will be called before the controller, it seems that spring needs MethodValidationPostProcessor instance, so it created shared instance of singleton bean: StorageAutoConfiguration, also create StorageContextProperties instance and call setBeanFactory().
the code works if I add above #Bean method. well things also goes well in this way, but I don't like the style since I actually have no need for MethodValidationPostProcessor.
is there any elegant(without #Bean method) way to achieve it?
my requirements are :
before the controller creating event.
1 create StorageContextProperties ( it's a #ConfigurationProperties class by #Import)
2 some callback I can call registerSingleton() to create my Storage Context instances
Thanks!
[UPDATED1]
I still don't find any way to make it works, but I changed the code as :
#Configuration
#Import(StorageContextProperties.class)
#ConditionalOnProperty(prefix = "avalon.thiton.storage.config", name = "masterKey")
public class StorageAutoConfiguration implements BeanFactoryAware {
private static Logger log = LoggerFactory.getLogger(StorageAutoConfiguration.class);
#Bean
#Order(Ordered.LOWEST_PRECEDENCE)
#ConditionalOnMissingBean(MethodValidationPostProcessor.class)
public MethodValidationPostProcessor placeholder() {
return new MethodValidationPostProcessor();
}
......
It makes me feel..... better.
Hi I've had similar problem when I was trying to initialize custom beans using BeanFactoryAware interface - The #Configuration annotated class was like:
#Configuration
#EnableConfigurationProperties(SomeProps.class)
class MyConfiguration implements BeanFactoryAware {
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
...
...
}
#Bean
MyBean myBean(){
return new MyBean();
}
}
It did not run this configuration class code before MyBean was needed and NoSuchBeanDefinitionException were thrown.
To fix this I've changed BeanFactoryAware interface to extending AbstractBeanFactoryAwareAdvisingPostProcessor like that:
#Configuration
#EnableConfigurationProperties(SomeProps.class)
class MyConfiguration extends AbstractBeanFactoryAwareAdvisingPostProcessor {
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
...
...
}
#Bean
MyBean myBean(){
return new MyBean();
}
}

Cannot autowire service into implementation of AsyncUncaughtExceptionHandler

I have a problem with spring autowiring. I've got a class that implements AsyncUncaughtExceptionHandler.
#Component
public class MyExceptionHandler implements AsyncUncaughtExceptionHandler {
#Autowired
MyService myService;
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
if (throwable instanceof MyException) {
myService.handleException((MyException) throwable);
} else {
//...
}
}
When the code runs into the places where myService is called, Nullpointer is thrown as myService is null. MyService is autowired on other places without any problem, only autowiring in the implementation of AsyncUncaughtExceptionHandler seems to cause issues.
#Service
public class MyService {
public void handleException(MyException e) {
//...
}
}
I've been through some questions on StackOverflow, which solved similar problems. Those problems were caused by proxy mechanisms, but I did not manage to make the code work. Also, I am not sure if this is the problem.
Any help is appreciated. Thanks in advance.
You need to mark MyExceptionHandler as a bean as well:
#Component
public class MyExceptionHandler implements AsyncUncaughtExceptionHandler {
Mistake was actually on my side in configuration of asyncExceptionHandler.
had to change this:
#Configuration
#EnableAsync
#ComponentScan
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new RccAsyncExceptionHandler();
}
}
into this:
#Configuration
#EnableAsync
#ComponentScan
public class AsyncConfiguration implements AsyncConfigurer {
#Autowired
MyExceptionHandler rccAsyncExceptionHandler;
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return rccAsyncExceptionHandler;
}
}
Rookie mistake, but I couldn't find it for very long time.
What is happening here is,
The async code is run on a separate thread(Hence #ComponentScan to
see and initialize new threads).
By default all beans are singleton (see bean scope in spring for more details). Since the new thread runs in a different container. It wont get the reference of the service bean and hence null.
Why did #ComponentScan not handle it?
Component scan is mostly scanning a subset of packages and the
service may be defined in a different package.
Solution:
Make the service bean application scope using #ApplicationScope annotation.

Add some extra configuration to beans defined in Spring starters

TL;DR
What is the best way to add some configuration to already created beans, e.g. to bean created by Spring Auto Configuration mechanism?
UseCase
I'm trying to configure a ContentNegotiatingViewResolver in a best possible way. I tried to create a new instance of that object in my MvcConfiguration class, and configure everything in that place. It worked, but I was thinking about something more elegant.
And I found a WebMvcAutoConfiguration, with viewResolver(BeanFactory beanFactory) method, that creates a ContentNegotiatingViewResolver bean. I decided I would like to use this, since it's better to use existing code, than duplicate it.
But how can I add some more configuration to that bean? I tried with something like this:
#Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
#Autowired
private ContentNegotiatingViewResolver contentNegotiatingViewResolver;
#Autowired
private ThymeleafViewResolver thymeleafViewResolver;
#Bean
public ViewResolver jsonViewResolver() {
return new JsonViewResolver();
}
#PostConstruct
public void postConstruct() {
contentNegotiatingViewResolver.setViewResolvers(Arrays.asList(jsonViewResolver(), thymeleafViewResolver));
}
}
and configure everything in #PostConstruct method but I wonder, if it is the best way.
Make use of Autowiring by constructor.
#Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
private ContentNegotiatingViewResolver contentNegotiatingViewResolver;
private ThymeleafViewResolver thymeleafViewResolver;
#Autowired
public MvcConfiguration(ContentNegotiatingViewResolver contentNegotiatingViewResolver,ThymeleafViewResolver thymeleafViewResolver){
this.contentNegotiatingViewResolver=contentNegotiatingViewResolver;
this.thymeleafViewResolver=thymeleafViewResolver;
ViewResolver jsonViewResolver= new JsonViewResolver();
this.contentNegotiatingViewResolver.setViewResolvers(Arrays.asList(jsonViewResolver, thymeleafViewResolver));
}
}
Alternate solution :
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation();
registry.viewResolver(jsonViewResolver());
super.configureViewResolvers(registry );
}
}

Spring annotations confusion

i am really confused with spring annotations.
where to use # Autowired, where class is # Bean or # Component,
i understand we cannot use
Example example=new Example("String");
in Spring
but how alone
#Autowired
Example example;
will solve the purpose?
what about Example Constructor ,how spring will provide String value to Example Constructor?
i went through one of the article but it does not make much sense to me.
it would be great if some one can give me just brief and simple explanation.
Spring doesn't say you can't do Example example = new Example("String"); That is still perfectly legal if Example does not need to be a singleton bean. Where #Autowired and #Bean come into play is when you want to instantiate a class as a singleton. In Spring, any bean you annotate with #Service, #Component or #Repository would get automatically registered as a singleton bean as long as your component scanning is setup correctly. The option of using #Bean allows you to define these singletons without annotating the classes explicitly. Instead you would create a class, annotate it with #Configuration and within that class, define one or more #Bean definitions.
So instead of
#Component
public class MyService {
public MyService() {}
}
You could have
public class MyService {
public MyService() {}
}
#Configuration
public class Application {
#Bean
public MyService myService() {
return new MyService();
}
#Autowired
#Bean
public MyOtherService myOtherService(MyService myService) {
return new MyOtherService();
}
}
The trade-off is having your beans defined in one place vs annotating individual classes. I typically use both depending on what I need.
You will first define a bean of type example:
<beans>
<bean name="example" class="Example">
<constructor-arg value="String">
</bean>
</beans>
or in Java code as:
#Bean
public Example example() {
return new Example("String");
}
Now when you use #Autowired the spring container will inject the bean created above into the parent bean.
Default constructor + #Component - Annotation is enough to get #Autowired work:
#Component
public class Example {
public Example(){
this.str = "string";
}
}
You should never instantiate a concrete implementation via #Bean declaration. Always do something like this:
public interface MyApiInterface{
void doSomeOperation();
}
#Component
public class MyApiV1 implements MyApiInterface {
public void doSomeOperation() {...}
}
And now you can use it in your code:
#Autowired
private MyApiInterface _api; // spring will AUTOmaticaly find the implementation

How to disable Spring autowiring in unit tests for #Configuration/#Bean usage

I want configure a component test using spring-test configuration inner class (#Configuration). Tested components has some services which I'd like to mock for the test. These services are classes (no interface used) and have spring annotations (#Autowired) in them. Mockito can easily mock them, however, I found no way of disabling spring autowiring.
Example how I can easily reproduce:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Test
public void test() {
}
#Configuration
#ImportResource("/spring/component-config.xml")
static class Beans {
#Bean
ThirdPartyService createThirdPartyService() {
return mock(ThirdPartyService.class);
}
}
}
public class ThirdPartyService {
#Autowired
Foo bar;
}
public class TestedBean {
#Autowired
private ThirdPartyService service;
}
In this example "TestBean" represents the service to be mocked. I would NOT like "bar" to be injected by spring! #Bean(autowire = NO) does not help (in fact, that's the default value).
(Please save me from "use interfaces!" comments - the mocked service can be 3rd party which I can't do anything with.)
UPDATE
Springockito partially solves the problem, as long as you don't have to have anything else to configure (so you can't use configuration class with Springockito - it does not support it), but use mocks only.
Still looking for pure spring solution, if there's any...
Here is my solution to your problem:
import static org.mockito.Mockito.mockingDetails;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MockitoSkipAutowireConfiguration {
#Bean MockBeanFactory mockBeanFactory() {
return new MockBeanFactory();
}
private static class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
#Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockingDetails(bean).isMock();
}
}
}
and then just
#Import(MockitoSkipAutowireConfiguration.class)
in your test #Configuration and you are all set
I solved it by creating FactoryBean for my bean instead of just mocking bean. At this way Spring don't try to autowire fields.
Factory bean helping class:
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private final Class<T> clazz;
public MockitoFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
#Override public T getObject() throws Exception {
return mock(clazz);
}
#Override public Class<T> getObjectType() {
return clazz;
}
#Override public boolean isSingleton() {
return true;
}
}
Actual test context part:
#Configuration
public class TestContext {
#Bean
public FactoryBean<MockingService> mockingService() {
return new MockitoFactoryBean<>(MockingService.class);
}
}
Check Spring profiles. You don't need to disable auto wiring, you need to inject different beans for different configuration.
You could add the mocked service manually to the spring application context via org.springframework.beans.factory.config.SingletonBeanRegistry#registerSingleton. This way the mock is not post-processed by spring and spring does not attempt to autowire the mock. The mock itself will be injected into your tested bean.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Autowired
private ThirdPartyService thirdPartyServiceMock;
#Test
public void test() {
}
#Configuration
static class Beans {
#Autowired
private GenericApplicationContext ctx;
#Bean
TestedBean testedBean() {
ctx.getBeanFactory().registerSingleton("thirdPartyService", mock(ThirdPartyService.class));
return new TestedBean();
}
}
public static class ThirdPartyService {
#Autowired
Object bar;
}
public static class TestedBean {
#Autowired
private ThirdPartyService service;
}
}
I am in quite the same situation.
What I found that if you do not set the context loader by #ContextConfiguration annotation on your test class, the default context loader will be used, which derived from AbstractGenericContextLoader. I had a look at its source and turned out it registers all the bean post processors which are responsible for reading annotations such #Autowired. In other words, annotation config is enabled by default.
So the main problem is that there are two configurations which are in conflict: in the java config we said that autowiring is not needed, while the autowired annotation tells the opposite. The real question is how to disable the annotation processing in order to eliminate the undesired configuration.
As far as I know there is no such spring implementation of ContextLoader which would not be derived from AbstractGenericContextLoader so I guess the only we can do is to write our own. It would be something like this:
public static class SimpleContextLoader implements ContextLoader {
#Override
public String[] processLocations(Class<?> type, String... locations) {
return strings;
}
#Override
public ApplicationContext loadContext(String... locations) throws Exception {
// in case of xml configuration
return new ClassPathXmlApplicationContext(strings);
// in case of java configuration (but its name is quite misleading)
// return new AnnotationConfigApplicationContext(TestConfig.class);
}
}
Of course it would be worth to spend more time to find out how to implement ContextLoader properly.
Cheers,
Robert
There are so many ways of doing this, I'm pretty sure that this answer will be incomplete, but here are a few options...
As currently seems to be recommended practice, use constructor injection for your services rather than autowiring the fields directly. This makes testing like this so much easier.
public class SomeTest {
#Mock
private ThirdPartyService mockedBean;
#Before
public void init() {
initMocks(this);
}
#Test
public void test() {
BeanUnderTest bean = new BeanUnderTest(mockedBean);
// ...
}
}
public class BeanUnderTest{
private ThirdPartyService service;
#Autowired
public BeanUnderTest(ThirdPartyService ThirdPartyService) {
this.thirdPartyService = thirdPartyService;
}
}
By doing that, you can also mix up autowired and mocked services by autowiring into the test itself and then constructing the beans under test with the most useful mix of autowired and mocked beans.
A reasonable alternative is to use Spring profiles to define stub services. This is particularly useful when wish to use the same stubbed features in multiple tests:
#Service
#Primary
#Profile("test")
public class MyServiceStub implements MyService {
// ...
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
#ActiveProfiles({"test"})
public class SomeTest {
// ...
}
By using the #Primary annotation, it ensures that this stub bean will be used instead of any other bean implementing the MyService interface. I tend to use this approach for things like email services, where by changing profile, I'm able to switch between a real mail server and Wiser.

Categories