SpringMvc使用

一、简介

  1、在SpringMVC的各个组件中,前端控制器、处理器、视图称为SpringMVC的组件

  2、在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件

  3、需要开发的组件有:处理器、视图

  4、SpringMVC六大组件

组件 组件 描述
DispatcherServlet 前端控制器 用户请求到达前端控制器,它相当于MVC模式中的C,dispatcherServlet是整个流程的控制中心,由他调用其他组件来处理用户的请求,dispatcherServlet降低了组件之间的耦合性
Handler 处理器

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

2、由于handler涉及到具体的业务请求,所以一般情况下需要开发人员根据业务需求开发handler

View 视图

1、SpringMvc框架提供了很多View视图类型的支持,包括jstlView、freemarkerView、pdfView等,我们最常用的视图就是jsp

2、一般情况下需要通过页面标签或页面模板技术将模板数据通过页面展示给用户,需要有开发人员根据业务需求开发具体的页面

HandlerMapping 处理器映射器 HandlerMapping负责根据用户请求找到Handler(处理器),SpringMvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式、接口实现方式、注解方式等
HandlAdapter 处理器适配器 通过HandlerAdaoter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行
View Resolver 视图解析器 View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名(即页面地址),再生成View视图对象,最后对View 进行渲染,将处理结果通过页面展示给用户

 

二、项目搭建

(一)搭建入门程序

  1、pom依赖

  在pom依赖中,添加springmvc包、jstl包和servlet-api包

<?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.lcl.galaxy</groupId>
    <artifactId>spring-mvc-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>


    <dependencies> <!-- spring MVC依赖包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency> <!-- jstl -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency> <!-- servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins> <!-- 配置Maven的JDK编译级别 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/</path>
                    <port>8080</port>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

  2、在main目录下创建webapp/WEB-INF文件夹,并创建web.xml文件

  在web.xml文件中,创建一个名为springmvc的前端控制器(DispatcherServlet)拦截器,同时设置拦截器的拦截路径为 “/” ,设置加载配置文件地址为classpath:springmvc

  说明:

    (1)使用param-name为contextConfigLocation可以配置Spring配置文件路径,如果不做配置,DispatcherServlet默认会读取默认文件(WEB-INF/springmvc-servlet.xml)

    (2)指定load-on-startup为2,表示tomcat启动时,DispatcherServlet会跟随着初始化;如果不指定初始化时机,DispatcherServelt会在第一次被请求时初始化,且只会被初始化一次。

      (3)对于拦截规则,不能配置成/*,否则会报错,因为/*会拦截整个项目中的资源,包括jsp和静态资源,但是springmvc对于静态资源提供了默认的处理器来处理,但是没有默认的jsp处理器,我们也没有写jsp处理器,因此就短路了

      (4)web.xml中存在servlet、filter、listener、context-param,他们的加载顺序为context-param > listener > filter > servlet,当容器(tomcat)启动时,会先找到context-param配置的值,将其初始化到web项目的上下文中,然后会加载listener监听web项目上下文内容,做相应的处理;然后调用filter来实例化过滤器,最后在服务被调用时,初始化servlet

      (5)关于load-on-startup,为servlet容器的启动顺序,值必须为整数,否则将不会加载servlet;如果该值小于等于0,则该servlet会在容器被调用时加载,如果大于0,则说明在tomcat启动时就会加载该servlet容器,数值越小,优先级越高,加载顺序越靠前。

      (6)关于url-pattern,有四种配置规则,精确匹配(url必须完全匹配,/aa/*/bb 为精确匹配)、扩展名匹配(/aaa/*.jsp)、路径匹配(以“/*”开头)、缺省匹配(以“/”开头),匹配顺序为 精确匹配 > 路径匹配 > 扩展名匹配 > 缺省匹配

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

  3、配置springmvc.xml文件

  这里主要配置自动扫描、处理器适配器&处理器映射器、视图访问地址、文件后缀

  其中mvc:annotation-driven内置了RequestMappingHandlerMapping、RequestMappingHandlerAdapter等组件

<?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.lcl.galaxy.springmvc"/>
    <mvc:annotation-driven/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

  4、编码

  使用@ResponseBody注解返回具体的Json串,不使用的情况,默认走视图(例如返回hello,根据springmvc.xml中配置,会展示/WEB-INF/jsp/hello.jsp中的内容)

package com.lcl.galaxy.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {
    @RequestMapping("/view")
    public String getStrView(){
        return "hello";
    }

    @RequestMapping("/data")
    @ResponseBody
    public String getStr(){
        return "hello";
    }
}

(二)SSM项目搭建

  SSM项目搭建使用三层结构的方式搭建

  (1)整合持久层mapper、数据源、SqlSessionFactory及mapper代理对象的整合(applicationContext-dao.xml)

  (2)整合业务层Service、包括事务Bean及service的Bean配置(applicationContext-service.xml)

  (3)整合表现层Controller,直接使用springmvc.xml配置文件(springmvc.xml)

  (4)web.xml加载spring容器(web.xml)

  接下来就一步步整合

  1、整合持久层mapper、数据源、SqlSessionFactory及mapper代理对象的整合(applicationContext-dao.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-4.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.2.xsd">

    <context:property-placeholder location="classpath:db123.properties"/>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.lcl.galaxy.springmvc.mapper"/>
    </bean>

</beans>

  2、整合业务层Service、包括事务Bean及service的Bean配置(applicationContext-service.xml)

  这里需要说明一下,切面一定不要写execution(* *..*.*(..)),这样会导致切到非业务代码,最终引发dataSource的循环依赖。

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.lcl.galaxy.springmvc.service"/>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="add*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lcl.galaxy.springmvc..*.*(..))"/>
    </aop:config>

</beans>

  3、整合表现层Controller,直接使用springmvc.xml配置文件(springmvc.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.lcl.galaxy.springmvc.controller"/>

    <mvc:annotation-driven/>

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

  4、web.xml加载spring容器(web.xml)

  这里与上面多了web项目上下文的初始化配置(加载了dao和service的配置文件),同时多了listener监听上下文配置的变化以做相关初始化工作。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext-*.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

  5、加载mapper配置文件

  在pom文件的builds标签下新增resources标签配置

        <resources>
            <resource>
                <directory>src/main/Java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>

  6、然后就是相关代码的编写,就无需多说了,使用注解进行处理即可

三、应用核心

(一)返回值处理

  1、不使用注解修饰  

  不使用注解修饰,可以使用ModelAndView、void和String三种

  (1)ModelAndView

  下述代码直接设置了返回的页面为hello,同时设置了user属性的值,然后在jsp页面就可以使用${user}获取设置的值

    @RequestMapping("/view")
    public ModelAndView view(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("hello");
        modelAndView.addObject("user",str);
        return new ModelAndView("hello","user",str);
    }

  (2)使用void,通过HttpServletRequest和HttpServletResponse来处理

  a、设置返回值及编码格式

    @RequestMapping("/http")
    public void getStr(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(str);
    }

  b、进行转发

    @RequestMapping("/forward")
    public void forword(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getRequestDispatcher("view").forward(request, response);
    }

  c、进行重定向

    @RequestMapping("/redirect")
    public void redirect(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendRedirect("http");
    }

  (3)使用String

  a、返回访问页面

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

  b、返回具体值

    @RequestMapping("/str")
    @ResponseBody
    public String getStr(){
        return "hello";
    }

  c、转发

    @RequestMapping("/strforword")
    public String forword(){
        return "forward:view";
    }

  d、重定向

    @RequestMapping("/strRedirect")
    public String rediect(){
        return "redirect:http";
    }

  2、使用注解修饰

  使用注解修饰主要就是在类上或者方法上加载@ResponseBody注解,让请求直接返回Json串;

  同时,可以将@Controller注解和@ResponseBody注解进行合并,直接使用@RestController注解即可

(二)参数绑定处理

  参数绑定是指将请求串中的value值获取到后,再进行类型转换,然后将转换后的值赋值给Controller类中方法的形参,这个过程就是参数绑定。

  SpringMvc内置了24种参数组件,同时Controller种形参可以使用以下类型的参数,处理器会自动识别并进行赋值

    HttpServletRequest:通过request对象获取请求信息

    HttpServletResponse:通过response处理响应信息

    HttpSession:通过session对象得到session中存放的对象

    InputStream、OutPutStream

    Reader、Writer

    Model、ModelMap

  对于参数绑定,可以分为简单参数绑定、对象参数绑定、集合参数绑定、自定义日期绑定、文件类型绑定

  1、简单参数绑定

    (1)如果什么都不写,表示请求参数名称必须与方法中参数名称一致、同时该值必须传递

    (2)同时可以使用@RequestParam注解来进行设置,该注解中有:value属性,表示传递参数的名称;defaultValue属性,表示如果不传该参数,默认值为多少;required属性,表示是否必传,默认为true。

  2、对象类型绑定

    这里主要是保证参数名称要和对象中的属性保持一致

  3、集合参数绑定

    如果是简单类型的参数绑定,则使用数据来处理,不能使用List处理;如果是对象中某个值的集合,则可以使用list作为对象的属性即可。

@RequestMapping("/request")
@RestController
public class RequestController {

    private static final String returnStr = "返回值。。。。。。。。。。。";

    @RequestMapping("str")
    public String getStr(String name){
        return returnStr;
    }

    @RequestMapping("str1")
    public String getStr1(@RequestParam(value = "name",defaultValue = "123",required = false) String name123){
        return returnStr;
    }

    @RequestMapping("user")
    public String user(UserDo userDo){
        return returnStr;
    }

    @RequestMapping("strArray")
    public String strArray(String[] arrs){
        return returnStr;
    }

}

  4、自定义日期参数绑定

  对于SpringMvc无法解析的参数绑定,例如年月日绑定到日期上会报错,此时就需要自定义的参数转换器来进行参数绑定

  首先需要自定义一个参数转换器

public class DateConverter implements Converter<String, LocalDate> {

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Override
    public LocalDate convert(String dateStr) {
        LocalDate date = LocalDate.parse(dateStr, formatter);
        return date;
    }
}

  然后将对象参数类型转换器进行配置

    <mvc:annotation-driven/>

    <mvc:annotation-driven conversion-service="conversionService"/>

    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.lcl.galaxy.springmvc.utils.DateConverter"/>
            </set>
        </property>
    </bean>

  5、文件类型参数绑定

  对于文件类型的参数绑定,需要先导入一个文件上传的依赖包

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>

  然后JSP页面上需要指定表单enctype=”multipart/form-data”

  然后配置muitipart类型解析器

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="5242880"/>
    </bean>

  最后写Controller代码:主要就是新生成一个名称,然后文件赋值,然后进行上传

    @RequestMapping("upload")
    public String upLoad(MultipartFile file) throws IOException {
        if (file != null){
            String originalFilename = file.getOriginalFilename();
            String picPath = "";
            String extName = originalFilename.substring(originalFilename.lastIndexOf("."));
            String newName = UUID.randomUUID() + extName;
            File newFile = new File(newName + "." + extName);
            file.transferTo(newFile);
        }
        return "OK";
    }

(三)拦截器应用

  SpringMvc的拦截器主要是针对特定处理器进行拦截的

  SpringMvc拦截器实现对每一个请求处理前后进行相关的业务处理,类似于Servlet中的filter;但是两者有很大的区别,一个是过滤(回调),一个是拦截增强(AOP)

  SpringMvc中的Interceptor拦截器是通过HandlerInterceptor接口来实现的。

  在SpringMvc中定义一个Interceptor主要有四种方式:

    (1)实现SpringMvc的HandlerInterceptor接口

    (2)继承实现了HandlerInterceptor接口的类,比如SpringMvc中的抽象类HandlerInterceptorAdaptor

    (3)实现SpringMvc的WebRequestInterceptor接口

    (4)继承实现了WebRequestInterceptor接口的类

  我们可以看下SpringMvc的HandlerInterceptor接口,提供了三个方法preHandle、postHandle、afterCompletion,这三个方法的执行时机为:

    preHandle:在Handle执行前调用,可以做登录认证、身份认证等,如果返回true,则放行,否则不放行

    postHandle:进入handle开始执行,并在返回ModelAndView之前调用

    afterCompletion:执行完handle之后调用,可以用作统一异常处理、统一日志等

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

  下面就写一个登陆拦截的样例

  1、Controller

package com.lcl.galaxy.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;


@Controller
public class LoginController {

    @RequestMapping("/login")
    public String login(HttpSession session, String username, String password){
        if("lcl".equals(username) && "123".equals(password)){
            session.setAttribute("username", username);
            return "redirect:orderInfo";
        }
        return "login";
    }

    @RequestMapping("/orderInfo")
    public String order(HttpSession session, String username, String password){
        return "orderInfo";
    }

    @RequestMapping("/logout")
    public String logout(HttpSession session){
        session.invalidate();
        return "redirect:login";
    }
}

  2、拦截器代码

package com.lcl.galaxy.springmvc.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class MyHandlerInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        log.info("requestURI={}", requestURI);
        if(requestURI.indexOf("login") > -1){
            return true;
        }
        String username = (String) request.getSession().getAttribute("username");
        if(username != null && !"".equals(username)){
            return true;
        }
        response.sendRedirect("login.jsp");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

  3、拦截器配置

    <mvc:interceptors>
        <bean class="com.lcl.galaxy.springmvc.interceptor.MyHandlerInterceptor"/>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.lcl.galaxy.springmvc.interceptor.MyHandlerInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

(四)CORS跨域解决方案

  浏览器为了安全考虑,设置了同源策略,DNS域名、端口号、协议完全一致被称为同源。

  同源下的页面之间才可以进行js的dom操作,如果不在同一个源下,任何跨文档dom的访问都是被阻止的。

  那么解决跨域有两个方面的考虑,避开Ajax请求方式和解决同源策略问题

  那么解决跨域的方式有多种,例如:

    (1)基于JavaScript的src方式

    (2)基于Jquery的JSONP方式

    (3)基于CORS的方式(解决同源的问题)

  对于JSONP和CORS的区别主要是JSONP只能解决get请求,而CORS既能支持get请求,又可以支持post请求。

  这里主要说一下CORS的解决方案,CORS是一个W3C标准,全程是“跨域资源共享”,它允许浏览器向跨域服务器发出XMLHttpRequest请求,从而客服了Ajax只能同源使用的限制;CORS同时需要浏览器和服务器的支持(目前浏览器都已支持,IE需要IE10以上)

  CORS的原理:

    (1)客户端自动向请求头header中注入origin

    (2)服务器端需要向响应头Header中注入Access-Control-Allow-Origin

    (3)浏览器检测到Access-Control-Allow-Origin,就可以跨域操作了

   对于CORS的实现,在Spring4.0之前的版本使用SpringMvc的拦截器处理;在SpringMvc4.0之后,则只需要在允许跨域访问的类或者方法上使用@CrosOrigin注解即可,同时需要在Spring的配置文件中做配置。

    <mvc:cors>
        <mvc:mapping path="/**"/>
    </mvc:cors>

(五)父子容器

  由于项目是SSM整合项目,那么项目中就会存在spring容器和springmvc容器,例如下面的代码:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext-*.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

  上面代码就是之前写的web.xml文件,里面通过DispatcherServlet初始化了springMvc容器,同时也使用ContextLoadListener初始化了spring容器,由于是spring对springmvc、mybatis的整合,使用ContextLoadListener初始化的spring容器是一个父容器,那么使用DispatcherServlet初始化的springmvc容器则是子容器。

  spring项目各个容器是相互隔离的,一个容器不能访问其他容器的内容。父容器和子容器并非包含关系,而是并列关系,只是在子容器中使用parent指向父容器。在spring项目只有一个树状结构的容器数,子容器虽然不能直接访问父容器的内容,但是可以通过parent变相访问。

  基于以上内容,如果springmvc中的bean都是controller,父容器中都是service,那么Controller是可以直接使用@Autowired注解使用Service的,但是Service不能使用@Autowired注解调用Controller

四、其他

(一)Mock测试

  Mock测试主要是模拟一些不太容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。

  目前,在Java阵营中主要的Mock测试工具有JMock、MockCreator、EasyMock、MockMarker等

  1、引入pom依赖

        <!-- spring 单元测试组件包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency>
        <!-- 单元测试Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- Mock测试使用的json-path依赖 -->
        <dependency>
            <groupId>com.jayway.jsonpath</groupId>
            <artifactId>json-path</artifactId>
            <version>2.2.0</version>
        </dependency>

  2、编写测试文件

  (1)MockMvc是Mock测试的主入口;首先注入web项目的配置文件上下文对象WebApplicationContext,然后使用MockMVCBuilder构造一个MockMvc。

  (2)使用MockMvcRequestBuilders构建一个Request请求,其主要的两个子类MockHttpServletRequestBuilder和MockMuiltipartServletRequestBuilder(用于上传文件使用)

  (3)MockMvc提供了一个perform方法,参数为Request请求,返回值为ResultActions

  (4)ResultActions里面封装了几个方法:

      andExpect:添加ResultMatcher验证规则,验证执行器执行后的结果是否正确

      andDo:添加ResultHandler处理结果,比如调试时打印结果到控制台等

      andReturn:最后返回相应的MvcResult;

import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/*.xml")
@WebAppConfiguration
@Slf4j
public class TestMocMvc {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

    @Before
    public void setup(){
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    }


    @Test
    public void test() throws Exception {
        ResultActions perform = mockMvc.perform(MockMvcRequestBuilders.get("/mvc/str").param("name", "123"));
        MvcResult mvcResult = perform
                                     .andExpect(MockMvcResultMatchers.view().name("hello"))
                                     .andExpect(MockMvcResultMatchers.status().isOk())
                                     .andDo(MockMvcResultHandlers.print())
                                     .andReturn();
        log.info("========================================{}", mvcResult.getHandler());
    }

}

(二)异常处理器

  1、自定义异常,继承Exception

package com.lcl.galaxy.springmvc.exception;

import lombok.Data;

@Data
public class MyException extends Exception {

    private String message;

    public MyException(String msg){
        super(msg);
        this.message = msg;
    }
}

  2、编写异常处理器

package com.lcl.galaxy.springmvc.exception;

import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        MyException myException = null;
        if(e instanceof MyException){
            myException = (MyException) e;
        }else {
            myException = new MyException("未知错误");
        }
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("message", myException.getMessage());
        modelAndView.setViewName("error");
        return modelAndView;
    }
}

  3、配置自定义异常处理器

    <bean class="com.lcl.galaxy.springmvc.exception.MyExceptionResolver"/>

  4、异常测试

    @RequestMapping("/error")
    public String errTest(String id) throws MyException {
        if("1".equals(id)){
            throw new MyException("id = 1  error");
        }
        return "susccess";
    }

(三)乱码解决

  1、get请求乱码

    (1)修改tomcat配置文件,添加转码

<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

    (2)对请求参数进行重新编码

id = new String(id.getBytes("ISO8859-1"),"utf-8") ;

    (3)过滤器(MyRequestWrapper)+请求装饰器(MyCharacterEncodingFilter)一起解决

  2、post请求乱码

    在web.xml文件中添加过滤器,对请求统一做转码处理

    <filter> 
        <filter-name>CharacterEncodingFilter</filter-name> 
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 
        <init-param> 
            <param-name>encoding</param-name> 
            <param-value>utf-8</param-value> 
        </init-param> 
    </filter> 
    <filter-mapping> 
        <filter-name>CharacterEncodingFilter</filter-name> 
        <url-pattern>/*</url-pattern> 
    </filter-mapping>

  3、返回值乱码

  在@RequestMapping注解的produces属性上设置编码格式即可

    @RequestMapping(value = "/error", produces = "text/plain;charset=UTF-8")
posted @ 2020-12-13 18:22  李聪龙  阅读(203)  评论(0编辑  收藏  举报