Spring Boot 2.0之Web MVC 核心

Spring Framework 时代的一般认识

实现 Controller

@Controller
public class HelloWorldController {

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

}

配置 Web MVC 组件

app-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.imooc.web"/>

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

部署 DispatcherServlet

web.xml

<web-app>
    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/app-context.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

使用可执行 Tomcat Maven 插件

 <build>
        <plugins>
            <!--tomcat7 插件-->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <execution>
                        <id>tomcat-run</id>
                        <goals>
                            <goal>exec-war-only</goal>
                        </goals>
                        <phase>package</phase>
                        <configuration>
                            <!-- ServletContext path -->
                            <path>/</path>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

pom.xml其他依赖

<packaging>war</packaging>
<dependencies>

        <!--Servlet 3.1 API-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>

        <!--spring web mvc依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
    </dependencies>

Spring Framework 时代的重新认识

Web MVC 核心组件

组件 Bean 类型说明
HandlerMapping映射请求(Request)到处理器(Handler)加上其关联的拦截器(HandlerInterceptor)列表,其映射关系基于不同的 HandlerMapping 实现的一些标准细节。其中两种主要 HandlerMapping 实现, RequestMappingHandlerMapping支持标注 @RequestMapping 的方法, SimpleUrlHandlerMapping 维护精确的URI路径与处理器的映射
HandlerAdapter帮助 DispatcherServlet 调用请求处理器(Handler),无需关注其中实际的调用细节。比如,调用注解实现的 Controller 需要解析其关联的注解. HandlerAdapter的主要目的是为了屏蔽与 DispatcherServlet 之间的诸多细节
HandlerExceptionResolver解析异常,可能策略是将异常处理映射到其他处理器(Handlers) 、或到某个 HTML错误页面,或者其他。
ViewResolver从处理器(Handler)返回字符类型的逻辑视图名称解析出实际的 View 对象,该对象将渲染后的内容输出到HTTP 响应中。
LocaleResolver,LocaleContextResolver从客户端解析出 Locale ,为其实现国际化视图。
MultipartResolver解析多部分请求(如 Web 浏览器文件上传)的抽象实现
交互流程

在这里插入图片描述

源代码分析

DispatcherServlet中,处理请求的是doDispatch
首先,根据请求路径从handlerMappings中拿到HandlerExecutionChainhandlerMappings是初始化容器时赋值,具体这里先不讲。

mappedHandler = getHandler(processedRequest);

然后通过handle找到合适的HandlerAdapter
在这里插入图片描述

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

处理后返回一个ModelAndView
在这里插入图片描述

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

处理请求结果

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

返回结果

render(mv, request, response);

根据viewName先找到View

view = resolveViewName(viewName, mv.getModelInternal(), locale, request);

由具体得View返回视图

view.render(mv.getModelInternal(), request, response);

Web MVC 注解驱动

版本依赖 : Spring Framework 3.1 +

基本配置步骤
  • 注解配置: @Configuration ( Spring 范式注解 )
  • 组件激活: @EnableWebMvc (Spring 模块装配)
  • 自定义组件: WebMvcConfigurer (Spring Bean)
示例重构

app-context.xml的配置注释起来,编程方式写一个配置类WebMvcConfig

@Configuration
@EnableWebMvc
public class WebMvcConfig {

//    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
//        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
//        <property name="prefix" value="/WEB-INF/jsp/"/>
//        <property name="suffix" value=".jsp"/>
//    </bean>-->


    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/jsp/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

断点,可以看到多了一个ViewResolverViewResolverComposite是通过@EnableWebMvc装配进来的
在这里插入图片描述

编程实现DispatcherServlet
  • 基础接口: WebApplicationInitializer
  • 编程驱动: AbstractDispatcherServletInitializer
  • 注解驱动: AbstractAnnotationConfigDispatcherServletInitializer

实现WebApplicationInitializer接口太复杂,AbstractAnnotationConfigDispatcherServletInitializer比较好实现
web.xml注释起来

public class DefaultAnnotationConfigDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {// 对应web.xml
        return new Class[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {// 对应DispatcherServlet
        return new Class[]{DispatcherServletConfiguration.class};
    }

    @Override
    protected String[] getServletMappings() {// 对应url-pattern
        return new String[]{"/"};
    }
}

DispatcherServlet配置实现类

@ComponentScan(basePackages = {"com.imooc.web"})
public class DispatcherServletConfiguration {
}
自定义组件 : WebMvcConfigurer (Spring Bean)

实现WebMvcConfigurer接口,里面有很多方法,这里只重写添加拦截器方法。

@Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HandlerInterceptor() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                System.out.println("handler="+handler);
                System.out.println("拦截中");
                return true;
            }
        });
    }

控制台打印

handler=public java.lang.String com.imooc.web.HelloWorldController.index()
拦截中
常用注解
  • 注册模型属性: @ModelAttribute
  • 读取请求头: @RequestHeader
  • 读取 Cookie: @CookieValue
  • 校验参数: @Valid 、 @Validated
  • 注解处理: @ExceptionHandler
  • 切面通知: @ControllerAdvice

HelloWorldControllerAdvice.java所有的注解处理抽离出一个类

// 切面通知的目标对象
@ControllerAdvice(assignableTypes = HelloWorldController.class)
public class HelloWorldControllerAdvice {

    @ModelAttribute("connection")
    public String getConnection(@RequestHeader("Connection") String connection){
        return connection;
    }

    @ModelAttribute("cookies")
    public String getMessage(@RequestHeader("Cookie") String cookie){
        return cookie;
    }

    @ModelAttribute("JSESSIONID")
    public String getJSESSIONID(@CookieValue("JSESSIONID") String JSESSIONID){
        return JSESSIONID;
    }

    @ModelAttribute("message")
    public String getMessage(){
        return "hello world.";
    }

    @ExceptionHandler(Throwable.class)
    public ResponseEntity<String> onException(Throwable throwable){
        return ResponseEntity.ok(throwable.getMessage());
    }
}

index.jsp

<div>${message}</div>
<div>${connection}</div>
<div>${cookies}</div>
<div>${JSESSIONID}</div>

页面展示http://localhost:8080/?value=1

hello world.
keep-alive
JSESSIONID=F178F3EC65995638A2F3DFF932BD2CD1
F178F3EC65995638A2F3DFF932BD2CD1

如果不带value=1,则报错,但是页面返回200,因为使用@ExceptionHandler(Throwable.class)做了处理

Required int parameter 'value' is not present

Spring Boot 时代的简化

重构示例

其他任何配置类都不需要,只需要一个配置类``SpringBootWebMvcBootstrap`

@SpringBootApplication(scanBasePackages = "com.imooc.web")
public class SpringBootWebMvcBootstrap {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootWebMvcBootstrap.class, args);
    }
}

通过配置文件设置视图application.properties

spring.mvc.view.prefix = /WEB-INF/jsp/
spring.mvc.view.suffix = .jsp

完全自动装配

  • 自动装配 DispatcherServlet : DispatcherServletAutoConfiguration
  • 替换 @EnableWebMvc : WebMvcAutoConfiguration
  • Servlet 容器 : ServletWebServerFactoryAutoConfiguration
理解自动配置顺序性

绝对顺序: @AutoConfigureOrder
相对顺序: @AutoConfigureAfter

装配条件

Web 类型判断( ConditionalOnWebApplication )

API 判断( @ConditionalOnClass )

Bean 判断( @ConditionalOnMissingBean 、 @ConditionalOnBean )

pom配置

<packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>

        <!-- Provided -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

打包测试

cmd:$ mvn -Dmaven.test.skip -U clean package
在这里插入图片描述
启动:java -jar target/springboot-webmvc-0.0.1-SNAPSHOT.war

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值