美文网首页
第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