Bootstrap

Spring mvc 组件介绍

1. 组件介绍

1.1 DispatcherServlet

统一收口,接收请求,响应结果,相当于转发器,中央处理器。
有了 DispatcherServlet 减少了其它组件之间的耦合度。

1.2 HandlerMapping-处理器映射器

根据请求的url查找Handler,既负责完成客户请求到 Controller 映射。
SpringMVC提供了不同的映射器实现不同的映射方式,
例如:配置文件方式、实现接口方式、注解方式等。

1.3 HandlerAdapter-处理器适配器

按照特定规则去执行Handler。

1.4 Handler-处理器 也叫 Controller

Handler 是继 DispatcherServlet 前端控制器的后端控制器,
在 DispatcherServlet 的控制下,Handler对具体的用户请求进行处理。

1.5 视图解析器ViewResolver

进行视图解析,根据逻辑视图名解析成真正的视图(view)

ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面的展示给用户。
SpringMVC框架提供了很多View视图类型,
包括:JSTLView、freemarkerView、pdfView等等.

1.6 视图View

View是一个接口,实现类支持不同的View类型(jsp、freemarker…),
一般情况下需要通过页面标签或者页面模板技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

总结: 需要我们开发的工作只有处理器 Handler 的编写以及视图比如JSP页面的编写。

2. 组件配置说明

2.1 配置前端控制器(DispatcherServlet)

在web project的web.xml中配置:

<!-- 配置前端控制器DispatcherServlet -->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--springmvc.xml 是自己创建的SpringMVC全局配置文件,用contextConfigLocation作为参数名来加载 
        如果不配置 contextConfigLocation,那么默认加载的是/WEB-INF/servlet名称-servlet.xml,在这里也就是 
        springmvc-servlet.xml 参数多个值使用逗号隔开,如:a.xml,b.xml -->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet><servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!--第一种配置:*.do,还可以写*.action等等,表示以.do结尾的或者以.action结尾的URL都由前端控制器DispatcherServlet来解析 
        第二种配置:/,所有访问的 URL 都由DispatcherServlet来解析,但是这里最好配置静态文件不由DispatcherServlet来解析,需要对静态资源单独处理 
        错误配置:/*,注意这里是不能这样配置的,因为如果这样写,最后转发到 jsp 页面的时候,仍然会由DispatcherServlet进行解析, 而这时候会找不到对应的Handler,从而报404!!! -->
    <url-pattern>/</url-pattern>
</servlet-mapping>

2.2 配置处理器映射器(HandlerMapping)

在 springmvc.xml 文件中配置,是请求的 URL 怎么能被 SpringMVC 识别,从而去执行我们编写好的 Handler。

(1) 第一种方法

<!-- 配置Handler 通过name的值为url来请求-->   
<bean name="/hello2" class="com.beiyou.controller.HelloController2" />

//Handler
//我们的这个controller 就是一个Handler(执行器)
public class HelloWord2 implements Controller {
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv=new ModelAndView();
        //如果xml统一配置路径,可以简写程文件名
        mv.setViewName("success");
        mv.addObject("msg","今天天气很冷");
        return mv;
    }
}

这样配置的话,那么请求的 URL,必须为 http://ip:port/hello2

(2) 第二种方法

<!-- 配置Handler  通过key值做为url来访问对应的bean-->   
<bean id="hello2" class="com.beiyou.controller.HelloController2" />
<bean id="hello3" class="com.beiyou.controller.HelloController3" />
<!-- 简单URL配置处理器映射器 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/hello2">hello2</prop>
            <prop key="/hello3">hello3</prop>
        </props>
    </property>
</bean>

这种配置请求的 URL可以为 http://ip:prot/项目名/hello,或者http://ip:port/项目名/hello2

2.3 配置处理器适配器(HandlerAdapter)

在 springmvc.xml 文件中配置,用来约束我们所需要编码的 Handler类。

第一种

编写 Handler 时必须要实现 Controller,否则不能被适配器解析。

<!-- 配置处理器适配器,所有适配器都得实现 HandlerAdapter接口 处理器必须实现controller接口的handler-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

第二种

编写 Handler 时必须要实现 HttpRequestHandler

<!-- 配置处理器适配器第二种方法,这样配置所有Handler都必须实现 HttpRequestHandler接口-->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />

👋

1、如果没有指明,则可以同时处理实现Controller接口和HttpRequestHandler接口的handler。

2、如果指明其中一种,则要求所有的hander类都必须实现对应的接口,否则无法访问。

2.4 处理器(Handler,即我们的Controller)

在 springmvc.xml 文件中配置.,就是请求的 URL
到我们这里所编写的 Handler 类的某个方法进行一些业务逻辑处理。
两个处理器适配器来约束 Handler,通过上面两种配置分别编写两个 Handler:

(1) 第一种

实现Controller 接口

//Handler
//我们的这个controller 就是一个Handler(执行器)
public class HelloWord implements Controller {
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv=new ModelAndView();
        //如果xml统一配置路径,可以简写程文件名
        mv.setViewName("success");
        mv.addObject("msg","今天天气很冷");
        return mv;
    }
}

(2) 第二种 HttpRequestHandler

public class HelloController2 implements HttpRequestHandler {
    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setAttribute("msg", "测试数据");
     request.getRequestDispatcher("/WEB-INF/success.jsp").forward(request, response);
    }
}

⏳ 通常我们使用第一种方式来编写 Handler ,但是第二种如果不需要渲染视图,我们可以通过 response 修改相应内容,比如返回 json 数据:如下图:

response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json字符串");
//controller示例
public class HelloWord2 implements HttpRequestHandler {
    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
​
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write("json字符串");
    }
}

2.5 配置视图解析器(ViewResolver)

1.简单配置:

<!-- 配置视图解析器 进行jsp解析,默认使用jstl标签,classpath下得有jstl的包-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" />
<!--如果这样配,那么在 Handler 中返回的必须是完整路径(含完整文件名)。-->

2.完整配置:

<!--配置视图解析器  -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 返回视图页面的前缀 -->
    <property name="prefix" value="/WEB-INF/jsp/"></property>
    <!-- 返回页面的后缀 -->
    <property name="suffix" value=".jsp"></property>
</bean>

如果这样配,那么在 Handler 中只需要返回在 pages 文件夹下的jsp 页面名称就可以了。

3. 基本配置

3.1 常规配置(Controller加载控制)

  • SpringMVC的处理器对应的bean必须按照规范格式开发,未避免加入无效的bean可通过bean加载过滤器进行包含设定或排除设定,表现层bean标注通常设定为@Controller

xml方式

<context:component-scan base-package="com.by">
        <!--指定包扫描的包含controller-->
    <context:include-filter
                            type="annotation"
                            expression="org.springframework.stereotype.Controller"/>
    <!--开启注解扫描-->
</context:component-scan>

context:include-filter有两个必须的属性:

type: 配置filter的类型,这个类型一共有以下五个值:

assignable — 指定扫描某个接口派生出来的类
annotation — 指定扫描使用某个注解的类
aspectj — 指定扫描AspectJ表达式相匹配的类
custom — 指定扫描自定义的实现了
expression: 根据type的不同,这个表达式的配置方式也不同。

1、assignable 的用法

创建接口类:

//标签
public interface Label {
}

Seller类: 注意,我们在Seller类上方添加了标准注解@Component,所以Seller类会被spring加载

//供应商
@Component
public class Seller {
}

ManuFacture类:ManuFacture类没有标注注解,理论上不会被Spring加载。但是ManuFacture类实现了接口Lable

//厂商
public class ManuFacture implements Label{
}

spring-mvc.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"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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
        http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd"><context:component-scan base-package="com.by.pojo">
        <!-- 通知spring将实现接口Label的类也加载进来 -->
        <context:include-filter type="assignable"
                                expression="com.by.pojo.Label"/>
    </context:component-scan>
    <!--开启注解扫描-->
    <mvc:annotation-driven/>
</beans>

测试类:

public class AppTest {public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("spring-mvc.xml");
        System.out.println(ac.getBean(Seller.class));
        System.out.println(ac.getBean(ManuFacture.class));
    }
}

加入servlet-api: 使用完删除

 <dependency>
      <groupId>tomcat</groupId>
      <artifactId>servlet-api</artifactId>
      <version>5.5.23</version>
    </dependency>

2、annotation — 指定扫描使用某个注解的类

创建一个注解接口类: 类似@Controller

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DemoAnno {
   
}

ManuFacture加上我们配置的注解

@DemoAnno
public class ManuFacture {
}

spring-mvc.xml配置:

   <!-- 通知spring将有指定注解的类加载进来 -->
<context:include-filter type="annotation" expression="com.by.pojo.DemoAnno"/>

测试类不变:直接运行

**3、aspectj-**指定扫描AspectJ表达式相匹配的类:比如要求加载某个类的派生类

这里需要添加AspectJ的依赖:

 <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.3.20</version>
    </dependency>

我们添加一个新的CommBar类:

public class CommBar {
}

继续修改我们的ManuFacture类,不实现接口,不需要注解,只要继承CommBar类即可。

public class ManuFacture extends CommBar{
}

mvc.xml配置

<!-- 通知spring所有继承和扩展自指定类的类全部加载进来 -->
 <context:include-filter type="aspectj" expression="com.by.pojo.CommBar+"/>

直接测试运行

**4、custom—**指定扫描自定义的实现了org.springframework.core.type.filter.TypeFilter接口的类

继续修改我们的ManuFacture类,不实现接口,不需要注解,不继承任何类。

public class ManuFacture {
}

我们添加一个MyFilter实现TypeFilter接口:

public class MyFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader,
                 MetadataReaderFactory metadataReaderFactory) throws IOException {
        //这里判断读取的类型是否是ManuFacture,如果是就返回true,否则返回false;
        // 返回true就会被spring加载,否则不加载
     if(metadataReader.getClassMetadata().getClassName().equals(ManuFacture.class.getName())){
          return true;
        }
        return false;
    }
}

mvc配置类:

 <!-- 通知spring根据我们配置的MyFilter类进行加载 这里的 expression要配置我们自己的MyFilter -->
  <context:include-filter type="custom" expression="com.by.pojo.MyFilter"/>

直接运行即可

回到之前的配置:

<context:component-scan base-package="com.by">
        <!--指定包扫描的包含controller-->
    <context:include-filter
                            type="annotation"
                            expression="org.springframework.stereotype.Controller"/>
    <!--开启注解扫描-->
</context:component-scan>

controller中编写:

@Controller
public class HelloWord2  {
    @RequestMapping("/hello2")
    public ModelAndView handleRequest() throws ServletException, IOException {
        ModelAndView mv=new ModelAndView();
        mv.setViewName("success");
        mv.addObject("msg","给不了的永远都是空的");
        return mv;
    }
}

使用 @EnableWebMvc 注解 代替配置

<mvc:annotation-driven> 和 @EnableWebMvc 解析
这两个起到的作用是类似的(避免重复注册,二选一即可), 都是注册了大部分spring mvc开发所需的bean(HandlerMapping,HandlerAdapter等等),
还有根据包存在添加messageConverter(例如jackson,以支持@ResponseBody).

配置类编写

@Configuration
//开启 Spring MVC注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    /*
    配置视图解析器
    */
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        WebMvcConfigurer.super.configureViewResolvers(registry);
        //注册到视图配置中
        registry.viewResolver(resourceViewResolver());
    }
    /**
     * 配置请求视图映射
     */
    private InternalResourceViewResolver resourceViewResolver(){
        InternalResourceViewResolver viewResolver=new InternalResourceViewResolver();
        //请求视图的前缀
        viewResolver.setPrefix("/WEB-INF/");
        //请求视图的后缀
        viewResolver.setSuffix(".jsp");
        return  viewResolver;
    }
}

controller编写:

@Controller
public class HelloWord3  {
    @RequestMapping("/hello3")
    public ModelAndView query() {
        ModelAndView mv=new ModelAndView();
        //如果xml统一配置路径,可以简写程文件名
        mv.setViewName("success");
        mv.addObject("msg","使用配置类操作");
        return mv;
    }
}

;