Detailed Explanation of Spring MVC Framework: Creating High-Performance Web Applications

First, introduce the Spring MVC framework

1 Spring MVC framework concept

Spring MVC is a web framework based on Java's lightweight, Model-View-Controller (MVC) architecture, which is highly flexible and loosely coupled. It focuses on providing a good development experience and fast development efficiency.

2 Advantages of the Spring MVC framework

The Spring MVC framework has the following advantages:

  1. Ease of use: The Spring MVC framework adopts an annotation-based configuration method, which simplifies the workload of developers.
  2. Strong flexibility: The Spring MVC framework provides a large number of extension points to meet the needs of different scenarios.
  3. Rich template engines: The Spring MVC framework supports a variety of template engines, such as JSP, FreeMarker, Thymeleaf, etc.
  4. Good scalability: All components of the Spring MVC framework can be replaced or extended without affecting the operation of the entire application.

3 Comparison between Spring MVC framework and other MVC frameworks

Compared with other MVC frameworks, the Spring MVC framework has the following advantages:

  1. Easy to learn and use: The API interface of the Spring MVC framework is simple and easy to understand, very easy to learn and use.
  2. Strong scalability: All components of the Spring MVC framework can be customized or replaced, so it has very strong scalability.
  3. Support multiple template engines: Spring MVC framework supports multiple template engines, such as JSP, FreeMarker, Thymeleaf, etc.
  4. Integrate the Spring framework: The Spring MVC framework is designed as a part of the Spring framework, so it can fully utilize the functions provided by the Spring framework.

Two, the workflow of the Spring MVC framework

1 Introduction to interceptors

The first component that processes requests in the workflow of the Spring MVC framework is the interceptor. The interceptor can pre-process and post-process the request, such as recording logs and checking user login status.

public interface HandlerInterceptor {
    
    
   boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
   void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;
   void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}

2 Internal Processor Introduction

If the request is not intercepted by the interceptor, it will be handled by the internal processor. The internal processor is usually a controller class, which is responsible for parsing the request, calling the corresponding service method, and binding the processing result to the ModelAndView object.

public interface Controller {
    
    
   ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

3 Introduction to HandlerAdapter

HandlerAdapter is an adapter whose role is to adapt different types of processors (such as Controller, @RequestMapping) to the processing flow of the Spring MVC framework. HandlerAdapter is responsible for determining whether the incoming request can be processed by the corresponding processor.

public interface HandlerAdapter {
    
    
   boolean supports(Object handler);
   ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

4 Introduction to View Resolver

After binding the processing result to the ModelAndView object in the internal processor, the Spring MVC framework will pass the ModelAndView object to the view resolver. The view resolver resolves the specific view object according to the view name in the ModelAndView object.

public interface ViewResolver {
    
    
   View resolveViewName(String viewName, Locale locale) throws Exception;
}

5 Introduction to Render View

After the view resolver resolves the view name into a specific view object, it passes it to the renderer for rendering. The renderer will bind the model data to the view and output the rendered result to the client.

public interface View {
    
    
   void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}

3. Basic configuration of Spring MVC framework

1 Spring MVC framework configuration file

When using the Spring MVC framework, you need to create an XML configuration file to define the basic configuration information of the Spring MVC framework. The following is an example of a typical Spring MVC framework configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">

    <!--启用注解配置-->
    <context:annotation-config />

    <!--启用MVC注解驱动-->
    <mvc:annotation-driven />

    <!--配置HandlerMapping-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!--配置HandlerAdapter-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--配置静态资源文件映射-->
    <mvc:resources location="/static/" mapping="/static/**"/>
</beans>

2 Technology Selection and Configuration

In the process of using the Spring MVC framework, some technologies need to be selected for configuration. For example, which view template engine needs to be selected, how to implement form validation, and how to process uploaded files.

The following are some commonly used technology selection configurations:

  1. View template engine configuration:
<!--配置Thymeleaf视图解析器-->
<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
    <property name="prefix" value="/WEB-INF/templates/" />
    <property name="suffix" value=".html" />
    <property name="templateMode" value="HTML5" />
    <property name="cacheable" value="false" />
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
    <property name="templateResolver" ref="templateResolver" />
    <property name="enableSpringELCompiler" value="true" />
</bean>
<bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
    <property name="templateEngine" ref="templateEngine" />
    <property name="characterEncoding" value="UTF-8" />
</bean>
  1. Form validation framework configuration:
<!--配置验证框架-->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="validationMessageSource" ref="messageSource" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>ValidationMessages</value>
         </list>
    </property>
</bean>
  1. File upload and download configuration:
<!--配置文件上传下载相关的组件-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="52428800" />
</bean>

3 Annotation Configuration Skills

Annotating configuration in the Spring MVC framework is a very convenient configuration method. The following are some common annotation configuration usage techniques:

  1. A controller class is defined using the @Controller annotation. The @RequestMapping annotation is used to specify the request path and request method.
@Controller
@RequestMapping("/user")
public class UserController {
    
    
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String getUserById(@PathVariable("id") int id, ModelMap model) {
    
    
        User user = userService.getUserById(id);
        model.addAttribute("user", user);
        return "user";
    }
}
  1. Use the @ResponseBody annotation to specify that the return type is in JSON format.
@RequestMapping(value = "/user", method = RequestMethod.POST)
@ResponseBody
public User addUser(@RequestBody User user) {
    
    
    int userId = userService.addUser(user);
    User returnUser = userService.getUserById(userId);
    return returnUser;
}
  1. Use the @ModelAttribute annotation to limit the property scope of the Form object.
@RequestMapping(value = "/user/edit", method = RequestMethod.POST)
public String editUser(@ModelAttribute("userForm") User user) {
    
    
    userService.editUser(user);
    return "redirect:/user";
}

4 Common problems and solutions

There are some common problems you may encounter when using the Spring MVC framework. Here are some common problems and solutions:

  1. Page garbled problem

Add the following code to the JSP file to avoid the page garbled problem:

<%@ page contentType="text/html;charset=UTF-8" %>

Add the following configuration to the configuration file:

<!--配置字符编码过滤器-->
<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  1. Static resources cannot be accessed

If you encounter the problem of not being able to access static resources when using the Spring MVC framework, you can add the following configuration to the configuration file:

<!--配置静态资源文件映射-->
<mvc:resources location="/static/" mapping="/static/**"/>

4. Advanced application of Spring MVC framework

1 RESTful web service

RESTful web service is a web service based on HTTP protocol, which uses simple URL and HTTP methods to define resources and their operations. The following is an example of a controller class for a typical RESTful web service:

@RestController
@RequestMapping("/users")
public class UserController {
    
    
    private UserService userService;
    @Autowired
    public UserController(UserService userService) {
    
    
        this.userService = userService;
    }
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public List<User> getUsers() {
    
    
        return userService.getUsers();
    }
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public User getUserById(@PathVariable("id") int id) {
    
    
        User user = userService.getUserById(id);
        if (user == null) {
    
    
            throw new UserNotFoundException("User not found with id:" + id);
        }
        return user;
    }
    @RequestMapping(value = "/", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.CREATED)
    public User addUser(@RequestBody User user) {
    
    
        return userService.addUser(user);
    }
    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void editUser(@PathVariable("id") int id, @RequestBody User user) {
    
    
        userService.editUser(id, user);
    }
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteUser(@PathVariable("id") int id) {
    
    
        userService.deleteUser(id);
    }
}

2 WebSocket communication

WebSocket is a newly added protocol in the HTML5 standard. It realizes full-duplex communication between the browser and the server, and enables the server to actively push messages to the client, thereby realizing real-time communication. The following is an example of a typical WebSocket handler:

@Component
@ServerEndpoint("/websocket/{name}")
public class WebSocketHandler {
    
    
    private static ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
    private UserService userService;
    @Autowired
    public WebSocketHandler(UserService userService) {
    
    
        this.userService = userService;
    }
    @OnOpen
    public void onOpen(Session session, @PathParam("name") String name) throws IOException {
    
    
        if(!sessions.containsKey(name)){
    
    
            session.setMaxIdleTimeout(1800000);
            sessions.put(name, session);
        }
    }
    @OnClose
    public void onClose(Session session, @PathParam("name") String name) {
    
    
        sessions.remove(name);
    }
    @OnMessage
    public void onMessage(Session session, String message, @PathParam("name") String name) throws IOException {
    
    
        session.getBasicRemote().sendText("Received:" + message);
    }
    @OnError
    public void onError(Session session, Throwable throwable) {
    
    
        throwable.printStackTrace();
    }
}

3 File upload and download

When using the Spring MVC framework, it may be necessary to handle the functional requirements of file upload and download. Here is an example of a typical file upload and download handler:

@Controller
@RequestMapping("/file")
public class FileController {
    
    
    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public String uploadFile(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws IOException {
    
    
        String fileName = file.getOriginalFilename();
        String filePath = request.getSession().getServletContext().getRealPath("/upload/") + fileName;
        file.transferTo(new File(filePath));
        return "redirect:/file";
    }
    @RequestMapping("/download")
    public ResponseEntity<byte[]> downloadFile(HttpServletRequest request) throws IOException {
    
    
        String fileName = "example.pdf";
        String filePath = request.getSession().getServletContext().getRealPath("/upload/") + fileName;
        byte[] content = FileUtils.readFileToByteArray(new File(filePath));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_PDF);
        headers.setContentDispositionFormData("attachment", fileName);
        return new ResponseEntity<byte[]>(content, headers, HttpStatus.OK);
    }
}

4 Tips for using interceptors

Interceptors are a very convenient way to implement controller pre- and post-processing logic in the Spring MVC framework. The following is an example of a typical interceptor implementation:

@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
    
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        HttpSession session = request.getSession();
        if (session.getAttribute("user") != null) {
    
    
            return true;
        } else {
    
    
            response.sendRedirect(request.getContextPath() + "/login");
            return false;
        }
    }
}

Define interceptors and interceptor mappings in configuration files:

<!--定义拦截器-->
<bean id="authorizationInterceptor" class="com.example.interceptor.AuthorizationInterceptor" />
<!--定义拦截器映射-->
<interceptor-mapping path="/**" >
    <interceptor> 
        <ref bean="authorizationInterceptor" />
    </interceptor>
</interceptor-mapping>

5 Tips for using exception handlers

Exception handlers are a very convenient way to implement global exception handling in the Spring MVC framework. The following is an example of a typical exception handler implementation:

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(Exception ex) {
    
    
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
        errorResponse.setMessage(ex.getMessage());
        return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFoundException(UserNotFoundException ex) {
    
    
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setCode(HttpStatus.NOT_FOUND.value());
        errorResponse.setMessage(ex.getMessage());
        return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }
}

6 Tips for using custom annotations

Customizing annotations in the Spring MVC framework is a very convenient way to implement business logic. The following is an example of a typical custom annotation implementation:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthorizationRequired {
    
    
    String[] value();
}

After defining the annotation, it can be used in the Controller layer:

@RestController
@RequestMapping("/users")
public class UserController {
    
    
    @AuthorizationRequired({
    
    "admin"})
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public List<User> getUsers() {
    
    
        return userService.getUsers();
    }
}

In the interceptor, reflection can be used to obtain the annotation information defined on the controller method:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
    if (handler instanceof HandlerMethod) {
    
    
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        AuthorizationRequired annotation = handlerMethod.getMethodAnnotation(AuthorizationRequired.class);
        if (annotation != null) {
    
    
            String[] roles = annotation.value();
            if (/*判断用户是否具备指定角色*/) {
    
    
                return true;
            } else {
    
    
                response.setStatus(HttpStatus.FORBIDDEN.value());
                return false;
            }
        }
    }
    return true;
}

5. Optimization practice of Spring MVC framework

Optimization should be an important consideration when developing web applications using the Spring MVC framework. It's not enough to just implement functionality, you also need to make your application scalable and performant. Below are some optimization practices to improve the performance and maintainability of your application.

1 Make full use of the cache

  1. Cache common data

Caching is a common optimization technique that can significantly reduce application response time and system load. We can store commonly used data in the cache, such as popular product lists, user information, etc.

  1. Use the caching framework

The cache framework allows us to implement the cache function more conveniently, and provides some advanced functions, such as expiration time, cache hit rate, distributed cache, etc. There are many commonly used caching frameworks in Spring, such as Ehcache, Redis, Guava, etc. When using a cache framework, we need to choose an appropriate framework according to the actual situation, and we also need to consider the consistency and concurrent access of cached data.

2 Optimization of front-end and back-end

  1. Use CDN to accelerate static resources

The content distribution network (CDN) can distribute the static resources of the website (such as pictures, CSS, JS files) to the global nodes, so as to reduce the response time when users access these resources and improve the user experience. We can use the resource processor provided by the Spring MVC framework to configure the CDN address, as follows:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    
    @Value("${cdn.url}")
    private String cdnUrl;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    
    
        registry.addResourceHandler("/resources/**").addResourceLocations(cdnUrl);
    }
}
  1. Front-end code compression

In the production environment, the front-end code needs to be compressed to reduce file size and loading time. The front-end code can be compressed using many tools, such as YUI Compressor, Google Closure Compiler, etc.

  1. Backend code optimization

Optimizing the backend code allows us to better utilize server resources and improve application performance. We can optimize the following aspects:

  • Use a thread pool to manage requests
  • Use cache wisely
  • Reduce the number of database queries

3 Database optimization techniques

  1. Reduce the number of database connections

The number of database connections is a limited resource, and too many connections will lead to a decrease in database performance. We can use a connection pool to manage database connections so that we can quickly get a connection when we need it and release the connection when we are done using it.

  1. Fair use of indexes

Indexes can speed up database query operations, but too many or incorrect indexes can degrade query performance. We need to select the appropriate index according to the actual situation, and avoid creating duplicate or redundant indexes.

  1. Database sub-database sub-table

In large applications, a single database may not meet our needs. We can divide the database into databases and tables to improve the load capacity and scalability of the database. But this also needs to consider issues such as data consistency and concurrent access.

4 Optimization of Spring container

  1. Use annotations instead of XML configuration

After Spring 3.0, we can use annotations instead of XML configuration files. The annotation configuration is concise and clear, which also helps to improve the readability and maintainability of the code.

  1. Use the singleton pattern

In Spring, beans are singletons by default, and repeated creation of beans will result in a lot of memory overhead and performance degradation. We can make reasonable use of the singleton pattern to initialize the Bean and improve the performance of the application.

Guess you like

Origin blog.csdn.net/u010349629/article/details/130673455