Slight Difference between #RestController & #Component - java

Although every page on internet says that #RestController is a specification of #Component.I dont know whether it has to be related with DispatcherServlet. But when I try below code by switching between #RestController & #Component, I don't see same behaviour :
First I tried with #RestController:
#RestComponent
public class TestController {
#RequestMapping(value="/testController", method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)
public void testController() {
System.out.println("Hello");
}
}
I got below output in Console:
Hello
Second I tried with #Component + #ResponseBody:
#Component
#ResponseBody
public class TestController {
#RequestMapping(value="/testController", method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)
public void testController() {
System.out.println("Hello");
}
}
I got an error on postman:
{
"timestamp": 1570998345860,
"status": 405,
"error": "Method Not Allowed",
"message": "Request method 'POST' not supported",
"path": "/testController"
}
If both annotations are same, then why is there a difference in output ??
Below is the Source code for #RestController & #Controller , which shows that both #RestController & #Controller are specification of #Component:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Controller
#ResponseBody
public #interface RestController {
}
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Controller {
}
Maybe it has to be related with DispatcherServlet. It might be possible that Dispatcher Servlet only check for URL in #RestController annotated classes.

#Controller is user in traditional controller and #RestController annotation was introduced to simplify the creation of RESTful web services. It's a convenience annotation that combines #Controller and #ResponseBody
#Controller annotation is simply a specialized of the #Component class and allows implementation classes to be autodetect through the classpath scanning.
#Controller is typically used in combination with a #RequestMapping annotation used on request handling methods.
The request handling method is annotated with #ResponseBody. This annotation enables automatic serialization of the return object into the HttpResponse.
#RestController is a specialized version of the controller. It includes the #Controller and #ResponseBody annotations and as a result, simplifies the controller implementation.
The controller is annotated with the #RestController annotation, therefore the #ResponseBody isn't required.
Every request handling method of the controller class automatically serializes return objects into HttpResponse.
see here in detail : https://www.baeldung.com/spring-controller-vs-restcontroller

Just because #RestController is a #Component does not mean you can achieve the same functionality by switching to the broader one #Component. Even with the addition of #ResponseBody, you don't achieve equivalent functionality (as expressed through the Request Method POST not being supported).
Replace #Component with #Controller, because a #RestController has the exact same functionality as a #Controller + #ResponseBody. You can also see this in the meta-annotations of #RestController, you see it is meta-annotated with #Controller instead of just #Component. In turn, #Controller is meta-annotated with #Component.

The #Controller , #RestController , #Service etc are all annotations that are meta-annotated with the #Component annotation. All these annotations are essentially specializations of the #Component annotation for specific use cases.
The #Component annotation is generally used to register a Spring bean.
The #Controller annotation even though it is annotated with #Component internally , it provides a different functionality altogether.By annotating a class with this annotation we are essentially telling spring to scan this class for #RequestMapping annotation to register beans for request mapping. This does not happen when you just use the #Component annotation.
When the spring application starts up, the DispatcherServlet will enable the RequestMappingHandlerMapping RequestMappingHandlerAdapter (which is a handler mapping which looks for #RequestMapping annotations on #Controllers). So when a request reaches the dispatcher servlet it gives it to the RequestMappingHandlerMapping which resolves the uri to a controller method bean.For more information read : DispatcherServlet, Updated Doc Spring 5.x HandlerMapping
Note :
In older versions of spring DefaultAnnotationHandlerMapping is enabled for this by spring.
So in summary :
#Component is a generic stereotype for any Spring-managed component
or bean.
#Repository is a stereotype for the persistence layer.
#Service is a stereotype for the service layer.
#Controller is a stereotype for the presentation layer (spring-MVC).
Spring [Doc][4].
The #Controller annotation indicates that a particular class serves
the role of a controller.
The basic purpose of the #Controller annotation is to act as a
stereotype for the annotated class, indicating its role. The
dispatcher will scan such annotated classes for mapped methods,
detecting #RequestMapping annotations (see the next section).
Annotated controller beans may be defined explicitly, using a standard
Spring bean definition in the dispatcher's context. However, the
#Controller stereotype also allows for autodetection, aligned with
Spring 2.5's general support for detecting component classes in the
classpath and auto-registering bean definitions for them.
Similar post : What's the difference between #Component, #Repository & #Service annotations in Spring?

You should not get the same behaviour because #Component is more generic. It only registers your class as a Spring Managed Bean. However, #RestController not only registers your class as a managed bean, it further registers it as an entry point for HTTP calls to the specified URL path. Same goes for other Layer specific annotations like #Repository, #Configuration e.t.c

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.

SPRING BOOT annotation: are they required

Are #Component, #Service and #Repository optional in Spring Boot 2?
Example If have a controller class called FirstController annotated with #Controller, #RestController and #RequestMapping. I also have service classes called FirstService and SecondService and a repository called FirstRespository.
I didn't annotate any of the class except FirstController but still my application works.
Does this mean that those stereotype annotations are not required for your app to make it work? You just need it for convention and if you need to modify behaviour like scope etc.
Thanks for answering in advance.
They are not required in order for your application to work BUT they will not be picked up by Spring on your application launch nor you will have benefits of that annotation specification
#Component - generic stereotype for any Spring-managed component
#Repository - stereotype for the persistence layer
#Service - stereotype for service layer
Any code can pass when you write your Spring application, but annotation helps Spring to understand what should be created as a bean or a component and for which use.
Consider this:
#Service
public class MyService {
private IComponent component;
#Autowired
public MyService(IComponent component) {
this.preparingService = preparingService;
}
}
In order to autowire a class implementing IComponent, you need to have that class decorated with #Service or #Component in order for Spring to inject it into MyService when MyService is itself of course injected in your controller.

Java #Component class and #Configuration class with AnnotationConfigApplicationContext

I know springs AnnotationConfigApplicationContext is capable of accepting not only #Configuration classes as input but also plain #Component classes and classes annotated with JSR-330 metadata.
I have created AppConfig.java below without #Configuration annotation.
public class AppConfig {
#Bean(name="sampleService")
public SampleService getSampleService(){
return new SampleService();
}
}
Passed this class as my java config class to AnnotationConfigApplicationContext, it accepted and registered my service beans.
I did some modification on above same AppConfig like below.
#Component
public class AppConfig {
#Bean(name="sampleService")
public SampleService getSampleService(){
return new SampleService();
}
}
passed AppConfig to AnnotationConfigApplicationContext, it accepted and registered my service beans.
Question:
AnnotationConfigApplicationContext class is accepting the java config class with #Configuration, without #Configuration and with #Component annotations, what is the difference between #Component and #Configuration?
Why is it Accepting even without #Configuration annotation?
When to use #Configuration, and when to use #Component as java config class?
#Component
Indicates that an annotated class is a "component".
That is, in a context where component scanning is enabled, Spring generates bean definitions for #Component annotated types. These bean definitions end up being turned into beans.
#Configuration, which is itself annotated with
Indicates that a class declares one or more #Bean methods and may be
processed by the Spring container to generate bean definitions and
service requests for those beans at runtime, [...]
So any #Configuration type, for which Spring generates a bean, acts as a factory for beans.
The javadoc of #Bean states
#Bean methods may also be declared within classes that are not
annotated with #Configuration. For example, bean methods may be
declared in a #Component class or even in a plain old class. In such
cases, a #Bean method will get processed in a so-called 'lite' mode.
Bean methods in lite mode will be treated as plain factory methods by
the container (similar to factory-method declarations in XML), with
scoping and lifecycle callbacks properly applied. The containing class
remains unmodified in this case, and there are no unusual constraints
for the containing class or the factory methods.
In contrast to the semantics for bean methods in #Configuration
classes, 'inter-bean references' are not supported in lite mode.
Instead, when one #Bean-method invokes another #Bean-method in lite
mode, the invocation is a standard Java method invocation; Spring does
not intercept the invocation via a CGLIB proxy. This is analogous to
inter-#Transactional method calls where in proxy mode, Spring does not
intercept the invocation — Spring does so only in AspectJ mode.
So #Bean methods have full functionality in #Configuration annotated classes and limited functionality in #Component annotated classes.
why it is Accepting even without #Configuration annotation?
That's how the class is designed. An ApplicationContext is a BeanFactory. AnnotationConfigApplicationContext simply offers an extra way to register a bean definition.
When to use #Configuration, and when to use #Component as java config class?
These really completely separate goals. Follow the javadoc. When you need to setup an ApplicationContext, you can use an AnnotationConfigApplicationContext with a #Configuration annotated class. If you simply need a bean, annotate its type with #Component.
#Component annotation marks the Java class as a bean, but #Configuration annotation marks the Java class containing beans (methods that have #Bean annotation).
The #Bean annotation must use with #Configuration exactly to create Spring
beans.
In following class
#Component
public class AppConfig {
#Bean(name="sampleService")
public SampleService getSampleService(){
return new SampleService();
}
}
#Bean annotation is not any effect, and getSampleService() method will be plain old java method and will not be singleton, because as i mentioned, #Bean annotation must use with #Configuration, so it must be repaired as following:
#Configuration
public class AppConfig {
#Bean(name="sampleService")
public SampleService getSampleService(){
return new SampleService();
}
}
so replacing #Configuration annotation with any other annotation, or removing it, just make #Bean annotation ineffective and getSampleService() method will not be singleton anymore.

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.

Why #Component is used in Controller annotation

Below is source code of For Component Interface
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Component {
and for Spring Controller annotation is as below
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Controller {
Why #Component is added in Controller annotation ? What is its purpose ? What if I remove this line ?
I am clear about below annotation types which are used to create custom annotations.
#Documented Whether to put the annotation in Javadocs
#Retention When the annotation is needed
#Target Places the annotation can go
#Inherited Whether subclasses get the annotation
#Controller is a #Component (just like #Service, #Repository, #Endpoint etc.).
The #Component is used as a meta-annotation here so that it can be picked-up using component-scanning. Next to that the #Controller is a special component which will have some added functionality (Spring MVC takes care of that). If you remove the #Component annotation component-scan will not be able to detect it anymore.
You can also create your own #Component based annotations by simply creating your own annotation and putting #Component on it.
#Component is a generic stereotype for any Spring-managed component. #Repository, #Service, and #Controller are specializations of #Component for more specific use cases, for example, in the persistence, service, and presentation layers, respectively.
Read this answer

Categories