Spring MVC基础

Spring MVC概述

MVC:Model +View +Controller(数据模型+视图+控制器);
三层架构:Presentation tier +Application tier +Data tier(展现层+应用层+数据访问层);
实际上MVC只存在三层架构的展现层。

Spring MVC快速搭建

MyMvcConfig.java

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

WebInitializer.java

public class WebInitializer implements WebApplicationInitializer{

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(MyMvcConfig.class);
        ctx.setServletContext(servletContext);
        Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
        servlet.addMapping("/");
        servlet.setLoadOnStartup(1);
    }

}

HelloController.java

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

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" 
pageEncoding="UTF-8"%> 
<!DOCTYPE html> 
<html> 
<head> 
<meta charset="UTF-8"> 
<title>Insert title here</title> 
</head> 
<body> 
Spring MVC
</body> 
</html>

这里写图片描述

Spring MVC常用注解

DemoObj.java

public class DemoObj {
private Long id;
private String name;

public DemoObj() { //jackson对对象和json做转换时一定需要此构造。需要jackson包。
    super();

}
public DemoObj(Long id,String name) {
    super();
    this.id = id;
    this.name = name;

}
public Long getId() {
    return id;
}
public void setId(Long id) {
    this.id = id;
}
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}

}

DemoAnnoController.java

@Controller
@RequestMapping("/anno")
public class DemoAnnoController {
//此方法未标注路径,因此使用类级别的路径/anno;produces可定制返回的response的媒体类型和字符集。
@RequestMapping(produces="text/plain;charset=UTF-8")
public @ResponseBody String index(HttpServletRequest request)
{
    return "url:"+request.getRequestURL()+" can access";
}
//接受路径参数,结合@PathVariable使用
@RequestMapping(value="/pathvar/{str}",produces="text/plain;charset=UTF-8")
public @ResponseBody String demoPathVar(@PathVariable String str , HttpServletRequest request)
{
    return "url:"+request.getRequestURL()+" can access,str "+str;
}
//演示常规的request参数获取
@RequestMapping(value="/requestParam",produces="text/plain;charset=UTF-8")
public @ResponseBody String passRequestParam(Long id , HttpServletRequest request)
{
    return "url:"+request.getRequestURL()+" can access,id "+id;
}

@RequestMapping(value="/obj",produces="application/json;charset=UTF-8")//演示参数到对象
@ResponseBody  //@ResponseBody支持将返回值放在response体内,而不是返回一个页面;此注解可放置在返回值前或者方法上。
public  String passObj(DemoObj obj , HttpServletRequest request)
{
    return "url:"+request.getRequestURL()+" can access,obj id "+obj.getId()+" obj name "+obj.getName();
}
//映射不同的路径到相同的方法
@RequestMapping(value={"/name1","/name2"},produces="text/plain;charset=UTF-8")
public @ResponseBody String remove(HttpServletRequest request)
{
    return "url:"+request.getRequestURL()+" can access";
}
}

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
@RestController演示
DemoController.java

@RestController //声明控制器,并且返回数据时不需要@ResponseBody。
@RequestMapping("/rest")
public class DemoController {
    @RequestMapping(value="/getjson",produces={"application/json;charset=UTF-8"})//返回数据的媒体类型为json.
    public DemoObj getjson(DemoObj obj)
    {
        return new DemoObj(obj.getId()+1,obj.getName()+"yy");//直接返回对象,对象会自动转换成json。
    }
}

这里写图片描述

Spring MVC基本配置

静态资源映射

程序的静态文件等需要直接访问,避免被拦截。
MyMvcConfig.java

@Override 
public void addResourceHandlers(ResourceHandlerRegistry registry)
{   //addResourceLocations指的是文件放置的目录,addResourceHandler指的是对外暴露的访问路径。
    registry.addResourceHandler("/image/**").addResourceLocations("classpath:/image/");
}

拦截器配置

拦截器实现对每一个请求处理前后进行相关的业务处理,类似于Servlet的Filter。
可让普通的Bean实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter类来实现自定义拦截器。
DemoInterceptor.java

public class DemoInterceptor extends HandlerInterceptorAdapter{

//重写preHandle方法,在请求发生前执行。
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handle)
{
    long startTime = System.currentTimeMillis();
    request.setAttribute("startTime", startTime);
    System.out.println("请求前");
    return true;
}

//重写postHandle方法,在请求发生后执行。
@Override
public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handle,ModelAndView modelAndView)
{
    long startTime = (Long) request.getAttribute("startTime");
    request.removeAttribute("startTime");
    long endTime = System.currentTimeMillis();
    System.out.println("请求后");
    System.out.println("本次请求处理时间为:"+new Long(endTime -startTime)+"ms");
    request.setAttribute("handlingTime", endTime-startTime);
}
}

MyMvcConfig.java

//拦截器的Bean.
@Bean
public DemoInterceptor demoInterceptor()
{
    return new DemoInterceptor();
}

//重写addInterceptors方法,注册拦截器。
@Override
public void addInterceptors(InterceptorRegistry registry)
{
    registry.addInterceptor(demoInterceptor());
}

这里写图片描述

@ControllerAdvice

ExceptionHandlerAdvice.java

@ControllerAdvice
public class ExceptionHandlerAdvice {
@ExceptionHandler(value=Exception.class)//@ExceptionHandler用于全局处理控制器里的异常。
public ModelAndView exception(Exception exception,WebRequest request)
{
    ModelAndView modelAndView = new ModelAndView("error");
    modelAndView.addObject("errorMessage","出错啦");   
    return modelAndView;
}
@ModelAttribute//@ModelAttribute本来的作用是绑定键值对到Model里,此处是让全局的@RequestMapping都能获得在此处设置的键值对。
public void addAttributes(Model model)
{
    model.addAttribute("msg","额外信息");
}
@InitBinder
public void initBinder(WebDataBinder webDataBinder)
{   //用来自动绑定前台请求参数到Model中
    //@InitBinder方法不能有返回值,它必须为void
    //WebDataBinder是DataBinder的子类,用于完成由表单到JavaBean属性的绑定。
    webDataBinder.setDisallowedFields("id");
}
}

AdviceController.java

@Controller
public class AdviceController{
@RequestMapping("/advice")
public String getSomething(@ModelAttribute("msg") String msg,DemoObj obj)
{
    throw new IllegalArgumentException("非常抱歉,参数有误.来自@ModelAttribute"+msg);
}
}

error.jsp

<body> 
${ errorMessage } 
</body> 

其他配置

快捷的ViewController

@RequestMapping("/index")
public String hello()
{
    return "index";
}

此处没有任何业务处理,只是简单的转向,写了至少三行代码。实际开发中会涉及大量这样的页面转向,我们可以通过配置addViewControllers来简化配置:
MyMvcConfig.java

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

路径匹配参数配置

路径参数如果带”.”的话,”.”后面的值将被忽略。例如:
这里写图片描述
通过重写configurePathMatch方法可不忽略”.”后面的参数。
MyMvcConfig.java

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

这里写图片描述

自定义HttpMessageConverter

HttpMessageConverter是用来处理request和response里的数据的。
MyMessageConverter.java

public class MyMessageConverter extends AbstractHttpMessageConverter<DemoObj>{

    public  MyMessageConverter() {
        //新建一个我们自定义的媒体类型yd/yds。
        super(new MediaType("yd","yds",Charset.forName("UTF-8")));
    }


    //重写readInternal方法,处理请求的数据。处理由"-"隔开的数据,并转成DemoObj的对象
    @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]);
    }

    //表明本HttpMessageConverter只处理DemoObj这个类。
    @Override
    protected boolean supports(Class<?> clazz) {

        return DemoObj.class.isAssignableFrom(clazz);
    }


    //重写writeInternal,处理如何输出数据到response。此例中,我们在原样输出前面加上"hello"。
    @Override
    protected void writeInternal(DemoObj obj, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
      String out = "hello :"+obj.getId()+"-"+obj.getName();
      outputMessage.getBody().write(out.getBytes());

    }

}

addViewControllers中添加viewController映射页面访问演示页面。

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

配置自定义的HttpMessageConverter的Bean,在Spring MVC里注册HttpMessageConverter有两个方法:

  • configureMessageConverters:重载会覆盖掉Spring MVC默认注册的多个HttpMessageConverter。
  • extendMessageConverters:仅添加一个自定义的HttpMessageConverter,不覆盖默认注册的HttpMessageConverter。
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(converter());
}
@Bean
public MyMessageConverter converter()
{
    return new  MyMessageConverter();
}

ConverterController.java

@Controller
public class ConverterController{
@RequestMapping(value="/convert",produces={"yd/yds"})
/*@requestBody注解常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,
比如说:application/json或者是application/xml等。一般情况下来说常用其来处理application/json类型。
通过@requestBody可以将请求体中的JSON字符串绑定到相应的bean上,当然,也可以将其分别绑定到对应的字符串上。*/
public @ResponseBody DemoObj convert(@RequestBody DemoObj obj)//绑定到相应的bean上
{   
    return obj;
}
}

converter.jsp

<body> 
<div id="resp"></div>
<input type="button" onclick="rep();"value="请求" />
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
function rep()
{
$.ajax({
url:"convert",
data:"1-yangdong",
type:"POST",
contentType:"yd/yds",
success:function(data)
{
  $("#resp").html(data);
}
});
}
</script>
</body> 

这里写图片描述
这里写图片描述

服务器端推送技术

本节的服务器端推送方案都是基于:当客户端向服务器发送请求,服务端会抓住这个请求不放,等有数据更新的时候才返回给客户端,当客户端收到消息后,再向服务端发送请求,周而复始。这种方式的好处是减少了服务器的请求数量,大大减少了服务器的压力。
本节提供了基于SSE的服务器端推送和基于Servlet3.0+的异步方法特性,其中第一种方式需要新式浏览器的支持,第二种方式是跨浏览器的。
1:SSE
除了IE,其他基本都支持了。
这里写图片描述
SseController.java

@Controller
public class SseController {
@RequestMapping(value="/push",produces="text/event-stream;charset=UTF-8")//text/event-stream,这是服务器端SSE的支持。
public @ResponseBody String push(HttpServletResponse response,HttpServletRequest request) throws UnsupportedEncodingException
{   System.out.println("SseController");

    Random r = new Random();
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {

        e.printStackTrace();
    }
    return "data:Testing 1,2,3"+r.nextInt()+"\n\n";
}
}

sse.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html> 
<html> 
<head> 
<meta name="content-type" content="text/html; charset=UTF-8">
<title>Insert title here</title> 
</head> 
<body>
<div id="msgFromPush"></div>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
if(!!window.EventSource) 
{
  var source = new EventSource('push');
  s = '';
  source.addEventListener('message',function(e){
  s+=e.data+"<br />";
  $("#msgFromPush").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>
registry.addViewController("/sse").setViewName("/sse");

这里写图片描述
这里写图片描述
2.Servlet3.0+异步方法处理
开启异步支持:

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

PushService.java

@Service
public class PushService {
private DeferredResult<String> deferredResult;
public DeferredResult<String> getAsyncUpdate() {//在PushService里产生DeferredResult给控制器用,通过
    deferredResult = new DeferredResult<String>();//@Scheduled注解的方法定时更新DeferredResult。
    return deferredResult;
}
@Scheduled(fixedDelay = 5000)
public void refresh()
{
    if (deferredResult != null) {
        deferredResult.setResult(new Long(System.currentTimeMillis()).toString());
    }
}
}

AysncController.java

@Controller
public class AysncController{
@Autowired
private PushService pushService;
@RequestMapping("/defer")
@ResponseBody
public DeferredResult<String> deferredResult()//异步任务的实现是通过控制器从另一个线程返回一个DeferredResult。
{
    return pushService.getAsyncUpdate();
}
}

async.jsp

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
deferred();//页面打开就向后台发送请求
function deferred()
{
  $.get('defer',function(data){
  console.log(data);//在控制台输出服务端推送的数据
  deferred();//一次请求完成后再向后台发送请求。
  })
}
//使用的是jquery的Ajax请求,所以没有浏览器的兼容问题

</script>
@EnableScheduling   //开启计划任务的支持
registry.addViewController("/async").setViewName("/async");

这里写图片描述

参考书籍:Spring Boot 实战
以上只是学习所做的笔记, 以供日后参考。如有错误请指正,谢谢啦!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值