美文网首页
第4章 Spring MVC 4.x

第4章 Spring MVC 4.x

作者: 意大利大炮 | 来源:发表于2018-12-06 21:40 被阅读0次

4.1 Spring MVC概述

  • Spring MVC是Spring中的一个用于web开发的模块
  • MVC:Model + View + Controller(数据模型 + 视图 + 控制器)
  • 三层架构:Presentation tier + Application tier + Data tier (展现层 + 应用层 + 数据访问层)
  • MVC与三层架构的关系:MVC只存在于三层架构的展现层
    1. M实际上是数据模型,是包含数据的对象,在Spring MVC例有一个专门的类叫Model,用来和V之间的数据交互。传值
    2. V指的是视图页面,包含JSP、freeMarker、Velocity、Thymeleaf、Tile等。
    3. C就是控制器了(Spring MVC中注解@Controller的类)

4.2 Spring MVC项目快速搭建

4.2.1 构建maven项目

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.spring</groupId>
    <artifactId>springmvcDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <jsp.verion>2.2</jsp.verion>
        <jstl.version>1.2</jstl.version>
        <servlet.version>3.1.0</servlet.version>

        <!-- spring -->
        <spring-framework.version>4.1.5.RELEASE</spring-framework.version>

        <!-- logging -->
        <logback.version>1.0.13</logback.version>
        <slf4j.version>1.7.5</slf4j.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>


        <!-- 其他web依赖 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${jstl.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>${jsp.verion}</version>
            <scope>provided</scope>
        </dependency>

        <!-- spring and Transactions -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>

        <!-- 使用slf4J 和 LogBack作为日志 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-access</artifactId>
            <version>${logback.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
4.2.2 日志配置
<?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>logback: %d{HH:mm:ss.SSS} %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <logger name="org.springframework.web" level="DEBUG"/>
    <root level="info">
        <appender-ref ref="console"/>
    </root>
</configuration>
4.2.3 演示页面
  • 在src/main.resource建立views目录,并建立index.jsp(resource下防止页面是spring boot的放置方式)
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<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.2.4 SpringMVC配置
  • 使用配置类配置一个JSP的ViewResolver,用来映射路径和实际页面的位置
  • @EnableWebMvc注解会开启一些默认配置,如一些ViewResolve或者MessageConverter等等
@Configuration
@EnableWebMvc
@ComponentScan("com.springmvcDemo")
public class MyMvcConfig {
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return viewResolver;
    }
}
4.2.5 Web配置
  • 创建一个集成了WebApplicationInitializer接口的类,实现此接口可以自动的呗SpringServletContainerInitializer(用来启动Servlet3.0的容器)获取到
public class WebInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        // 注册
        ctx.register(MyMvcConfig.class);
        // 关联
        ctx.setServletContext(servletContext);

        ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
        servlet.addMapping("/");
        servlet.setLoadOnStartup(1);
    }
}
4.2.6 简单控制器
@Controller
public class HelloController {

    @RequestMapping("/index")
    public String hello() {
        return "index";
    }
}
4.2.7 运行
  • 程序部署到Tomcat,启动tomcat后,访问控制器的地址
  • 不知道为什么这里没能启动成功,需要再多次测试

4.3 Spring MVC 的常用注解

4.3.1 常用注解
  • @Controller
  • @RequestMappering
  • @ResponseBody: 防止返回结果被解析为页面
  • @RequestBody
  • @PathVariable
  • @RestController
4.3.2 使用示例
  • 添加jackson以及相关依赖,获得对象和json或xml之间的转换
  • 正常开发中,很少用到xml,一般使用json,因为json比xml简洁,但这里演示同时支持json和xml的maven与只支持json的maven
    同时支持xml和json的依赖
<dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.5.3</version>
        </dependency>

只纯支持json的依赖

  <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.5.3</version>
        </dependency>

创建一个对象,用于演示获取request对象参数和返回此对象到response
jackson对对象和json做转换时,一定需要一个空的构造方法???

public class DemoObj {
    private Long id;
    private String name;
    public DemoObj() {
        super();
    }
    public DemoObj(Long id, String name) {
        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;
    }
}

创建演示控制器

@Controller   // 表明是一个控制器
@RequestMapping("/anno")  // 映射此类的访问路径
public class DemoAnnoContraoller {

    @RequestMapping(produces = "text/plain;charset=UTF-8")   // 未设置路径,所以使用类级别的路径;produces可指定返回的response的媒体类型和字符集
    public @ResponseBody String index(HttpServletRequest request) {  // 演示可接收HttpServletRequest作为参数
        return "url:" + request.getRequestURL() + "can access";
    }

    @RequestMapping(value = "/pathvar/{str}", produces = "text/plain;charset=UTF-8")  // 演示可接收路径参数,并在方法参数前结合@PathVariable使用
    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")  // 演示常规的request参数获取,访问路径为/anno/requestParam?id=1
    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") // 演示直接解释参数到对象,访问路径为/anno/obj?id=1&name=xx
    @ResponseBody       // 演示@ResponseBody注解可以用到方法上
    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")  // 演示映射不同的路径到相同的方法上,访问路径为/anno/name1或/anno/name2
    public @ResponseBody String remove(HttpServletRequest request) {
        return "url:" + request.getRequestURL() + "can access";
    }
}

演示rest方式的控制器

@RestController
@RequestMapping("/rest")
public class DemoRestController {

    // 演示返回对象自动解析为json
    @RequestMapping(value = "/getJson", produces = "application/json;charset=UTF-8")
    public DemoObj getJson(DemoObj obj) {
        return new DemoObj(obj.getId() + 1, obj.getName() + "yy");
    }

    // 演示返回对象为xml
    @RequestMapping(value = "/getJson", produces = "application/xml;charset=UTF-8")
    public DemoObj getXml(DemoObj obj) {
        return new DemoObj(obj.getId() + 1, obj.getName() + "yy");
    }
}

4.4 Spring MVC的基本配置

  • Spring MVC的定制配置需要我们的配置类继承一个WebMvcConfigurerAdapter类,并在此类使用@EnableWebMvc注解,来开启对Spring MVC的配置支持,这样就可以重写这个类的方法,完成常用的配置
  • WebMvcConfigurerAdapter类是WebMvcConfigurer接口的实现,所以WebMvcConfigurer的API内的方法也可以用来配置MVC。下面展示一下这两个类的源码:
    WebMvcConfigurerAdapter:
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
    public WebMvcConfigurerAdapter() {
    }
    public void addFormatters(FormatterRegistry registry) {
    }
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    }
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    }
    public Validator getValidator() {
        return null;
    }
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    }
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    }
    public void configurePathMatch(PathMatchConfigurer configurer) {
    }
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    }
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
    }
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    }
    public MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
    public void addInterceptors(InterceptorRegistry registry) {
    }
    public void addViewControllers(ViewControllerRegistry registry) {
    }
    public void configureViewResolvers(ViewResolverRegistry registry) {
    }
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    }
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    }
}

WebMvcConfigurer:


public interface WebMvcConfigurer {
    void addFormatters(FormatterRegistry var1);

    void configureMessageConverters(List<HttpMessageConverter<?>> var1);

    void extendMessageConverters(List<HttpMessageConverter<?>> var1);

    Validator getValidator();

    void configureContentNegotiation(ContentNegotiationConfigurer var1);

    void configureAsyncSupport(AsyncSupportConfigurer var1);

    void configurePathMatch(PathMatchConfigurer var1);

    void addArgumentResolvers(List<HandlerMethodArgumentResolver> var1);

    void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> var1);

    void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> var1);
    // 注册拦截器
    void addInterceptors(InterceptorRegistry var1);

    MessageCodesResolver getMessageCodesResolver();
    // 无任何事务处理,只是简单页面转向的简化配置
    void addViewControllers(ViewControllerRegistry var1);

    void configureViewResolvers(ViewResolverRegistry var1);
    // 增加静态文件映射
    void addResourceHandlers(ResourceHandlerRegistry var1);

    void configureDefaultServletHandling(DefaultServletHandlerConfigurer var1);
}
4.4.1 静态资源映射
  • 程序的静态文件(js、css、图片)等需要直接访问,这时我们可以重写addResourceHandlers来实现

/**
 * 测试,静态资源映射
 */
@Configuration
@EnableWebMvc
@ComponentScan("com.springmvcDemo.resource_handlers")
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    
    // 类似于xml的<mvc: resource />的静态资源映射配置
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 指定目录下的本地文件映射为一个url,使得可以直接访问
        registry.addResourceHandler("/views/**").addResourceLocations("classpath:/views/");
    }
}
4.4.2 拦截器配置
  1. 让Bean实现HanlderInterceptor接口或者继承HandlerInterceptorAdapter类来实现拦截器
  2. 通过重写WebMvcConfigurerAdapter 的addInterceptors方法来注册自定义的拦截器
    实现一个拦截器:
public class DemoInterceptor implements HandlerInterceptor {
    // 请求前调用
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        httpServletRequest.setAttribute("startTime", System.currentTimeMillis());
        return true;
    }
    // 请求后调用
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("本次请求处理时间为:" + (System.currentTimeMillis() - (long) httpServletRequest.getAttribute("startTime")));
    }
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    }
}

注册拦截器到配置Bean:

    @Bean
    public DemoInterceptor demoInterceptor() {
        return new DemoInterceptor();
    }
    // 注册拦截器
    @Autowired 
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(demoInterceptor());
    }
4.4.3 @ControllerAdvice(建言)
  • 通过@ControllerAdvice注解,可以将控制器的全局配置放到一个位置,注解了@Controller的类的方法可以使用@ExeptionHandler、@InitBinder、@ModelAttribute注解到方法上,这对所有注解了@RequestMapping的控制器内方法有效
  • @ExceptionHandler:用于全局处理控制器内的异常
  • @InitBinder:用啦设置WebDataBinder,用来自动绑定一个前台请求参数到Model中
  • @ModelAttribute:绑定键值到Model内,让全聚德@RequestMapping可以获得在此处设置的键值对
  • 创建建言类
@ControllerAdvice //生命一个控制器建言,此注解组合了@Component注解
public class DemoHandlerAdvice {
    // 定义全局异常处理,拦截所有的Exception
    @ExceptionHandler(value = Exception.class)
    public ModelAndView exception(Exception exception, WebRequest request) {
        ModelAndView modelAndView = new ModelAndView("error");
        modelAndView.addObject("errorMsg", exception.getMessage());
        return modelAndView;
    }
    // 将键值对添加到全局,所有注解@RequestMappering的方法都可以获得此键值对
    @ModelAttribute
    public void addAttributes(Model model) {
        model.addAttribute("msg", "额外信息");
    }
    // 定制WebDataBinder
    @InitBinder
    public void initBinder(WebDataBinder webDataBinder) {
        webDataBinder.setDisallowedFields("id"); // 这里过滤掉了Id,即后台无法获取到该字段
    }
}

创建演示的控制器

@Controller
public class AdviceController {
    @RequestMapping("/advice")
    public String getSomething(@ModelAttribute("msg") String msg, DemoObj demoObj) { // 这里接收msg和demoObj,但demoObj中的id无法获取到了
        throw new IllegalArgumentException("非常抱歉,参数有误");
    }
}
4.4.4 其他配置
  1. ViewController
    正常的页面转向代码:
// 演示简单页面转向
    @RequestMapping("/index")
    public String hello() {
        return "index";
    }

通过重写addViewController方法来简化:

    @Autowired
    // 配置页面转向
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("index").setViewName("index");
    }

4.5 Spring MVC的高级配置

4.5.1 文件上传配置
4.5.2 自定义HttpMessageConverter

相关文章

网友评论

      本文标题:第4章 Spring MVC 4.x

      本文链接:https://www.haomeiwen.com/subject/uscbcqtx.html