第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/");
}
}
代码解释:
[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();
}
}
下一篇: 猫耳FM如何连麦?猫耳FM中连麦的方法
推荐阅读
-
struts2+spring+hibernate分页代码[比较多]第1/7页
-
ASP.NET Core MVC/WebApi基础系列2
-
Spring入门(十四):Spring MVC控制器的2种测试方法
-
Linux shell脚本基础学习详细介绍(完整版)第1/2页
-
awk基础知识小结第1/2页
-
PHP和Mysqlweb应用开发核心技术 第1部分 Php基础-3 代码组织和重用2
-
PHP和Mysqlweb应用开发核心技术-第1部分 Php基础-2 php语言介绍
-
PHP学习一(基础)第1/2页
-
黑马程序员IDEA版JAVA基础班\JavaWeb部分视频\2-10Request和Response\第5节 request登录案例
-
awk基础知识小结第1/2页