I would like to add resource handlers using WebMvcConfigurerAdapter in Windows, but in Linux it doesn't work, so I add WebMvcConfigurationSupport.
After debug and test I find two bean will be create in both OS, but the override function of WebMvcConfigurerAdapter will be executed only at Windows and the override function of WebMvcConfigurationSupport will be executed only at Linux.
I can't find out the reason. The two configuration classes are shown below:
#Configuration
public class JxWebAppConfigurer extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("file:"+System.getProperty("user.dir")+"/src/main/webapp/");
super.addResourceHandlers(registry);
}
}
This is the other one:
#Configuration
public class JxWebConfiguration extends WebMvcConfigurationSupport {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("file:"+System.getProperty("user.dir")+"/src/main/webapp/");
super.addResourceHandlers(registry);
}
}
#EnalbeMvc is already been added at the main class
As mentioned in the #EnableWebMvc Documentation:
Adding this annotation to an #Configuration class imports the Spring
MVC configuration from WebMvcConfigurationSupport
{..}
To customize the imported configuration, implement the interface
WebMvcConfigurer or more likely extend the empty method base class
WebMvcConfigurerAdapter and override individual methods
{..}
If WebMvcConfigurer does not expose some advanced setting that needs
to be configured, consider removing the #EnableWebMvc annotation and
extending directly from WebMvcConfigurationSupport
So in effect either:
#EnableWebMvc + extending WebMvcConfigurerAdapter (suggested first option)
Extending directly from WebMvcConfigurationSupport (fallback alternative for full control)
(on both cases needed #Configuration)
The alternative solution for org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapteris org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport (I am using Spring Framework 5.0.2.RELEASE).
WebMvcConfigurerAdapter has been deprecated.
I know the reasons. As mentioned above,you should choose one select(extends WebMvcConfigurerAdapter+#EnableWebMvc or just extends WebMvcConfigurationSupport ) ;
Never use #EnableWebMvc and extending WebMvcConfigurationSupport
together!!
if use spring-boot's #EnableAutoConfiguration ,you can just extends WebMvcConfigurerAdapter and don't use #EnableMvc
Behavior differences on OS are not clear to me (classpath order?) so I'll just talk about WebMvcConfigurationSupport vs WebMvcConfigurer. Let's start with WebMvcConfigurerAdapter that implements WebMvcConfigurer, but now is deprecated because the interface has the functionality via default methods.
Now to the "support" (extending WebMvcConfigurationSupport) vs "configurer" (implementing WebMvcConfigurer). These classes have very similar methods but it works roughly like this:
Support component finds all the configurers and combines them into the final configuration.
I recently wrote quite a long post about this with code examples and I recommend experimenting with small Spring Boot applications and debug them - it's eye opening.
Boot has a default implementation of WebMvcConfigurationSupport and it does a lot of stuff - including finding beans implementing WebMvcConfigurer and using them. If you take over and implement the support class, Boot will find it, disables the default one and you're in full control. But then a lot of default auto-magic is gone and you have to use it if needed.
Related
When using Spring Web, in this case for rest endpoints & using Spring Boot 2, I can configure interceptors for my app using by implementing the WebMvcConfigurer interface:
#Configuration
public class SpringWebConfig implements WebMvcConfigurer
{
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor).addPathPatterns("/api/endpoint/**");
}
}
I added this interceptor to most of my apps in an automatic fashion by doing the following:
Create a "common-jar", and put the above interface under package
com.company.api.
In every app, add the package com.company.api to
the api scan.
This common package also contains the Interceptor and utility classes to make this interceptor work, so in effect, adding this common-jar would automatically add he interceptor to all operations in the app, which is a similar concept as to what Spring itself does: adding dependencies changes the default configuration of Spring.
The problem I'm facing now is this approach cannot be extended to a second interceptor in a second jar, because I already used the WebMvcConfigurer implementation. and I cannot have two.
I was thinking about maybe using some kind of composite-configurer pattern where we loop over every configurer, collect all interceptors, and then add them once, but unfortunately Spring doesn't allow this. What are my options?
Currently, the approach I took is duplicating the WebMvcConfigurer interface in every app that requires it. I feel sad when something changes, and I have to change the same snippet of code in every single app.
If I understand your question correctly , basically you want to define some common Interceptors in multiple JARs such that an application can activate these Interceptors by simply including these JARs into their app ?
I was thinking about maybe using some kind of composite-configurer
pattern where we loop over every configurer, collect all interceptors,
and then add them once, but unfortunately Spring doesn't allow this.
What are my options?
Well, if implementation A returns a registry with only interceptor A,
and implementation B returns a registry with only interceptor B, would
spring combine both registries into one super registry containing both
A and B, or would it just pick one, or would it throw an error that
there was no unique bean definition ?
Actually , Spring has already implement this feature. When there are multiple WebMvcConfigurer beans , Spring simply loop them one by one and calls their configuration methods. So the end-result is that InterceptorRegistry will contain all interceptors.
If the client application need to activate certain WebMvcConfigurer only, it can simply exclude those JARs containing the WebMvcConfigurer that they don't want.
To take this idea further which allow the application to control which Interceptors to activate down to interceptor level , you could even do the following in each common JAR :
#Configuration
public class SpringWebConfig implements WebMvcConfigurer {
//Make sure the HandlerInterceptor implementation in this JAR is a bean (e.g mark it as #Component)
#Autowired
private List<HandlerInterceptor> interceptors;
#Override
public void addInterceptors(InterceptorRegistry registry) {
for(HandlerInterceptor interceptor : interceptors){
registry.addInterceptor(interceptor).addPathPatterns("/api/endpoint/**");
}
}
}
In the client application , use includeFilters / excludeFilters in #ComponentScan to customise which to interceptors to include. For example, to disable certain Interceptors, you could do :
#ComponentScan(
basePackages = {"com.company.api"},
excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=com.company.common.jar1.Inteceptor1.class) ,
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=com.company.common.jar2.Inteceptor1.class)
})
If I understand your problem correctly, you don't want to implement all the methods of WebMvcConfigurer in every app. You just want to add relevant interceptors and be done with it.
My approach would be to create an AbstractWebMvcConfigurerImpl by implementing the WebMvcConfigurer in the Common module. Just leave the addInterceptors() abstract and implement other methods. You can then extend that abstract implementation in your every Spring Boot project and just override the addInterceptors() method as per your need.
Also, you can have as many implementations of WebMvcConfigurer as you want in a Spring project. So, if you need to define some common interceptors in the Common module, you can extend the AbstractWebMvcConfigurerImpl in the common module also.
Please note that all your implementations of AbstractWebMvcConfigurerImpl should be annotated with #Configuration
How to configure an Interceptor, through Annotation only(I do NOT like to register the interceptor in .XML file, I do not use .XML based configuration)?
Note : I see in example on internet it is suggesting to use org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter, when I tried to use it, I found it is DEPRECATED
I am testing on SpringWebMVC-5 with SpringBoot-2
In Spring5 you can use org.springframework.web.servlet.config.annotation.WebMvcConfigurer:
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(....);
}
}
There are several features in Spring that are sort of black box for me.
In this case, I'm playing with websockets and there is #Configuration class implementing or extending something with overrided methods that expects instantiated objects as parameters.
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
Method configureMessageBroker expects instance of MessageBrokerRegistry class, but there isn't any bean configuration in the whole project.
My question is, where does Spring get an instance of those classes?
This is not only for #EnableWebSocketMessageBroker but for any other spring configuration. What you need to understand is when you run the project, spring creates the required objects, in this case MessageBrokerRegistry, inject it, then pass it to configureMessageBroker and call the method. This is the reason you add #Configuration annotation to a class. This annotation tells spring to initialize the related environment during spring initialization.
Another example:
#Configuration
#EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Now here we need DefaultServletHandlerConfigurer and it is created and managed completely by spring.The configureDefaultServletHandling() method is overridden and we enable default servlet handler. This will let other http request such as .css, .js slip through the usual DispatcherServlet and let the container process them. So now we can serve the static files css and javascript from our WebApp folder.
In short, #Configuration tells spring to set up the environment, so all the method in a class annotated with #Configuration are for initialization and are only and only for spring to manage.
As pointed out by Esther Álvarez Feijoo, you can understand it better by debugging.
#Configuration annotates that class also as a #Component (See the code of #Configuration, it has that annotation). This way, an instance of your WebSocketConfig is available in the IoC container.
The method "configureMessageBroker" is not magically invoked just because of that, and the argument "MessageBrokerRegistry registry" is not injected. It's just a regular method, that anybody with the class instance can invoke, passing the suitable argument
When running your app, in some step of spring initialization, spring will need to configure the web socket utilities, and will look for a implementation of "AbstractWebSocketMessageBrokerConfigurer", or some or its interfaces. It'll find your class instance, and use it for that configuration, passing the necessary parameters to the methods, because Spring already have them.
You can see this behaviour much better with the debugger. Put a break point in your method, and see the call stack. You can do reverse engineering and see how your method is invoked.
i am new to springboot application development and i generated my project with the help of this url https://start.spring.io/ and when i open this project in my IDE i had 2 classes generated
this is the first class
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(TravellingApplication.class);
}}
and this is the second class
#SpringBootApplication
public class TravellingApplication {
public static void main(String[] args) {
SpringApplication.run(TravellingApplication.class, args);
}}
i really don't get it whats happening inside the configure method in my Servletinitializer class.
i can write better code configuration if i delete both of the classes
and do something like this,
class simmilar to dispatcherservlet.xml
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.travelliing")
public class WebConfig extends WebMvcConfigurerAdapter {
}
class simmilar to web.xml
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException { }
}
correct me if i am wrong. i think both ServletInitializer class and webAppInitializer is capable of same functionalities since the somehow implement WebApplicationInitializer.
except for the configure method in servletInitializer class.
whats happening with the travellingApplication class annotated with #SpringBootApplication is it simmilar to my webConfig Class which extends WebMvcConfigureAdapter
Both classes load the Spring application context.
The class with the main method (TravellingApplication) will be used if you run your application as normal java application. For example if you do Run As -> Java applciatnion from Eclipse or if you package the application as a jar and run java -jar myApp.jar from the command line.
SpringBootServletInitializer will be used to load the application context if you package the application as a war file and deploy it in Tomcat or another web server that supports Servlet 3.0+. It basically replaces the web.xml.
i really don't get it whats happening inside the configure method in
my Servletinitializer class.
TravellingApplication is a #Configuration class - it declares Spring beans and other Spring configuration, so this line - return application.sources(TravellingApplication.class); just loads this configuration (application context). The same thing that happens in the main method.
whats happening with the travellingApplication class annotated with
#SpringBootApplication is it simmilar to my webConfig Class which
extends WebMvcConfigureAdapter
#SpringBootApplication is just a shortcut to
#Configuration
#EnableAutoConfiguration
#ComponentScan
See here.
Context:
I switched from XML based to Java based Spring configuration. My application has a JSP based web layer, Spring MVC, Spring Security and Hibernate as persistence provider.
I managed to separate the whole XML configuration in different config classes:
WebConfig - for the Spring MVC configurations;
PersistenceConfig - as the name states - for the JPA configuration;
ServiceConfig - only for the #Service and #Component annotated classes;
SecurityConfig - for the Spring Security Configuration.
For application initialization I have the SecurityInitializer and WebAppInitializer classes.
Here is some code:
WebConfig
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.demo.app.web"})
public class WebConfig extends WebMvcConfigurerAdapter { /* Bean initialization */ }
PersistenceConfig
#Configuration
#ComponentScan(basePackages = {"com.demo.app.dao"})
#EnableTransactionManagement(mode = AdviceMode.PROXY, proxyTargetClass = true)
public class PersistenceConfig { /* Bean initialization */ }
ServiceConfig
#Configuration
#ComponentScan(basePackages = {"com.demo.app.service", "com.demo.app.component"})
public class ServiceConfig { /* Bean initialization */ }
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { /* Bean initialization */ }
SecurityInitializer
#Order(1)
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
WebAppInitializer
#Order(2)
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {SecurityConfig.class, PersistenceConfig.class,
ServiceConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
And having to test the whole thing I have:
TestContext - abstract class that I think it sets up the basic context;
TestWebContext - extends TestContext and adds the WebCOnfig context. It's extended by all Controller tests;
DaoTest - extends TestContext and adds transaction management. It's extended by all DAO tests;
TestContext
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {PersistenceConfig.class, ServiceConfig.class, SecurityConfig.class})
public abstract class TestContext {
}
TestWebContext
#ContextConfiguration(classes = {WebConfig.class})
#WebAppConfiguration
public abstract class TestWebContext extends TestContext {
}
DaoTest
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
#Transactional
public abstract class DaoTest extends TestContext {
}
Questions:
When should I put the WebConfig.class in the getServletConfigClasses() or in the getRootConfigClasses() or both? What is the difference?
Does it matter what is the order of the classes that are present in the getRootConfigClasses() and getServletConfigClasses() methods? I've seen somewhere that order matters for the initializers and people put #Order at them but what about the Config classes?
For the TestWebContext class I know that just adding #ContextConfiguration(classes = {WebConfig.class}) overrides the #ContextConfiguration from the base class but how can I achieve the context extension?
If I add another configuration class say CoreConfig (I had one). Then load spring application context from XML in it and add it to the classes in getRootConfigClasses():
Note: no duplicate beans with Config classes are present in the applicationContext.xml.
CoreConfig
#Configuration
#EnableScheduling
#ImportResource("classpath:applicationContext.xml")
public class CoreConfig { // No duplicate Beans load }
Which beans are loaded at first? The ones in applicationContext.xml or the ones from the Config classes?
Any other tips from practice that worked for you about the Java configuration are also highly appreciated!
The WebConfig is responsible for the servlet related beans and therefore loaded in the servlet context. Other beans that could be potentially shared from other servlets in the same application can go in the root context. A good start for me to learn about the difference between the contexts was What is the difference between ApplicationContext and WebApplicationContext in Spring MVC?
Practically it does not matter. Changing the order does not make any difference in my applications. I could not find any theoretical proof documented somewhere though
Have a look at
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/ContextHierarchy.html which contains code examples on how to "merge" the context configurations between parent-children
It shouldn't really matter since spring first loads all bean definitions and only afterwards it instantiates them by performing the dependency-injection in an order where beans that are properties or constructor arguments of other beans are initialized first. In cases where a bean depends to another but there is no property or constructor dependency between them for spring to understand the order, you can make use of the "depends-on" attribute
Regarding the usage of the config files I actually use a similar approach with yours. A change that you may find useful could be to only have a RootConfig.class loaded from the getRootConfigClasses() , and that RootConfig may import the SecurityConfig.class, PersistenceConfig.class and ServiceConfig.class along with any other functionality it may have. For example in my case it also loads an application.properties file using #PropertySource("classpath:application.properties") annotation and containing a PropertySourcesPlaceholderConfigurer bean