四、spring mvc

4.1 Spring MVC概述

几个基本概念:

  1. 三层架构: Presentation tier+Application tier+Data tier(展现层+应用层+数据访问层)。
  2. MVC:Model+View+Controller(数据模型+视图+控制器)。
  3. 实际上MVC只存在三层架构的展现层,M实际上是数据模型,是包含数据的对象。
  4. 在Spring MVC里,有一个专门的类叫Model,用来和V之间的数据交互、传值;V指的是视图页面,包含JSP、freeMarker、Velocity、Thymeleaf、Tile等;C当然就是控制器(Spring MVC的注解@Controller的类)。
  5. 三层架构是整个应用的架构,是由Spring框架负责管理的。一般项目结构中都有Service层、DAO层,这两个反馈在应用层和数据访问层

4.2 Spring MVC项目搭建

Spring MVC提供了一个DispatcherServlet来开发Web应用。在Servlet 2.5及以下的时候只要在web.xml下配置元素即可。但我们在本节将使用Servlet 3.0+无web.xml的配置方式,在Spring MVC里实现WebApplicationInitializer接口便可实现等同于web.xml的配置。

4.2.1 示例

1.构建Maven项目
2.日志配置
在src/main/resources目录下,新建logback.xml配置:

<?xml version="1.0" encoding="UTF-8"?> 
<configuration scan="true" scanPeriod="1 seconds"> 
    <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"> 
           <resetJUL>true</resetJUL> 
    </contextListener> 
  
    <jmxConfigurator/> 
    
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> 
        <encoder> 
            <pattern>logbak: %d{HH:mm:ss.SSS} %logger{36} - %msg%n</pattern> 
        </encoder> 
    </appender> 
    
    <logger name="org.springframework.web" level="DEBUG"/> <!-- 1 --> 
    
    <root level="info"> 
        <appender-ref ref="console"/> 
    </root> 
</configuration> 

①将org.springframework.web包下的类的日志级别设置为DEBUG,我们开发Spring MVC经常出现和参数类型相关的4XX错误,设置此项我们会看到更详细的错误信息。

3.演示界面
在src/main/resources下建立views目录,并新建index.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>Insert title here</title> 
</head> 
<body> 
    <pre> 
        Welcome to Spring MVC world 
    </pre> 
</body> 
</html> 

4.Spring MVC配置

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

这里我们配置了一个JSP的ViewResolver,用来映射路径和实际页面的位置。
其中,@EnableWebMvc注解会开启一些默认配置,如一些ViewResolver或者MessageConverter等。
Spring MVC的ViewResolver,这是Spring MVC视图(JSP下就是html)渲染的核心机制;Spring MVC里有一个接口叫做ViewResolver(我们的ViewResolver都实现该接口)
前缀配置为/WEB-INF/classes/views/有些奇怪,怎么和我开发的目录不一致?因为看到的页面效果是运行时而不是开发时的代码,运行时代码会将我们的页面自动编译到/WEB-INF/classes/views/下

5.Web配置

package com.wisely.highlight_springmvc4; 

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); 
    } 
} 

①WebApplicationInitializer是Spring提供用来配置Servlet 3.0+配置的接口,从而实现了替代web.xml的位置。实现此接口将会自动被SpringServletContainerInitializer(用来启动Servlet 3.0容器)获取到。
②新建WebApplicationContext,注册配置类,并将其和当前servletContext关联。
③注册Spring MVC的DispatcherServlet。

6.简单控制器

package com.wisely.highlight_springmvc4.web; 

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

①利用@Controller注解声明是一个控制器。
②利用@RequestMapping配置URL和方法之间的映射。
③通过上面ViewResolver的Bean配置,返回值为index,说明我们的页面放置的路径为/WEB-INF/classes/views/index.jsp。

7.运行
启动Tomcat之后,访问:http://localhost:8080/highlight_springmvc4/index

4.3 spring MVC常用注解

(1)@Controller
@Controller注解在类上,表明这个类是Spring MVC里的Controller,将其声明为Spring的一个Bean,Dispatcher Servlet会自动扫描注解了此注解的类,并将Web请求映射到注解了@RequestMapping的方法上。
这里特别指出,在声明普通Bean的时候,使用@Component、@Service、@Repository和@Controller是等同的,因为@Service、@Repository、@Controller都组合了@Compoment元注解;但在Spring MVC声明控制器Bean的时候,只能使用@Controller。

(2)@RequestMapping
@RequestMapping注解是用来映射Web请求(访问路径和参数)、处理类和方法的。
@RequestMapping可注解在类或方法上。注解在方法上的@RequestMapping路径会继承注解在类上的路径,@RequestMapping支持Servlet的request和response作为参数,也支持对request和response的媒体类型进行配置。

(3)@ResponseBody
@ResponseBody支持将返回值放在response体内,而不是返回一个页面。我们在很多基于Ajax的程序的时候,可以以此注解返回数据而不是页面;此注解可放置在返回值前或者方法上。

(4)@RequestBody
@RequestBody允许request的参数在request体中,而不是在直接链接在地址后面。此注解放置在参数前。

(5)@PathVariable
@PathVariable用来接收路径参数,如/news/001,可接收001作为参数,此注解放置在参数前。

(6)@RestController
@RestController是一个组合注解,组合了@Controller和@ResponseBody,这就意味着当你只开发一个和页面交互数据的控制的时候,需要使用此注解。若没有此注解,要想实现上述功能,则需自己在代码中加@Controller和@ResponseBody两个注解。

4.3.2 示例

1.传值类
添加jackson及相关依赖,获得对象和json或xml之间的转换:

<!--对json和xml格式的支持 --> 
<dependency> 
    <groupId>com.fasterxml.jackson.dataformat</groupId> 
    <artifactId>jackson-dataformat-xml</artifactId> 
    <version>2.5.3</version> 
</dependency> 

此类用来演示获取request对象参数和返回此对象到response:

package com.wisely.highlight_springmvc4.domain; 
  
public class DemoObj { 
    private Long id; 
    private String name; 
    
    public DemoObj() { // 1 
        super(); 
    } 
    public DemoObj(Long id, String name) { 
        super(); 
        this.id = id; 
        this.name = name; 
    } 
     get set 省略....
}

①jackson对对象和json做转换时一定需要此空构造。

2.注解演示控制器

package com.wisely.highlight_springmvc4.web.ch4_3;      

@Controller // 1 
@RequestMapping("/anno") //2 
public class DemoAnnoController { 
  
    @RequestMapping(produces = "text/plain;charset=UTF-8")  // 3 
    public @ResponseBody String index(HttpServletRequest request) { // 4 
        return "url:" + request.getRequestURL() + " can access"; 
    } 
  
    @RequestMapping(value = "/pathvar/{str}", produces = "text/plain;charset=UTF-8")// 5 
    public @ResponseBody String demoPathVar(@PathVariable String str, 
            HttpServletRequest request) { 
        return "url:" + request.getRequestURL() + " can access,str: " + str; 
    } 
  
    @RequestMapping(value = "/requestParam", produces = "text/plain;charset=UTF-8") //6 
    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")//7 
    @ResponseBody //8 
    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")//9 
    public @ResponseBody String remove(HttpServletRequest request) { 
        
        return "url:" + request.getRequestURL() + " can access"; 
    } 
} 

①@Controller注解声明此类是一个控制器。
②@RequestMapping(“/anno”)映射此类的访问路径是/anno。
③此方法未标注路径,因此使用类级别的路径/anno;produces可定制返回的response的媒体类型和字符集,或需返回值是json对象,则设置produces=“application/json;charset=UTF-8”,在后面的章节我们会演示此项特性。
④演示可接受HttpServletRequest作为参数,当然也可以接受HttpServletReponse作为参数。此处的@ReponseBody用在返回值前面。
⑤演示接受路径参数,并在方法参数前结合@PathVariable使用,访问路径为/anno/pathvar/xx。
⑥演示常规的request参数获取,访问路径为/anno/requestParam?id=1。
⑦演示解释参数到对象,访问路径为/anno/obj?id=1&name=xx。
⑧@ReponseBody也可以用在方法上。
⑨演示映射不同的路径到相同的方法,访问路径为/anno/name1或/anno/name2。

3.@RestController演示

package com.wisely.highlight_springmvc4.web.ch4_3; 
  
@RestController //1 
@RequestMapping("/rest") 
public class DemoRestController { 
    
    @RequestMapping(value = "/getjson",produces={"application/json;charset=UTF-8"}) //2 
    public DemoObj getjson (DemoObj obj){ 
        return new DemoObj(obj.getId()+1, obj.getName()+"yy");//3 
    } 
    @RequestMapping(value = "/getxml",produces={"application/xml;charset=UTF-8"})//4 
    public DemoObj getxml(DemoObj obj){ 
        return new DemoObj(obj.getId()+1, obj.getName()+"yy");//5 
    } 
} 

①使用@RestController,声明是控制器,并且返回数据时不需要@ResponseBody。
②返回数据的媒体类型为json。
③直接返回对象,对象会自动转换成json。
④返回数据的媒体类型为xml。
⑤直接返回对象,对象会自动转换为xml。

4.4 Spring MVC基本配置

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

4.1.1 静态资源映射

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

示例
(1)添加静态资源
在src/main/resources下建立assets/js目录,并复制一个jquery.js放置在此目录下。
(2)配置代码

package com.wisely.highlight_springmvc4; 

@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) { 
        registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");//3 
    } 
} 

①@EnableWebMvc开启SpringMVC支持,若无此句,重写WebMvcConfigurerAdapter方法无效。
②继承WebMvcConfigurerAdapter类,重写其方法可对Spring MVC进行配置。
③addResourceLocations指的是文件放置的目录,addResourceHandler指的是对外暴露的访问路径。

4.4.2 拦截器配置

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

示例
(1)示例拦截器

package com.wisely.highlight_springmvc4.interceptor; 
@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

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

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

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

(1)定制ControllerAdvice。

package com.wisely.highlight_springmvc4.advice; 
@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,更多关于WebDataBinder的配置,请参考WebDataBinder的API文档。

(2)演示控制器

package com.wisely.highlight_springmvc4.web.ch4_4; 

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

4.5 Spring MVC的高级配置

4.5.1 文件上传配置

Spring MVC通过配置一个MultipartResolver来上传文件。
在Spring的控制器中,通过MultipartFile file来接收文件,通过MultipartFile[]files接收多个文件上传。

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

<!-- file upload --> 
<dependency> 
    <groupId>commons-fileupload</groupId> 
    <artifactId>commons-fileupload</artifactId> 
    <version>1.3.1</version> 
</dependency> 
<!-- 非必需,可简化I/O操作 --> 
<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("/index").setViewName("/index"); 
    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; 

@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快速写文件到磁盘。

原文地址:https://www.cnblogs.com/phtjzzj/p/7590717.html