SpringMVC source code analysis-basic knowledge (two)

The project address
SpringMVC_02
thinks the blogger can also give a Star

1. Annotation form is
introduced instead of web.xml maven

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>

Can we see
Insert picture description herethis familiarity first ? When we talked about ServletContainerInitializer above, the directories are exactly the same, only the class name is different.
The self-written org.springframework.web.SpringServletContainerInitializer added inside has the same function as ServletContainerInitializer, loading third-party dependencies to implement initialization operations. This is also the key part of springboot not using web.xml.

Now that we want to get rid of web.xml completely, we must first understand what configuration we have previously accessed in web.xml. In the previous article, there are scan annotations in the configuration ( jump ), open the mvc transaction, and of course there is a very important xml file. The above three can use @ComponentScan, @EnableWebMvc, @Configuration. There is also a very important DispatcherServlet filter in Web.xml. How do we need to inject this?
Use the WebApplicationInitializer class to achieve the
specific code as follows:
MyConfig.java:

package com.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;

/**
 * @author 龙小虬
 * @date 2021/3/9 17:33
 */
@Configuration
@ComponentScan("com.controller")
@EnableWebMvc
public class MyConfig {
    
    
}

WebInitializer.java:

package com.config;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

/**
 * @author 龙小虬
 * @date 2021/3/9 18:06
 */
public class WebInitializer implements WebApplicationInitializer {
    
    
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
    
    
        // 1.   创建SpringMVC容器
        AnnotationConfigWebApplicationContext app = new AnnotationConfigWebApplicationContext();
        // 2. 注册我们的配置文件
        app.register(MyConfig.class);
        // 注册我们的
        DispatcherServlet dispatcherServlet = new DispatcherServlet(app);
        ServletRegistration.Dynamic dynamic = servletContext.addServlet("dispatcherServlet", dispatcherServlet);
        dynamic.addMapping("/");
        dynamic.setLoadOnStartup(1);// 最优先启动
    }
}

MyController.java:

package com.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 龙小虬
 * @date 2021/3/9 17:33
 */
@Controller
public class MyController {
    
    

    // produces = "text/html;charset=UTF-8" 解决中乱码
    @RequestMapping(value = "/member",produces = "text/html;charset=UTF-8")
    @ResponseBody
    public String Member(){
    
    
        return "解决掉web.xml";
    }
}

Now we have started successfully, and there is no web.xml, but we will find that it cannot be forwarded to *.jsp. So we need to configure.
MyConfig.java:

package com.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;

/**
 * @author 龙小虬
 * @date 2021/3/9 17:33
 */
@Configuration
@ComponentScan("com.controller")
@EnableWebMvc
public class MyConfig {
    
    

    @Bean
    public ViewResolver viewResolver() {
    
    
        InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
        // 前缀
        internalResourceViewResolver.setPrefix("/WEB-INF/view/");
        // 后缀
        internalResourceViewResolver.setSuffix(".jsp");
        return internalResourceViewResolver;
    }

}

Just add this code. (Remember, web.xml is taken first by default, so web.xml and other xml must be removed first) The
code is complete. You can think about a problem.
The implementation class of WebApplicationInitializer is not injected but can be used. Why?
We have seen the contents of a jar package before, which contains "org.springframework.web.SpringServletContainerInitializer", we enter this class, can you see
Insert picture description herethat it is very familiar? Is this the annotation we used before? He will
inject all the subclasses that implement the WebApplicationInitializer class.

2. The difference between @RestController and @Controller
Click on @RestController to find that
Insert picture description hereit contains two annotations @Controller and @ResponseBody, which means that @RestController has the function of these two annotations, one is to return JSON data, the other is bean injection Comment.

3. Interceptors and filters

  1. The same point: all are based on AOP technology, the method is enhanced, and the request can be intercepted.
  2. Differences:
    1) The filter is developed by the servlet itself, and the interceptor technology is developed by SpringMVC itself.
    2) The filter is used to intercept WEB requests, and the interceptor can intercept not only requests but also common methods.
    3) Filters precede interception. The interceptor encapsulation method is easier to use than the filter interception.
    Application scenarios:
    Interceptors are used in most cases in practical development.
    Filter application scenarios: encoding conversion, cross-domain resolution;
    interceptor application scenarios: permission control, logging Printing, parameter verification, conversation

4. Use of SpringMVC interceptor
Application scenario: custom token verification (implement HandlerInterceptor interface) to
create MyHandlerInterceptor.java

package com.interceptor;

import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 龙小虬
 * @date 2021/3/11 14:42
 */
public class MyHandlerInterceptor implements HandlerInterceptor {
    
    

    /**
     * 目标方法执行之前执行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        String token = request.getParameter("token");
        if(StringUtils.isEmpty(token)){
    
    
            response.getWriter().print("not token");
            return false;
        }
        return true;
    }

    /**
     * 目标方法执行之后执行
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
        System.out.println("postHandle");
    }

    /**
     * 目标方法执行之后执行
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        System.out.println("afterCompletion");
    }
}

Now inject the MyHandlerInterceptor interceptor
and change the code in the MyConfig.java we created earlier .

package com.config;

import com.interceptor.MyHandlerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

/**
 * @author 龙小虬
 * @date 2021/3/9 17:33
 */
@Configuration
@ComponentScan("com.controller")
//@EnableWebMvc  若要添加拦截器必须将此注解删除,否则拦截器不会生效
public class MyConfig extends WebMvcConfigurationSupport {
    
    

    @Bean
    public ViewResolver viewResolver() {
    
    
        InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
        // 前缀
        internalResourceViewResolver.setPrefix("/WEB-INF/view/");
        // 后缀
        internalResourceViewResolver.setSuffix(".jsp");
        return internalResourceViewResolver;
    }

    public MyHandlerInterceptor myHandlerInterceptor(){
    
    
        return new MyHandlerInterceptor();
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
    
    
        super.addInterceptors(registry);
        registry.addInterceptor(myHandlerInterceptor()).addPathPatterns("/**");
    }
}

Run and visit http://localhost:8080/SpringMVC_02_war/abc, you can see the result. In
Insert picture description here
this way, we can only access the URL with the token parameter.
For example: (http://localhost:8080/SpringMVC_02_war/abc?token=1)

5. Asynchronous use

Asynchronous use is generally used for some time-consuming processing in our target method. Then we can use asynchrony to create another thread for this very time-consuming process.

Let's try it out below.
Create AsyncService.java

package com.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

/**
 * @author 龙小虬
 * @date 2021/3/11 15:01
 */
@Async
@Service
public class AsyncService {
    
    
	// 此方法替代很占用时间的处理
    public String Test(){
    
    
        try {
    
    
            System.out.println("开始休眠:"+Thread.currentThread().getName());
            Thread.sleep(3000);
            System.out.println("结束休眠:"+Thread.currentThread().getName());
            return "success";
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return "fail";
    }
}

In MyConfig.java, increase the scope of package scanning and enable asynchronous

@ComponentScan(basePackages = {
    
    "com.controller","com.service"})
@EnableAsync //打开异步

In MyController.java, inject AsyncService and add interfaces.

	@Autowired
    AsyncService asyncService;
    
	@RequestMapping(value = "/async",produces = "text/html;charset=UTF-8")
    @ResponseBody
    public String async(){
    
    
        System.out.println("方法开始调用:"+Thread.currentThread().getName());
        String res = asyncService.Test();
        System.out.println("方法结束调用:"+Thread.currentThread().getName()+";res:"+res);
        return "AsyncController";
    }

Run and visit, we look at the results, we
Insert picture description herewill find that the value of res we printed is empty, and the value of res is a very time-consuming process. So what do we need to do? (It's impossible to change it back to sync again, right?)

Then we need to use Callable at this time, let's get to know Callable first.
The callable interface of Callable is similar to Runnable, but Callable can return a value after the task is executed. Others are similar to Runnable.

Then let's use it.
MyController.java add interface

	@RequestMapping(value = "/callable",produces = "text/html;charset=UTF-8")
    @ResponseBody
    public Callable<String> callable(){
    
    
        System.out.println("方法开始调用:"+Thread.currentThread().getName());
        Callable callable = new Callable<String>(){
    
    
            @Override
            public String call() throws Exception {
    
    
                return asyncService.Test();
            }
        };
        System.out.println("方法结束调用:"+Thread.currentThread().getName()+";res: "+callable.toString());
        return callable;
    }

Run it directly and visit http://localhost:8080/SpringMVC_02_war/callable?token=1 and you will find that the
Insert picture description here
error is reported directly, but there is a very important hint ==> <async-supported>true</async-supported>
This paragraph should be familiar, I have seen it in web.xml , Mentioning web.xml, we should be able to think that in the class WebInitializer.java we created before, we have also configured dispatcherServlet, dispatcherServlet can be configured, then that important prompt can definitely be configured, just add code dynamic.setAsyncSupported(true);.
The current code in our WebInitializer.java is as follows:

package com.config;

import com.interceptor.MyHandlerInterceptor;
import org.springframework.web.SpringServletContainerInitializer;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

/**
 * @author 龙小虬
 * @date 2021/3/9 18:06
 */
public class WebInitializer implements WebApplicationInitializer {
    
    
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
    
    
        // 1.   创建SpringMVC容器
        AnnotationConfigWebApplicationContext app = new AnnotationConfigWebApplicationContext();
        // 2. 注册我们的配置文件
        app.register(MyConfig.class);
        // 注册我们的
        DispatcherServlet dispatcherServlet = new DispatcherServlet(app);
        ServletRegistration.Dynamic dynamic = servletContext.addServlet("dispatcherServlet", dispatcherServlet);
        dynamic.addMapping("/");
        dynamic.setLoadOnStartup(1);// 最优先启动
        dynamic.setAsyncSupported(true);
//        SpringServletContainerInitializer
//        WebMvcConfigurerAdapter
    }
}

We run it again and visit http://localhost:8080/SpringMVC_02_war/callable?token=1, we will find that there is no error, but there is nothing on the page. Insert picture description hereWe added an asynchronous annotation to the processing that took a long time before writing. As long as the asynchronous annotation (@Async) is removed, the access will be normal.

There is a question left. Many people may have discovered that when we wrote the token verification interceptor, the annotations of the two methods postHandl() and afterCompletion() are executed after the target method is executed . Why do we need two methods? We will explain later.

Guess you like

Origin blog.csdn.net/weixin_43911969/article/details/114633548