欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第4章:Spring MVC基础(2)

程序员文章站 2022-05-07 17:31:32
...

4.4 Spring MVC基本配置

Spring MVC的定制配置需要我们的配置类继承一个WebMvcCongfigurerAdapter类,并在此类使用@EnableWebMvc注解,来开启对Spring MVC的配置支持,这样我们可以重写这个类的方法,完成我们的常用配置.

我们将前面的MyMvcConfig配置类继承WebMvcConfigurerAdapter,本章若不做特别说明,则关于配置的相关内容都在MyMvcConfig里编写.

4.4 .1 静态资源映射

1.点睛
程序的静态文件(js、css、图片)等需要直接访问,这时我们可以在配置里重写addResourceHandlers方法来实现.

2.示例
①:添加静态资源
同上,我们在src/main/resource 下建立asset/js目录,并复制一个jquery.js放置在此目录下.

@Configuration
@EnableWebMvc// 1
@ComponentScan("com.wisely.highlight_springmvc4")
public class MyMvcConfig extends WebMvcConfigurerAdapter {// 2
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return viewResolver;
    }


    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        // 3
        registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");

    }
}

代码解释:
[email protected]开启SpringMVC支持,若无此句,重写WebMvcConfigurerAdapter方法无效.
3.addResourceLocations指的是文件放置目录.addResourceHandler指的是对外暴露的访问路径.

4.4 .2 拦截器配置

1.点睛
拦截器(Interceptor)实现对每一个请求处理前后进行相关业务处理,类似于Servlet的Filter.
可让普通的Bean实现HanlderInterceptor接口或者继承HandlerInterceptorAdapter类来实现自定义拦截器.
通过重写WebMvcConfigurerAdapter的addInterceptors方法来注册自定义拦截器,本节演示一个简单的拦截器的开发和配置,业务含义为计算每一次请求的处理时间.

(1) 示例拦截器

public class DemoInterceptor extends HandlerInterceptorAdapter {//1

    @Override
    public boolean preHandle(HttpServletRequest request, //2
            HttpServletResponse response, Object handler) throws Exception {
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, //3
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        long startTime = (Long) request.getAttribute("startTime");
        request.removeAttribute("startTime");
        long endTime = System.currentTimeMillis();
        System.out.println("本次请求处理时间为:" + new Long(endTime - startTime)+"ms");
        request.setAttribute("handlingTime", endTime - startTime);
    }

}

代码解释
①:继承HandlerInterceptorAdapter 类来实现自定义拦截器
②:重写preHandle方法,在请求发生前执行.
③:重写postHandle方法,在请求完成后执行.

2.配置

@Bean
// 1
public DemoInterceptor demoInterceptor() {
    return new DemoInterceptor();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {// 2
    registry.addInterceptor(demoInterceptor());
}

代码解释
①:配置拦截器的Bean
②:重写addInterceptors方法,注册拦截器

4.4.3 @ControllerAdvice

1.点睛
通过@ControllerAdvice,我们可以将对于控制器的全局配置放在同一个位置,注解了@Controller的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上,这对所有注解了@RequestMapping的控制器内的方法有效.

@ExceptionHandler:用于处理控制器里的异常
@InitBinder:用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中
@ModelAttribute:@ModelAttribute本来的作用是绑定键值对到Model里,此处是让全局的@RequestMapping都能或得到此处设置的键值对.

本节演示使用@ExceptionHandler处理全局异常,更人性化的将异常输出给用户

2.示例
(1)定制ControllerAdvice

@ControllerAdvice //1
public class ExceptionHandlerAdvice { 

    @ExceptionHandler(value = Exception.class)//2
    public ModelAndView exception(Exception exception, WebRequest request) {
        ModelAndView modelAndView = new ModelAndView("error");// error页面
        modelAndView.addObject("errorMessage", exception.getMessage());
        return modelAndView;
    }

    @ModelAttribute //3
    public void addAttributes(Model model) {
        model.addAttribute("msg", "额外信息"); //3
    }

    @InitBinder //4
    public void initBinder(WebDataBinder webDataBinder) {
        webDataBinder.setDisallowedFields("id"); //5
    }
}

代码解释:
①:@ControllerAdvice声明一个控制器建言,@ControllerAdvice组合了@Component注解,所以自动注册为Spring的Bean
②:@ExceptionHandler在此处定义全局处理,通过@ExceptionHandler的value属性可过滤拦截的条件,在此处我们可以看出我们拦截所有的Exception
③:此处使用@ModelAttribute注解将键值对添加到全局,所有注解@RequestMapping的方法可获得此键值对.
④:通过InitBinder注解制定WebDataBinder
⑤:此处演示忽略request参数的id

(2) 演示控制器

@Controller
public class AdviceController {
    @RequestMapping("/advice")
    public String getSomething(@ModelAttribute("msg") String msg,DemoObj obj){//1

        throw new IllegalArgumentException("非常抱歉,参数有误/"+"来自@ModelAttribute:"+ msg);
    }

}

(3)异常展示页面

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>@ControllerAdvice Demo</title>
</head>
<body>
    ${errorMessage}
</body>
</html>

4.4.4 其他配置

1.快捷的ViewController
在4.2.2节我们配置页面转向的时候使用的代码如下

@Controller//1
public class HelloController {
    @RequestMapping("/index")//2
    public  String hello(){
        return "index";
    }
}

此处无任何业务处理,只是简单的页面转向,写了至少三行有效代码;在实际开发中会涉及大量这样的页面转向,若都这样写会很麻烦,我们可以通过在配置中重写addViewControllers来简化配置

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/index").setViewName("/index");
}

这样实现的代码更简洁,管理更集中

2.路径匹配参数配置
在Spring MVC中,路径参数如果带”.”的话,”.”后面的值将被忽略,例如,访问http://locahost:8080/anno/pathvar/xx.yy此时”.”后面的yy被忽略

通过重写configurePathMatch方法可不忽略”.”后面的参数,代码如下:

@Override
 public void configurePathMatch(PathMatchConfigurer configurer) {
 configurer.setUseSuffixPatternMatch(false);
 }

4.5 Spring MVC的高级配置

4.5.1 文件上传配置

1.点睛
文件上传是一个项目里经常使用的功能,Spring MVC通过配置一个MultipartResolver来上传文件.
在Spring的控制器中,通过MultipartFile file来接收文件,通过MultipartFlie[] files接收多个文件上传.

2.示例
(1)添加文件上传依赖

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
<!-- 非必需,可简化IO操作 -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.3</version>
</dependency>

(2)上传页面.在src/main/resources/views下新建upload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>upload page</title>

</head>
<body>
<div class="upload">
    <form action="upload" enctype="multipart/form-data" method="post">
        <input type="file" name="file"/><br/>
        <input type="submit" value="上传">
    </form>
</div>
</body>
</html>

(3)添加转向到upload页面的ViewController

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/toUpload").setViewName("/upload");
}

(4) MultipartResolver配置

@Bean
public MultipartResolver multipartResolver() {
    CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
    multipartResolver.setMaxUploadSize(1000000);
    return multipartResolver;
}

(5) 控制器

package com.wisely.highlight_springmvc4.web.ch4_5;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

@Controller
public class UploadController {

    @RequestMapping(value = "/upload",method = RequestMethod.POST)
    public @ResponseBody String upload(MultipartFile file) {//1

            try {
                FileUtils.writeByteArrayToFile(new File("e:/upload/"+file.getOriginalFilename()),
                        file.getBytes()); //2
                return "ok";
            } catch (IOException e) {
                e.printStackTrace();
                return "wrong";
            }


    }

}

代码解释
①:使用MultipartFile file接受上传的文件
②:使用FileUtils.writeByteArrayToFile快速写文件到磁盘

4.5.2 自定义HttpMessageConverter

1.点睛
HttpMessageConverter是用来处理request和response里的数据的.Spring为我们内置了大量的HttpMessageConverter,例如,MappingJackson2HttpMessageConverter

package com.wisely.highlight_springmvc4.messageconverter;

import java.io.IOException;
import java.nio.charset.Charset;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.StreamUtils;

import com.wisely.highlight_springmvc4.domain.DemoObj;

public class MyMessageConverter extends AbstractHttpMessageConverter<DemoObj> {//1

    public MyMessageConverter() {
        super(new MediaType("application", "x-wisely",Charset.forName("UTF-8")));//2
    }

    /**
     * 3
     */

    @Override
    protected DemoObj readInternal(Class<? extends DemoObj> clazz,
            HttpInputMessage inputMessage) throws IOException,
            HttpMessageNotReadableException {
        String temp = StreamUtils.copyToString(inputMessage.getBody(),

        Charset.forName("UTF-8"));
        String[] tempArr = temp.split("-");
        return new DemoObj(new Long(tempArr[0]), tempArr[1]);
    }

    /**
     * 4
     */
    @Override
    protected boolean supports(Class<?> clazz) {
        return DemoObj.class.isAssignableFrom(clazz);
    }

    /**
     * 5
     */
    @Override
    protected void writeInternal(DemoObj obj, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        String out = "hello:" + obj.getId() + "-"
                + obj.getName();
        outputMessage.getBody().write(out.getBytes());
    }

}

代码解释:
①:继承AbstractHttpMessageConverter接口来实现自定义的HttpMessageConverter.
②:新建一个我们自定义的媒体类型application/x-wisely
③:重写readInternal方法,处理请求的数据.代码表明我们处理由”-“隔开的数据,并转换成DemoObj对象.
④:表明本HttpMessageConverter只处理DemoObj这个类
⑤:重写writeInternal,处理如何输出数据到response,此例中我们在院上输出前面加上”hello”.

(2)配置.在addviewControllers中添加viewController映射页面访问演示页面,代码如下:

registry.addViewController("/converter").setViewName("/converter");

配置自定义的HttpMessageConverter的Bean,在Spring MVC里注册HttpMessageConverter有两个方法:
configureMessageConverters:重载会覆盖掉Spring MVC默认注册的多个HttpMessageConverter

extendMessageConverters:仅添加一个自定义的HttpMessageConverter,不覆盖默认注册的HttpMessageConverter.
所以此例中我们重写extendMessageConverters:

@Override
   public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
       converters.add(converter());
   }

@Bean 
public MyMessageConverter converter(){
    return new MyMessageConverter();
}

(3) 演示控制器

@Controller
public class ConverterController {

    @RequestMapping(value = "/convert", produces = { "application/x-wisely" }) //1
    public @ResponseBody DemoObj convert(@RequestBody DemoObj demoObj) {

        return demoObj;

    }

}

代码解释
①:指定返回的媒体类型为我们自定义的 媒体类型application/x-wisely

(4)演示页面.在src/main/resources 下新建conventer.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>HttpMessageConverter Demo</title>
</head>
<body>
    <div id="resp"></div><input type="button" onclick="req();" value="请求"/>
<script src="assets/js/jquery.js" type="text/javascript"></script>
<script>
    function req(){
        $.ajax({
            url: "convert",
            data: "1-wangyunfei", //1
            type:"POST",
            contentType:"application/x-wisely", //2
            success: function(data){
                $("#resp").html(data);
            }
        });
    }

</script>
</body>
</html>

代码解释:
①:注解这里的数据格式,后台处理按照此格式处理,用”-“隔开.
②:contentType设置的媒体类型使我们自定义的application/x-wisely.

4.5.3 服务器端推动技术

服务端推送技术在我们日常开发中较为常用,可能早起很多人的解决方案是使用Ajax向服务器轮询消息,使浏览器尽可能第一时间获得服务端的消息,因为这种方式的轮询频率不好控制,所以大大增加了服务端的压力.

本节所有的服务端推送的方案都是基于:当客户端向服务端发送请求,服务端会抓住这个请求不放,等有数据更新才返回给客户端,当客户端接收到消息后,在向服务端发送请求,周而复始,这种方式的好处是减少了服务器的请求数量,大大减少了服务器的压力.

除了服务端推送技术以外,还有一个另外的双向通信的技术–webSocket,我们将在本书的第三部分实战Spring Boot中演示.

本节将提供基于SSE(Server Send Event 服务端发送事件)的服务器端推送和基于Servlet3.0+异步方法特性,其中的第一种方式需要新式浏览的支持,第二种方式是跨浏览器的.

1:SSE
(1)演示控制器

@Controller
public class SseController {

    @RequestMapping(value="/push",produces="text/event-stream") //1
    public @ResponseBody String push(){
         Random r = new Random();
        try {
                Thread.sleep(5000);
        } catch (InterruptedException e) {
                e.printStackTrace();
        }   
        return "data:Testing 1,2,3" + r.nextInt() +"\n\n";
    }

}

代码解释:
①:注意,这里使用输出的媒体类型为text/enent-stream,这是服务器端SSE的支持,本例演示每5秒钟向服务器推送随机消息.

(2)演示页面.在src/main/resources/views下新建see.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>SSE Demo</title>

</head>
<body>


<div id="msgFrompPush"></div>
<script type="text/javascript" src="<c:url value="assets/js/jquery.js" />"></script>
<script type="text/javascript">


 if (!!window.EventSource) { //1
       var source = new EventSource('push'); 
       s='';
       source.addEventListener('message', function(e) {//2
           s+=e.data+"<br/>";
           $("#msgFrompPush").html(s);

       });

       source.addEventListener('open', function(e) {
            console.log("连接打开.");
       }, false);

       source.addEventListener('error', function(e) {
            if (e.readyState == EventSource.CLOSED) {
               console.log("连接关闭");
            } else {
                console.log(e.readyState);    
            }
       }, false);
    } else {
            console.log("你的浏览器不支持SSE");
    } 
</script>
</body>
</html>

代码解释
①:EventSource对象只有新式的浏览器才有(Chrome、Firefox)等,EventSource是SSE的客户端
②:添加SSE客户端监听,在此获得服务器推送的消息

(3)配置
添加转向see.jsp页面映射

registry.addViewController("/see").setViewName("/see");

2.Servlet 3.0+异步方法处理
(1) 开启异步方法支持:

public class WebInitializer implements WebApplicationInitializer {//1

    @Override
    public void onStartup(ServletContext servletContext)
            throws ServletException {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(MyMvcConfig.class);
        ctx.setServletContext(servletContext); //2

        Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx)); //3
        servlet.addMapping("/");
        servlet.setLoadOnStartup(1);
        servlet.setAsyncSupported(true);//此句开启异步方法支持

    }

}

(2) 演示控制器

@Controller
public class AysncController {
    @Autowired
    PushService pushService; //1

    @RequestMapping("/defer")
    @ResponseBody
    public DeferredResult<String> deferredCall() { //2
        return pushService.getAsyncUpdate();
    }

}

代码解释:
异步任务的实现是通过控制器从另外一个线程返回一个DeferredResult,这里的DeferredResult是从pushService中获得的.
①:定时任务,定时更新DeferredResult
②:返回给客户端DeferredResult

(3)定时任务:

@Service
public class PushService {
    private DeferredResult<String> deferredResult; //1

    public DeferredResult<String> getAsyncUpdate() {
        deferredResult = new DeferredResult<String>();
        return deferredResult;
    }

    @Scheduled(fixedDelay = 5000)
    public void refresh() {
        if (deferredResult != null) {
            deferredResult.setResult(new Long(System.currentTimeMillis())
                    .toString());
        }
    }


}

代码解释:
①:在PushService里产生DeferredResult给控制器使用,通过@Scheduled注解的方法定时更新DeferredResult.

(4)演示页面
在src/main/resources/views 下新建async.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>servlet async support</title>

</head>
<body>



<script type="text/javascript" src="assets/js/jquery.js"></script>
<script type="text/javascript">

    deferred();//1

    function deferred(){
        $.get('defer',function(data){
            console.log(data); //2
            deferred(); //3
        });
    }


</script>
</body>
</html>

代码解释
此处的代码使用的是jQuery的Ajax请求,所以没有浏览器兼容性问题.
①:页面打开后就向后台发送请求
②:在控制台输出服务端推送的数据
③:一次请求完成后再向后台发送请求

4.6 Spring MVC的测试

4.6.1 点睛

在Spring里,我们使用WebAppConfiguration指定加载的ApplicationContext是一个WebApplicationContext.

测试驱动开发(Test Driven Development,TDD)我们按照需求先编写一个自己预期结果的测试用例,这个测试用例刚开始肯定是失败的测试,随着不断的编码和重构,最终让测试用例通过测试,这样才能保证软件的质量和可控性.

在下面的示例里我们借助JUnit和Spring TestContext framework,分别演示对普通页面转向控制器和RestController进行测试.

4.6.2 示例

(1)测试依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring-framework.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>

(2)演示服务

@Service
public class DemoService {

    public String saySomething(){
        return "hello";
    }

}

(3)测试用例,在src/test/java下:

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.wisely.highlight_springmvc4.MyMvcConfig;
import com.wisely.highlight_springmvc4.service.DemoService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {MyMvcConfig.class})
@WebAppConfiguration("src/main/resources") //1
public class TestControllerIntegrationTests {
    private MockMvc mockMvc; //2

    @Autowired
    private DemoService demoService;//3

    @Autowired 
    WebApplicationContext wac; //4

    @Autowired 
    MockHttpSession session; //5

    @Autowired 
    MockHttpServletRequest request; //6

    @Before //7
    public void setup() {
        mockMvc =
                MockMvcBuilders.webAppContextSetup(this.wac).build(); //2
        }

    @Test
    public void testNormalController() throws Exception{
        mockMvc.perform(get("/normal")) //8
                .andExpect(status().isOk())//9
                .andExpect(view().name("page"))//10
                .andExpect(forwardedUrl("/WEB-INF/classes/views/page.jsp"))//11
                .andExpect(model().attribute("msg", demoService.saySomething()));//12

    }

    @Test
    public void testRestController() throws Exception{
        mockMvc.perform(get("/testRest")) //13
        .andExpect(status().isOk())
         .andExpect(content().contentType("text/plain;charset=UTF-8"))//14
        .andExpect(content().string(demoService.saySomething()));//15
    }

}

代码解释:
①:@WebAppConfiguration 注解在类上,用来声明加载的ApplicationContext是一个WebApplicationContext.它的属性指定Web资源的位置,默认src/main/webapp,本例修改为src/main/resources.
②:MockMvc 模拟MVC对象,通过MockMvcBuilders.webAppContextSetup(this.wac).build()初始化
③:可以在测试用例中注入Spring的Bean
④:可注入WebApplicationContext
⑤:可注入模拟http session
⑥:可注入模拟的http request
⑦:@Before 在测试前进行初始化工作
⑧:模拟向/normal进行get请求
⑨:预期返回状态为200
⑩:预期view的名称为page
11:预期页面转向的真正路径为/WEB-INF/classes/views/paage.jsp
12:预期model里的值是demoService.saySomething()返回值hello
13:模拟向/testRest进行get请求
14:预期返回值得媒体类型为 text/plain;charset=UTF-8
15:预期model里的值是demoService.saySomething()返回值hello

(4)编写普通控制器

//@Controller
public class NormalController {
    @Autowired
    DemoService demoService;



    @RequestMapping("/normal")
    public  String testPage(Model model){
        model.addAttribute("msg", demoService.saySomething());
        return "page";
    }

}

(5) 编写普通控制器演示页面,在src/main/resources/views下新建page.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test page</title>
</head>
<body>
    <pre>
        Welcome to Spring MVC world
    </pre>
</body>
</html>

(6)编写RestController控制器

//@RestController
public class MyRestController {

    @Autowired
    DemoService demoService;

    @RequestMapping(value = "/testRest" ,produces="text/plain;charset=UTF-8")
    public @ResponseBody String testRest(){
        return demoService.saySomething();
    }

}
相关标签: springMVC高级应用