2.0SpringMVC中报文信息转换器HttpMessageConverter 、文件上传、拦截器、异常处理器 和 注解方式

文章目录

😹 作者: gh-xiaohe
😻 gh-xiaohe的博客
😽 觉得博主文章写的不错的话,希望大家三连(✌关注,✌点赞,✌评论),多多支持一下!!!

🚏 HttpMessageConverter

    HttpMessageConverter,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文

报文信息转换器请求报文(浏览器到服务器 request对象),响应报文(服务器响应给浏览器)

    HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody 常用 ,RequestEntity,ResponseEntity 常用

🚀 1、@RequestBody

    @RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用==@RequestBody==进行标识,当前请求的请求体就会为当前注解所标识的形参赋值

<form th:action="@{/testRequestBody}" method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit">
</form>
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
    System.out.println("requestBody:"+requestBody);
    return "success";
}

输出结果:

requestBody:username=admin&password=123456

🚄 2、RequestEntity

    RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息

@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
     //当前requestEnity表示整个请求报文的信息
    System.out.println("requestHeader:"+requestEntity.getHeaders());
    System.out.println("requestBody:"+requestEntity.getBody());
    return "success";
}

输出结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pUAo0uwH-1656123432337)(HttpMessageConverter.assets/image-20220625092904406.png)]

🚒 3、@ResponseBody

原生 HttpServletResponse 实现

@RequestMapping("/testResponse")
public void testResponse(HttpServletResponse response) throws IOException {

    response.getWriter().print("hello,response");
 
	//response.getWriter().write()   //write() 是以流的形式,必须满足流的要求
	//response.getWriter(). print(Object object)  // 可以响应这一组数据   
    //这两句话到底是什么意思 :将print方法中内容直接作为响应报文的响应体,响应到浏览器,相应提示什么,在浏览器看到的就是什么  	
}

getWriter()

首先解释一下 PrintWriter = response.getWriter();

​ wirte()是类PrintWriter提供的一个方法,是发送请求内容至页面,ajax常用到这个
​ response.write()是直接在页面输出内容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OpFCFzxT-1656123432338)(HttpMessageConverter.assets/image-20220625094406896.png)]

    @ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文响应体响应到浏览器

    作用:方法返回的是 当前响应的响应体 (不书写是作为视图名称解析

@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
    return "成功";
}

结果:浏览器页面显示成功

🚤 4、SpringMVC处理json

    SpringMVC处理JSON (JSON一种数据交互格式)、xml也是一种数据交互格式

    JSON有两种格式:一种是json对象 {}、一种是json数组[]

    SpringMVC处理ajax(ajax就是页面,发生跳转的情况下,与=服务器进行交互,不能使用转发重定向,只能使用响应浏览器数据

🚬 @ResponseBody处理json的步骤:

🚭 1、导入jackson的依赖 (把java对象转化为json对象)
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
</dependency>
🚭 2、开启mvc的注解驱动

    在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串

<mvc:annotation-driven />
🚭 3、在处理器方法上使用@ResponseBody注解进行标识
🚭 4、将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串
@RequestMapping("/testResponseUser")
@ResponseBody
public User testResponseUser(){
    return new User(1001,"admin","123456",23,"男");
}

浏览器的页面中展示的结果:

{“id”:1001,“username”:“admin”,“password”:“123456”,“age”:23,“sex”:“男”}

🚗 5、SpringMVC处理ajax

🚬 a>请求超链接:

<div id="app">
	<a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br>
</div>

🚬 b>通过vue和axios处理点击事件:

<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
<script type="text/javascript">
    var vue = new Vue({
        el:"#app",
        methods:{
            testAjax:function (event) {
                axios({
                    method:"post",
                    url:event.target.href,
                    params:{
                        username:"admin",
                        password:"123456"
                    }
                }).then(function (response) {
                    alert(response.data);
                });
                event.preventDefault();
            }
        }
    });
</script>

🚬 c>控制器方法:

@RequestMapping("/testAjax")
@ResponseBody
public String testAjax(String username, String password){
    System.out.println("username:"+username+",password:"+password);
    return "hello,ajax";
}

🛹 6、@RestController 复合注解

    @RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了==@ResponseBody==注解

🛴 7、ResponseEntity

    ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文

自定义一个响应报文响应浏览器文件下载

🚏 文件上传和下载

🚀 1、文件下载

    使用ResponseEntity实现下载文件的功能

    @RequestMapping("/testDown")
    public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
        //获取ServletContext对象
        ServletContext servletContext = session.getServletContext();
        //获取服务器中文件的真实路径
        String realPath = servletContext.getRealPath("/static/img/1.jpg");
        System.out.println(realPath);
        //创建输入流
        InputStream is = new FileInputStream(realPath);
        //创建字节数组
        //is.available() 获取输入流所对应文件的所有字节数
        byte[] bytes = new byte[is.available()];
        //将流读到字节数组中
        is.read(bytes);
        //创建HttpHeaders对象设置响应头信息
        MultiValueMap<String, String> headers = new HttpHeaders();
        //设置要下载方式以及下载文件的名字  固定格式
        headers.add("Content-Disposition", "attachment;filename=1.jpg");
        //设置响应状态码
        HttpStatus statusCode = HttpStatus.OK;
        //创建ResponseEntity对象
        /*
        三个参数

            bytes:需要下载文件的所有字节(响应体)
            headers:响应头
            statusCode:响应状态码(200,404….)
        */
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, headers, statusCode);
        //关闭输入流
        is.close();
        return responseEntity;
    }

🚄 2、文件上传

文件上传要求:不可以用get请求,

  • post请求

  • enctype=“multipart/from-data” ,当前form表单中数据,就不是name=value的方式传输到服务器,而是通过二进制的方式

  SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息

🚬 上传步骤:

🚭 添加依赖:
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
🚭 在SpringMVC的配置文件中添加配置:
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
🚭 控制器方法:
    @RequestMapping("/testUp")
    public String testUp(MultipartFile photo, HttpSession session) throws IOException {
        //获取上传的文件的文件名
        String fileName = photo.getOriginalFilename();
        //获取上传的文件的后缀名   
        // 截取最后一次出现 . 的位置 到最后
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //将UUID作为文件名  // replaceAll("-",""); - 替换为 空
        String uuid = UUID.randomUUID().toString().replaceAll("-","");
        //将uuid和后缀名拼接后的结果作为最终的文件名
        fileName = uuid + suffixName;
        //通过ServletContext获取服务器中photo目录的路径
        ServletContext servletContext = session.getServletContext();
        String photoPath = servletContext.getRealPath("photo");
        File file = new File(photoPath);
        //判断photoPath所对应路径是否存在
        if(!file.exists()){
            //若不存在,则创建目录
            file.mkdir();
        }
        // 拼接全路径  File.separator 文件分割符
        //(没有文件服务器,传入到tomcat中)上传 到 部署服务器 的photo目录下(不存在新创建)
        // [E:\tomcat\apache-tomcat-9.0.56\webapps\ROOT\photo\5aa04da74d3d4c5dbe14475bfd5c6d52.png]
        String finalPath = photoPath + File.separator + fileName;
        //上传文件
        photo.transferTo(new File(finalPath));
        return "success";
    }

🚏 拦截器

🚀 1、拦截器的配置

    SpringMVC中的拦截器用于拦截控制器方法的执行

    SpringMVC中的拦截器需要实现HandlerInterceptor

🚬 SpringMVC.xml 配置 拦截器

<!--配置方式一: bean-->
<mvc:interceptors>
	<!--所写的拦截器 路径-->
	<!--一个bean就是IOC的组件(对象) 表示某一个类型的对象就是一个拦截器-->
	<bean class="com.atguigu.interceptor.FirstInterceptor"></bean> 
</mvc:interceptors>

<!--配置方式二: ref--> 
<!--ref引用IOC容器中的某个bean的id,引用某一个拦截器的id--> 
<mvc:interceptors>
    <!--所写的拦截器 类名 需要使用普通组件注解@Component 表示为IOC容器 + 扫描 -->
    <ref bean="firstInterceptor"></ref>
</mvc:interceptors>
<!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截,没有办法设置拦截规则 -->

<!--配置方式三:--> (推荐)
<mvc:interceptors>
	<mvc:interceptor>
    	<!--需要拦截的请求--> 
   	 	<mvc:mapping path="/**"/>
    	<!--需要排除的请求,即不需要拦截的请求--> 
    	<mvc:exclude-mapping path="/testRequestEntity"/>
    	<!--和上面的两种写法一样 bean 或者 ref--> 
    	<ref bean="firstInterceptor"></ref>
	</mvc:interceptor>
</mvc:interceptors>
<!--
	以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
-->
🚭 / 和 /* 和 /** 的区别
  • / :拦截所有请求 包括静态资源动态请求 但是不拦截jsp
  • /*:拦截所有请求所有的文件夹,但是不包括子文件夹 包括静态资源和动态请求 也拦截jsp
  • /**:的意思是所有文件里面的子文件夹

🚬 SpringMVC拦截器流程图片:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OJFkBx9b-1656212021942)(文件上传和下载.assets/image-20220625123321996.png)]

🚬 拦截器和过滤器:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZF41yg3G-1656212021943)(文件上传和下载.assets/image-20220626105110870.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p6pxlv6l-1656212021943)(文件上传和下载.assets/image-20220625123433331.png)]

🚬 源码
public class DispatcherServlet extends FrameworkServlet {


protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                        }

                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
					// 调用当前所有的拦截器的 PreHandle 方法
                    // mappedHandler 执行链 存放的是 执行的控制器方法 + 3个处理控制器方法的拦截器
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
					
                    // 控制器方法的执行
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    // 调用当前所有的拦截器的 PostHandle 方法
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }
				//processDispatchResult 里面有个 render方法 来渲染视图
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }
    
    
        private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
        boolean errorView = false;
        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                this.logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException)exception).getModelAndView();
            } else {
                Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                mv = this.processHandlerException(request, response, handler, exception);
                errorView = mv != null;
            }
        }

        if (mv != null && !mv.wasCleared()) {
            this.render(mv, request, response); // 渲染视图来对 ModelAndView进行处理
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        } else if (this.logger.isDebugEnabled()) {
            this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
        }

        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                // triggerAfterCompletion 拦截器的三个方法 在渲染视图完毕之后
                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
            }

        }
    }

🚄 2、拦截器的三个抽象方法

🚬 SpringMVC中的拦截器有三个抽象方法:

  • preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法

  • postHandle:控制器方法执行之后执行postHandle()

  • afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()

🚒 3、多个拦截器的执行顺序

🚬 a>若每个拦截器的preHandle()都返回true

    此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件配置顺序有关:

    preHandle()会按照配置的顺序执行,而postHandle()afterComplation()会按照配置的反序执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3GWwVfCK-1656212021944)(文件上传和下载.assets/image-20220625123517430.png)]

🚬 b>若某个拦截器的preHandle()返回了false

    preHandle()返回false和它之前的拦截器的preHandle()都会执行postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行

  • 举例:拦截器 1 2 3 4 5 3 未放行
    • preHandle 123
    • postHandle 一个都不会执行
    • afterCompletion 1 2 执行

🚏 异常处理器

🚀 1、基于配置的异常处理

    SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver

    HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolverSimpleMappingExceptionResolver

    作用:在控制器方法执行时,出现异常,返回新的ModelAndView,跳转到制定页面

SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
        	<!--
        		properties的键表示处理器方法执行过程中出现的异常
        		properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
        	-->
            <prop key="java.lang.ArithmeticException">error</prop>
        </props>
    </property>
    <!--
    	exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
    -->
    <property name="exceptionAttribute" value="ex"></property>
</bean>
error 页面

<body>
出现错误
<p th:text="${ex}"></p>
</body>

🚄 2、基于注解的异常处理

//@ControllerAdvice将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {

    //@ExceptionHandler用于设置所标识方法处理的异常
    @ExceptionHandler(ArithmeticException.class)
    //ex表示当前请求处理中出现的异常对象
    public String handleArithmeticException(Exception ex, Model model){
        model.addAttribute("ex", ex);
        return "error";
    }

}

🚏 注解配置SpringMVC

    使用配置类和注解代替web.xmlSpringMVC配置文件的功能

🚀 1、创建初始化类,代替web.xml

    在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果找到的话就用它来配置Servlet容器(Tomcat)Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。

    Spring3.2引入了一个便利WebApplicationInitializer基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文。

//web工程的初始化类,用来代替web.xml
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {

    /**
     * 指定spring的配置类
     * @return
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

    /**
     * 指定springMVC的配置类
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    /**
     * 指定DispatcherServlet的映射规则,即url-pattern
     * @return
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    /**
     * 注册过滤器
     * @return
     */
    @Override
    protected Filter[] getServletFilters() {
        // 配置编码过滤器
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        // 设置初始化参数
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceResponseEncoding(true);
        //配置HiddenHttpMethodFilter
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
    }
}

🚄 2、创建SpringConfig配置类,代替spring的配置文件

@Configuration
public class SpringConfig {
	//ssm整合之后,spring的配置信息写在此类中
}

🚒 3、创建WebConfig配置类,代替SpringMVC的配置文件

/**
 * 代替SpringMVC的配置文件:
 * 1、扫描组件   2、视图解析器     3、view-controller    4、default-servlet-handler静态资源处理
 * 5、mvc注解驱动    6、文件上传解析器   7、异常处理      8、拦截器
 */
//将当前类标识为一个配置类
@Configuration
//1、扫描组件
@ComponentScan("com.atguigu.mvc.controller")
//5、mvc注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    //4、default-servlet-handler
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    //8、拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        TestInterceptor testInterceptor = new TestInterceptor();
        // addPathPatterns 添加拦截路径
        // excludePathPatterns 排除拦截路径
        registry.addInterceptor(testInterceptor).addPathPatterns("/**");
        registry.addInterceptor(testInterceptor).excludePathPatterns();

    }

    //3、view-controller
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/hello").setViewName("hello");
    }

    //6、文件上传解析器
    @Bean
    public MultipartResolver multipartResolver() {
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
        return commonsMultipartResolver;
    }

    //7、异常处理
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
        Properties prop = new Properties();
        prop.setProperty("java.lang.ArithmeticException", "error");
        exceptionResolver.setExceptionMappings(prop);
        exceptionResolver.setExceptionAttribute("exception");
        resolvers.add(exceptionResolver);
    }

    // 2、视图解析器
    // 配置生成模板解析器(jsp只需配置 ITemplateResolver 即可) Thymeleaf 3个全部都需要配置
    @Bean
    public ITemplateResolver templateResolver() {
        WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
        // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
                webApplicationContext.getServletContext());
        templateResolver.setPrefix("/WEB-INF/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        return templateResolver;
    }

    // 生成模板引擎并为模板引擎注入模板解析器
    @Bean
    public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        return templateEngine;
    }

    // 生成视图解析器并未解析器注入模板引擎
    @Bean
    public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setCharacterEncoding("UTF-8");
        viewResolver.setTemplateEngine(templateEngine);
        return viewResolver;
    }

}

🚤 4、测试功能

@RequestMapping("/")
public String index(){
    return "index";
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gh-xiaohe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值