Content:
- Introduction
- Creating a Project
- Spring MVC connection
- Creating Pages and Controller
- Configuration
- Model
- Model-View-Controller
Introduction
I started getting acquainted with technologies and frameworks that were new to me by studying various examples in which they were used, because usually I understand something best when I see it in action using an example of a full-fledged application. Typically, such examples are CRUD applications ( C reate, Read , U pdate, D elete), the Internet is full of such examples of varying degrees of complexity. The problem is that they usually don’t explain in detail how, what and why was done there, why such and such a dependency was added, why such and such a class is needed, etc. In most cases, they take a completely finished application, with a final POM file, with final versions of classes, and simply run through each one, without focusing on the little things that probably seem obvious to an experienced person. I’ve looked at a lot of such examples and it’s usually clear how everything works, but how they came to this is not entirely clear. Therefore, I decided that such an example would be useful, not from the position of an experienced developer, but from the position of a beginner who has never dealt with Spring, Hibernate and other things.Creating a Project
So, since I’m a newbie, we won’t use any obscure archetypes. Spring initializr still sounds too scary. Therefore, we will create the most ordinary simple Maven project. I don’t have a domain name, so in groupid I’ll just writetestgroup
, and in artifactid I’ll write the name, for example, filmography
(this will be a list of films). We create a project and choose Enable auto-import
when the idea suggests it. Thanks to this, every time we make any changes to the POM file (Project Object Model, this file describes the entire structure of the Maven project), everything will immediately be automatically applied to the project. The libraries will be taken from our local repository if we already have them, or if we use some new dependencies that we have not dealt with before, Maven will simply download them via the Internet from the central repository. Maven also has a function to download sources and documentation (Download Sources and/or Documentation). It’s also very convenient, if something is not clear with some class or method, you can go to the source code and see how it all works inside. Let's add a couple of details. This will be a web application and we will use Tomcat . To deploy an application to Tomcat, you need to transfer it there in the form of a war archive (Web Application Resource, a special format for web applications). To do this, add the following line to the POM file so that the application is compiled into a war archive:
<packaging>war</packaging>
Well, you will also need a special directory for web sources, in our case there will be jsp pages and some web resources. Let's create a main
directory webapp
. It should be called exactly that and be located in exactly main
the same way as java
, resources
because this is the standard Maven directory structure. Once we have installed the package in war
and thus determined that this is a web project, the directory webapp
will be automatically marked as Web application sources (there will be a blue dot on it) and everything related to the web will be searched in this folder. And one moment. By default, Maven uses language version 1.5, but I want to use, for example, version 1.8 - Java 8 (You can take 10, or 11, but still there are no plans to use any features from there, so let it be 8). This can be solved very simply, we write in Google something like “Maven java 8” and see what needs to be added to the POM file so that Maven compiles our classes for the required version. As a result, we have the following:
Spring MVC connection
You have to start somewhere. According to the plan, we will connect the database and use Hibernate, but this all sounds a little too scary for now. We need to do something simpler first. Spring MVC, this is already better, we have been familiar with the MVC pattern for a long time, it was used in half of the large tasks of the course. From here we will start dancing. To create a web application with Spring MVC, we also need a Servlet-API, i.e. that thing with the help of which the request-response interaction will take place. Let's try to connect this. We go to Google, look for the necessary dependencies in the Maven repository and add them to thepom.xml
. In the External Libraries section you can see that not only spring-webmvc was loaded , but also a bunch of other things. Those. we don't need to additionally include dependencies for spring core , context , beans , etc. that we need, everything we needed was pulled up along with spring-webmvc .
We need to make a small disclaimer. It is usually recommended to still add a dependency separately for each library used, even if they are already bundled with those already added, because this can help avoid some problems and glitches. A simple example. Let's say we added a dependency that uses some API, and at the same time it will pull up some kind of implementation for this API. And then we added another dependency that uses the same API and also pulls up some of its implementation for this, but this time it’s different. Thus, we will have 2 different implementations of the same API. And if we ourselves want to use some methods of this API somewhere, then a problem will arise, because the system will not know which implementation to use, it will choose randomly, perhaps not the one we expected. And if you explicitly specify a dependency for one of the implementations, then priority will be given to it. However, this is not such a strict recommendation; it mainly applies to large projects where many different libraries from different companies are used. We won’t do that here, so as not to load the POM file too much; no problems are expected. But nevertheless, it is still worth keeping this in mind. |
provided
depending mean javax.servlet-api
? Scope is the scope of the dependency, provided
which means that the dependency will be available at the stage of compiling and testing the application, but it will not be archived. The fact is that to deploy the application we will use a servlet container, Tomcat, and it already has such libraries inside, so there is no need to transfer them there and burden the archive with unnecessary load. Looking ahead, for the same reason we will do without the usual method main
, because it already exists inside Tomcat.
Creating Pages and Controller
Let's try to cook up something simple now. First, let's create anwebapp
additional directory, for example pages
, in which our views will be stored, i.e. jsp pages, and create a couple of pages. We will need a page on which in the future a list of films will be displayed, for example films.jsp
, and perhaps we can make a separate page for editing, let it be editPage.jsp
. We won’t fill them in with anything serious for now; just for testing, we’ll make a link on one page to another. Now we need a class that will process requests, i.e. controller. Let's add a new package controller
and create a class in it FilmController
(in general, it is not necessary to package everything in different packages, this application will be very small and simple, but in a normal project there can be many controllers, configuration classes, models, etc., so even starting with small projects, it’s better to immediately get used to doing everything in an orderly and structured manner so that there is no mess). In this class we will create methods that will return our views in response to requests.
package testgroup.filmography.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class FilmController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView allFilms() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("films");
return modelAndView;
}
@RequestMapping(value = "/edit", method = RequestMethod.GET)
public ModelAndView editPage() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("editPage");
return modelAndView;
}
}
What's the point? Spring MVC has a thing called DispatcherServlet
. This is like the main controller, all incoming requests pass through it and it then passes them on to a specific controller. The annotation @Controller
just tells Spring MVC that this class is a controller (well, logical in general), the dispatcher will check the annotations @RequestMapping
to call the appropriate method. The annotation @RequestMapping
allows you to set addresses for controller methods, by which they will be available in the client (browser). It can also be applied to the controller class to set, so to speak, the root address for all methods. allFilms()
The parameter for the method value
is set to " /
", so it will be called immediately when the combination http://host:port/ is entered in the browser (i.e., by default it is http://localhost:8080/ or http://127.0 .0.1:8080/ ). The parameter method
specifies what type of request is supported (GET, POST, PUT, etc.). Since here we only receive data, GET is used. Later, when methods for adding and editing appear, there will already be POST requests. (By the way, instead of an annotation @RequestMapping
indicating a method, you can use annotations @GetMapping
, @PostMapping
etc. @GetMapping
equivalently @RequestMapping(method = RequestMethod.GET
)). In our methods, we create an object ModelAndView
and set the name of the view that needs to be returned.
Configuration
Let's move on to setting up the configuration.config
Let's create a class in the package WebConfig
. It will have only one method that returns an object of type ViewResolver
, this is the interface necessary to find a representation by name.
package testgroup.filmography.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "testgroup.filmography")
public class WebConfig {
@Bean
ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
@Configuration
tells Spring that this class is a configuration class and contains the definitions and dependencies bean
of the components. Beans are objects that are managed by Spring. The annotation is used to define a bean @Bean
. @EnableWebMvc
allows you to import Spring MVC configuration from the WebMvcConfigurationSupport
. You can also implement, for example, an interface WebMvcConfigurer
that has a whole bunch of methods, and customize everything to your liking, but we don’t need to go into that yet, the standard settings will suffice. @ComponentScan
tells Spring where to look for the components it should manage, i.e. classes marked with an annotation @Component
or its derivatives such as @Controller
, @Repository
, @Service
. These annotations automatically define the class bean. In the method, viewResolver()
we create its implementation and determine where exactly to look for representations in webapp
. Therefore, when in the controller method we set the name " films
" the view will be found as " /pages/films.jsp
" So, we have a configuration class, but for now it is just some kind of separate class, it does not affect our application in any way. We need to register this configuration in the Spring context. For this you need a class AbstractAnnotationConfigDispatcherServletInitializer
. In the package, config
we create its successor, say AppInitializer , and implement its methods.
package testgroup.filmography.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
The last method registers addresses and there are 2 more methods for registering configuration classes. Web configurations, where ViewResolver
's and the like are defined, are placed in getServletConfigClasses()
. It’s better to read about all this in the documentation and various guides, but in our case it’s not necessary to delve into this yet, ours, WebConfig
in principle, can be RootClasses
defined in both, you can even define both at once, it will still work. One more thing. There may be problems with encoding when, when sending values with Russian characters from the form, the result will be scribbles. To solve this problem, we will add a filter that will pre-process requests. We go to the AppInitializer class and override the method getServletFilters
, in which we indicate the desired encoding, it, of course, should be the same as everywhere else, as on the pages and in the database:
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return new Filter[] {characterEncodingFilter};
}
Well, everything seems to be set up, you can try to run it and see what happens. Run -> Run -> Edit Configurations -> Add New Configuration -> Tomcat Server -> Local Next, you need to select an artifact to deploy. The idea itself will give a hint Warning: No artifacts marked for deployment . Click the fix button and select ...: war exploded . Or you can go to Deployment -> add -> Artifact -> ...: war exploded . And you also need to go to Deployment and set the Applecation context field (this will be part of the url address where the application will be available in the browser) to " /
". Then our application will be immediately available at http://localhost:8080/ (but you can also specify something there, for example " /filmography
", and then you will just need to add this to all addresses, i.e. for example there will be no " http://localhost:8080/edit" , but it will be "http://localhost:8080/filmography/edit" ). Click Run and wait until it starts. Here's what I got: Everything seems to be fine, but there is one caveat. The fact is that our pages are now publicly accessible and can be accessed directly by writing the path in the address bar. We enter http://localhost:8080/pages/films.jsp and now we have received our page without the controller’s knowledge. Somehow this is not very correct, so we will create a webapp
special directory WEB-INF
. What's inside will be hidden from the public and can only be accessed through a controller. We place the directory with our views ( pages
) in WEB-INF
, and ViewResolver
accordingly add it to the prefix:
viewResolver.setPrefix("/WEB-INF/pages/");
Now we get our page at http://localhost:8080 , but if we try directly to http://localhost:8080/WEB-INF/pages/films.jsp we get a 404 error. Well, great, we have the simplest web -application, Hello World as they say. The project structure currently looks like this:
Model
We already have views and a controller, but in MVC there is also a 3rd letter, so to complete the picture we will also add a model. In the package,model
let's create a class Film
, for example, with the following fields: int id
, String title
(title), int year
(year of release), String genre
(genre) and boolean watched
(i.e. have you already watched this film or not).
package testgroup.filmography.model;
public class Film {
private int id;
private String title;
private int year;
private String genre;
private boolean watched;
// + Getters and setters
}
Nothing special, just an ordinary class, private fields, getters and setters. Objects of such classes are also called POJO
(Plain Old Java Object), well, i.e. "simple java object". Let's now try to create such an object and display it on the page. For now, we won’t worry too much about how to create it and initialize it. To try it out, let’s just stupidly create it directly in the controller, for example, like this:
public class FilmController {
private static Film film;
static {
film = new Film();
film.setTitle("Inception");
film.setYear(2010);
film.setGenre("sci-fi");
film.setWatched(true);
}
And add this object to ours ModelAndView
using the method addObject
:
@RequestMapping(method = RequestMethod.GET)
public ModelAndView allFilms() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("films");
modelAndView.addObject("film", film);
return modelAndView;
}
Now we can display this object on our page. In films.jsp
instead of Hello World we will write ${film}
and the object corresponding to the attribute name " film
" will be substituted here. Let's try to run it and see what happened (for clear output of the object, the class Film
was redefined toString()
):
Model-View-Controller
At this stage, we already seem to have a full-fledged Spring MVC application. Before moving on, it would be good to take a look at everything again and figure out how it all works. On the Internet you can find many pictures and diagrams about this, I like this one:Dispatcher Servlet
, then it finds a suitable controller to process this request using HandlerMapping
(this is an interface for selecting a controller, checks which of the available controllers has a method that accepts such an address), calls a suitable method and Controller
returns information about the view, then the dispatcher finds the desired view by name using ViewResolver
'a, after which the model data is transferred to this view and we get our page as output. Something like this. To be continued... Introducing Maven, Spring, MySQL, Hibernate and the first CRUD application (part 1) Introducing Maven, Spring, MySQL, Hibernate and the first CRUD application (part 2) Introducing Maven, Spring, MySQL, Hibernate and the first CRUD application (part 3) Introduction to Maven, Spring, MySQL, Hibernate and the first CRUD application (part 4)
GO TO FULL VERSION