第4章:Spring MVC基础(2)

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/");

    }
}

代码解释:
1.@EnableWebMvc开启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();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值