Controllers are the Presentation layer components that are responsible for responding to the user's actions. These actions could be entering a particular URL in the browser, clicking on a link, submitting a form on a web page, or something similar. Any regular Java classes can be transformed into a Controller by simply annotating them with the @Controller
(org.springframework.stereotype.Controller
) annotation.
And as you have already learned, the @Controller
annotation supports Spring's component scanning mechanism in auto-detecting/registering the bean definition in the web application's context. To enable this auto-registering capability, we must add the @ComponentScan
(org.springframework.context.annotation.ComponentScan
) annotation in the web application context configuration file. You saw how to do this in
Chapter 2, Spring MVC Architecture – Architecting Your Web Store under the section Understanding the web application context configuration.
A Controller class is made up of request-mapped methods, also in short called handler methods, and handler methods are annotated with the @RequestMapping
(org.springframework.web.bind.annotation.RequestMapping
) annotation. The @RequestMapping
annotation is used to map the request path of a URL to a particular handler method. In
Chapter 2, Spring MVC Architecture – Architecting Your Web Store you got a short introduction to the @RequestMapping
annotation and learned how to apply the @RequestMapping
annotation on the handler method level, but in Spring MVC you can even specify the @RequestMapping
annotation on the Controller class level. In that case, Spring MVC will consider the Controller class level @RequestMapping
annotation's value before mapping the remaining URL request path to the handler methods. This feature is called relative request mapping.
Let's add an @RequestMapping
annotation on the class level of our ProductController
to demonstrate the relative request mapping feature on the handler methods. But before that, we just want to ensure that you have already replaced the ProductRepository
reference with the ProductService
reference in the ProductController
class as part of the previous chapter's Have a go hero - accessing the product domain object via a service section. Because contacting the Persistence layer directly from Presentation layer is not best practice, all access to the Persistence layer should go through the Service layer. Those who completed the exercise can directly start from step 5; others should continue from step 1.
ProductService
interface from the com.packt.webstore.service
package in the src/main/java
source folder, and add the following method declarations to it:List<Product> getAllProducts();
ProductServiceImpl
implementation class from the com.packt.webstore.service.impl
package in the src/main/java
source folder, and add the following method implementation to it:@Override public List<Product> getAllProducts() { return productRepository.getAllProducts(); }
ProductController
and remove the existing ProductRepository
reference. Basically delete the following two lines from ProductController
:@Autowired private ProductRepository productRepository;
list
method as follows in the ProductController
class. Note this time we used the productService
reference to get all the products:@RequestMapping("/products") public String list(Model model) { model.addAttribute("products", productService.getAllProducts()); return "products"; }
ProductController
class, add the following annotation on top of the class:@RequestMapping("market")
http://localhost:8080/webstore/products/
). You will see the HTTP Status 404 error page in the browser.http://localhost:8080/webstore/market/products/
.What we demonstrated here is a simple concept called relative request mapping. In step 5, we just added an extra @RequestMapping
annotation at the class level with a value attribute defined as market
. Basically, your class signature will look as follows after your change:
@Controller @RequestMapping("market") public class ProductController {
In all our previous examples, we annotated @RequestMapping
annotations only on the Controller's method level, but Spring MVC also allows us to specify request mapping on the Controller's class level. In that case, Spring MVC maps a specific URL request path on the method level that is relative to the class level of the @RequestMapping
URL value. So if we have defined any class level request mapping, Spring MVC will consider that class level request path before mapping the remaining request path to the handler methods.
That's why when we accessed our regular products page in step 6, we got the HTTP Status 404 error. Because we added an extra request mapping on the Controller class level, our list method is now being mapped to the URL http://localhost:8080/webstore/market/products
.
Now have we changed our Controller's request mapping in ProductController
, we also need to change the return value of the updateStock
method as follows to avoid side effects because of our change:
return "redirect:/market/products";
We will consider more about the redirect:
prefix in the next chapter. For now, just remember this change is needed to avoid side-effects since we changed the Controller request mapping.