(regarding Interceptors) Difference between spring #Controller and #RestController - java

I know there is a question about Difference between spring #Controller and #RestController, also I know the difference about the two annotations.
My question is regarding interceptors only, is there any difference between the two annotations when we define interceptors.
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TemplateMappingInterceptor()).addPathPatterns("/**", "/admin-functions**").excludePathPatterns("/login**");
}
}

#RestController is a newer annotation introduced to simplify building of rest controllers. It includes the #Controller and #ResponseBody annotations.
In a wide point of view, they both do the same thing in the end, one in a simpler way. Interceptors operate on a higher level and are independent of the inner workings of a rest controller, so nope, there is no difference.

Related

Define a Spring RestController via Java configuration

Is it possible to Define a Spring RestController (#RestController annotated class) solely in the Java Configuration (the class with #Configuration annotated in the method marked with #Bean)?
I have an application managed by spring boot (the version doesn't matter for the sake of the question, even the last one available). This application exposes some endpoints with REST, so there are several rest controllers, which in turn call the services (as usual).
Now depending on configuration (property in application.yml) I would like to avoid starting some services and, say 2 classes annotated with #RestController annotation because they deal with the "feature X" that I want to exclude.
I would like to configure all my beans via Java configuration, and this is a requirement. So my initial approach was to define all the beans (controllers and services) in a separate configuration which is found by spring boot during the scanning) and put a #ConditionalOnProperty on the configuration so that it will appear in one place:
#Configuration
public class MyAppGeneralConfiguration {
// here I define all the beans that are not relevant for "feature X"
#Bean
public ServiceA serviceA() {}
...
}
#Configuration
#ConditionalOnProperty(name = "myapp.featureX.enabled", havingValue = "true")
public class MyAppFeatureXConfiguration {
// here I will define all the beans relevant for feature X:
#Bean
public ServiceForFeatureX1 serviceForFeatureX1() {}
#Bean
public ServiceForFeatureX2 serviceForFeatureX2() {}
}
With this approach My services do not have any spring annotations at all and I don't use #Autowired annotation as everything is injected via the constructors in #Configuration class:
// no #Service / #Component annotation
public class ServiceForFeatureX1 {}
Now my question is about the classes annotated with #RestContoller annotation. Say I have 2 Controllers like this:
#RestController
public class FeatureXRestController1 {
...
}
#RestController
public class FeatureXRestController2 {
...
}
Ideally I would like to define them in the Java Configuration as well, so that these two controllers won't even load when I disable the feature:
#ConditionalOnProperty(name = "myapp.featureX.enabled", havingValue = "true", matchIfMissing=true)
public class MyAppFeatureXConfiguration {
#Bean
#RestController // this doesn't work because the #RestController has target Type and can't be applied
// to methods
public FeatureXRestController1 featureXRestController1() {
}
So the question is basically is it possible to do that?
RestController is a Controller which is in turn a component hence its subject to component scanning. Hence if the feature X is disabled the rest controllers for feature X will still start loading and fail
because there won't be no "services" - beans excluded in the configuration, so spring boot won't be able to inject.
One way I thought about is to define a special annotation like #FeatureXRestController and make it #RestController and put #ConditionalOnProperty there but its still two places and its the best solution I could come up with:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#RestController
#ConditionalOnProperty(name = "myapp.featureX.enabled", havingValue = "true", matchIfMissing=true)
public #interface FeatureXRestController {
}
...
#FeatureXRestController
public class FeatureXRestController1 {...}
#FeatureXRestController
public class FeatureXRestController2 {...}
I've Found a relatively elegant workaround that might be helpful for the community:
I don't use a specialized meta annotation like I suggested in the question and annotate the controller of Feature X with the regular #RestController annotation:
#RestController
public class FeatureXController {
...
}
The Spring boot application class can be "instructed" to not load RestControllers during the component scanning exclusion filter. For the sake of example in the answer I'll use the built-in annotation filter, but in general custom filters can be created for more sophisticated (real) cases:
// Note the annotation - component scanning process won't recognize classes annotated with RestController, so from now on all the rest controllers in the application must be defined in `#Configuration` classes.
#ComponentScan(excludeFilters = #Filter(RestController.class))
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Now, since I want the rest controller to be loaded only if Feature X is enabled, I create the corresponding method in the FeatureXConfiguration:
#Configuration
#ConditionalOnProperty(value = "mayapp.featureX.enabled", havingValue = "true", matchIfMissing = false)
public class FeatureXConfiguration {
#Bean
public FeatureXService featureXService () {
return new FeatureXService();
}
#Bean
public FeatureXRestController featureXRestController () {
return new FeatureXRestController(featureXService());
}
}
Although component scanning process doesn't load the rest controllers, the explicit bean definition "overrides" this behavior and the rest controller's bean definition is created during the startup. Then Spring MVC engine analyzes it and due to the presence of the #RestController annotation it exposes the corresponding end-point as it usually does.
You can use local classes in the java config. E.e.
#Configuration
public class MyAppFeatureXConfiguration {
#Bean
public FeatureXRestController1 featureXRestController1(AutowireCapableBeanFactory beanFactory) {
#RestController
class FeatureXRestController1Bean extends FeatureRestController1 {
}
FeatureXRestController1Bean featureBean = new FeatureXRestController1Bean();
// You don't need this line if you use constructor injection
autowireCapableBeanFactory.autowireBean(featureBean);
return featureBean;
}
}
Then you can omit the #RestController annotation on the "real" implementation, but use the other annotations like #RequestMapping as usual.
#RequestMapping(...)
public class FeatureXRestController1 {
#RequestMapping(value="/somePath/{someId}", method=RequestMethod.GET)
public String findSomething(#PathVariable String someId) {
...
}
}
Sine the FeatureXRestController1 doesn't have a #RestController annotation, it is not a #Component anymore and thus will not be picked up through component scan.
The MyAppFeatureXConfiguration returns a bean that is a #RestController. This FeatureXRestController1Bean extends the FeatureXRestController1 and thus has all the methods and request mappings of the superclass.
Since the FeatureXRestController1Bean is a local class it is not included in a component scan. This does the trick for me ;)
I like both the solutions presented above. However, I came up with another one which worked for me and is pretty clean.
So, I decided to create my beans only using #Configuration classes, and I gave up the #RestController annotations entirely. I put the #Configuration class for web controllers in a separate package, so that I can pass the class descriptor to the #ComponentScan or #ContextConfiguration annotations whenever I want to enable the creation of controller beans. The most important part is to add the #ResponseBody annotation to all controller classes, above the class name, to preserve the REST controller properties. Very clean.
The drawback is that the controller classes are not recognized by the #WebMvcTest annotation, and I need to create the beans for all my controllers every time I do a MockMvc test for one controller. As I have just four controllers though, I can live with it for now.

How does Spring configuration class get instance of objects expected by its methods?

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.

AOP can't pointcut class without #Service #Controller

AOP can pointcut like #Controller or #Service.
But it doesn't work well on Class without spring's annotation.
package com.erp.module;
#Slf4j
public class SalesOrderModule {
public void cancel(){
log.info("test");
}
public static SalesOrderModule init(int type) {
SalesOrderModule salesOrderModule = new SalesOrderModule(salesOrder);
*****
return salesOrderModule;
My Aspect.java like below
#Aspect
#Component
#Slf4j
public class WebLogAspect {
#Pointcut("execution(public * com.erp.controller.*.*(..)) || execution(public * com.erp.module.*.*(..))")
public void logPointCut() {
}
I invoke cancel like below:
SalesOrderModule so = SalesOrderModule.init(3);
so.cancel();
I know this problem is relative to proxy. But how can I
Spring Docs says:
Thus, for example, the Spring Framework’s AOP functionality is normally used in conjunction with the Spring IoC container. Aspects are configured using normal bean definition syntax (although this allows powerful "autoproxying" capabilities): this is a crucial difference from other AOP implementations. There are some things you cannot do easily or efficiently with Spring AOP, such as advise very fine-grained objects (such as domain objects typically): AspectJ is the best choice in such cases. However, our experience is that Spring AOP provides an excellent solution to most problems in enterprise Java applications that are amenable to AOP.
So if you need to intercept non-spring managed code, you need to use AspectJ instead of Spring AOP. TBH, I didn't need that so far.

Is it possible to set interceptor with annotation in Spring?

I am trying to stay declarative, convention-base and XML-less.
So, I have no web.xml, and I have no context configuration XMLs. Unfortunately, Google is spammed with old-fashion Spring examples and it is impossible to find modern answer.
My question is: is it possible to declare interceptor with annotations in Spring? Apparently it would be possible to do the same way as it done with controllers (controller class is annotated with #Controller and it's methods -- with #RequestMapping).
The best way I found is here https://stackoverflow.com/a/16706896/258483
Unfortunately, it is not declarative.
Using Java configuration and the #Configuration annotation it looks like you create an interceptor and register it as described here. It's not as simple as annotating a class as an interceptor but may still adhere to your stipulations.
EDIT:
In java configuration class, we need to extend
WebMvcConfigurerAdapter. To add our interceptor, we override
WebMvcConfigurerAdapter. addInterceptors() method. Find the code
snippet.
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor());
registry.addInterceptor(new TransactionInterceptor()).addPathPatterns("/person/save/*");
}

Difference between spring #Controller and #RestController annotation

Difference between spring #Controller and #RestController annotation.
Can #Controller annotation be used for both Web MVC and REST applications?
If yes, how can we differentiate if it is Web MVC or REST application.
#Controller is used to mark classes as Spring MVC Controller.
#RestController is a convenience annotation that does nothing more than adding the #Controller and #ResponseBody annotations (see: Javadoc)
So the following two controller definitions should do the same
#Controller
#ResponseBody
public class MyController { }
#RestController
public class MyRestController { }
In the code below I'll show you the difference
between #controller
#Controller
public class RestClassName{
#RequestMapping(value={"/uri"})
#ResponseBody
public ObjectResponse functionRestName(){
//...
return instance;
}
}
and #RestController
#RestController
public class RestClassName{
#RequestMapping(value={"/uri"})
public ObjectResponse functionRestName(){
//...
return instance;
}
}
the #ResponseBody is activated by default. You don't need to add it above the function signature.
#RestController is the combination of #Controller and #ResponseBody.
Flow of request in a #Controller class without using a #ResponseBody annotation:
#RestController returns an object as response instead of view.
If you use #RestController you cannot return a view (By using Viewresolver in Spring/springboot) and yes #ResponseBody is not needed in this case.
If you use #Controller you can return a view in Spring web MVC.
#RestController annotated classes are the same as #Controller but the #ResponseBody on the handler methods are implied.
Actually, be careful - they are not exactly the same.
If you define any interceptors within your application, they will not apply to Controllers annotated as #RestController, however they do work with #Controller annotated controllers.
ie. configuration for the interceptor:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TemplateMappingInterceptor()).addPathPatterns("/**", "/admin-functions**").excludePathPatterns("/login**");
}
}
and in the declaration of a Spring controller:
#Controller
public class AdminServiceController {...
Will work, however
#RestController
public class AdminServiceController {...
does not end up having the interceptor being associated with it.
#Controller returns View. #RestController returns ResponseBody.
As you can see in Spring documentation (Spring RestController Documentation) Rest Controller annotation is the same as Controller annotation, but assuming that #ResponseBody is active by default, so all the Java objects are serialized to JSON representation in the response body.
THE new #RestController annotation in Spring4+, which marks the class as a controller where every method returns a domain object instead of a view. It’s shorthand for #Controller and #ResponseBody rolled together.
#RestController was provided since Spring 4.0.1. These controllers indicate that here #RequestMapping methods assume #ResponseBody semantics by default.
In earlier versions the similar functionality could be achieved by using below:
#RequestMapping coupled with #ResponseBody like #RequestMapping(value = "/abc", method = RequestMethod.GET, produces ="application/xml")
public #ResponseBody MyBean fetch(){
return new MyBean("hi")
}
<mvc:annotation-driven/> may be used as one of the ways for using JSON with Jackson or xml.
MyBean can be defined like
#XmlRootElement(name = "MyBean")
#XmlType(propOrder = {"field2", "field1"})
public class MyBean{
field1
field2 ..
//getter, setter
}
#ResponseBody is treated as the view here among MVC and it is dispatched directly instead being dispatched from Dispatcher Servlet and the respective converters convert the response in the related format like text/html, application/xml, application/json .
However, the Restcontroller is already coupled with ResponseBody and the respective converters. Secondly, here, since instead of converting the responsebody, it is automatically converted to http response.
#Controller is used in legacy systems which use JSPs. it can return views.
#RestController is to mark the controller is providing REST services with JSON response type. so it wraps #Controller and #ResponseBody annotations together.
#Controller: This annotation is just a specialized version of #Component and it allows the controller classes to be auto-detected based on classpath scanning.
#RestController: This annotation is a specialized version of #Controller which adds #Controller and #ResponseBody annotation automatically so we do not have to add #ResponseBody to our mapping methods.
The #Controller annotation indicates that the class is a "Controller" like a web controller while #RestController annotation indicates that the class is a controller where #RequestMapping methods assume #ResponseBody semantics by default i.e. servicing REST API
#RestController is composition of #Controller and #ResponseBody, if we are not using the #ResponseBody in Method signature then we need to use the #Restcontroller.
Instead of using #Controller and #ResponseBody, #RestController let's you expose Rest APIs in Spring 4.0 and above.

Categories