后端主流框架——04Spring业务整合

目录

前端渲染

后端渲染

session 存值

拦截器

 统一异常处理

文件上传

Lombok

spring mvc 处理请求过程

ssm 项目整合


前端渲染

前端渲染指后台响应一般由 json 字符串的方式返回,再由专业前端开发工程师渲染前端页面,是现在前后端分离开发项目主要技术手段。在前后端分离开发中后端使用 @ResponseBody 注解将返回的对象以 json 格式传给前端,而不是寻找响应视图。@ResponseBody 加在方法上,则该方法返回的对象序列化成 json 字符串,如果 @ResponseBody 加在控制器类上,则该类的全部方法都返回 json 字符串,可简写为 @RestController。通过源码可以看到 @RestController 是 @Controller 与 @ResponseBody 的复合注解。

//@RestController源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
	String value() default "";
}
@ResponseBody
@RequestMapping("/user")
public User testJson(User user){
    return user;
}

注意:当实体包含时间时,会自动转换为该时间的 long 类型。如果需要按照指定格式序列化成 json 使用 @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 注解,其中的 timezone ="GMT+8" 表示东八区。

后端渲染

传统模式还是使用 jsp 渲染页面,通过客户端浏览器调用对应路径的接口,该接口会将数据存入 request 然后转发到对应的 jsp 渲染页面,浏览器收到的是已经渲染好的页面。在 spring MVC 中使用模型视图 ModelAndView 即可指明转发的视图和携带的数据。

public ModelAndView testModelAndView(){   
    ModelAndView modelAndView = new ModelAndView("index");
    //添加模型数据到 ModelAndView 中.
    modelAndView.addObject("time", new Date());
    return modelAndView;
}

public String testModel(Model model){   
    //添加模型数据到 ModelAndView 中.
    model.addObject("time", new Date());
    return "index";
}

2.模型视图 Map 也可以完成在 request 域中存入数据,在对应转发的 jsp 上也能获取数据。但是由于所写代码可读性差,不建议使用。在实际开发中,如果不想由对象指定视图可以使用 Model 或 ModeMap 取代 Map 在 request 中存入数据。

public String testMap(Map<String, Object> map){
    map.put("names", Arrays.asList("Tom", "Jerry", "Mike"));
    return "index";
}

session 存值

 在默认情况下,ModelMap 中的属性作用域是 request 级别是,也就是说,当本次请求结束后,ModelMap 中的属性将销毁。如果希望在多个请求中共享 ModelMap 中的属性,必须将其属性转存到 session 中,这样 ModelMap 的属性才可以被跨请求访问。

spring 允许我们有选择地指定 ModelMap 中的哪些属性需要转存到 session 中,以便下一个请求属对应的 ModelMap 的属性列表中还能访问到这些属性。这一功能是通过类定义处标注 @SessionAttributes 注解来实现的。

在清除 Session 中的数据时不能调用 HttpSession 中的 removeAttribute("attributeName") 方法,需要调用接口 SessionStatus 中的 setComplete 方法。接口SessionStatus 的唯一实现是类 SimpleSessionStatus,调用 setComplete 方法将 boolean 类型的属性 complete 设置为 true。

@Controller
@SessionAttributes("loginUser")
public class IndexController {
}
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login(String username,String password,Model model) {
    User u = userService.login(username, password);
    model.addAttribute("loginUser", u);
    return "redirect:/user/users";
}

@SessionAttributes 参数   

  • names:这是一个字符串数组。里面应写需要存储到 session 中数据的可能名称。   
  • types:根据指定参数的类型,将模型中对应类型的参数存储到 session 中 。
  • value:其实和 names 功能是一样的。

@SessionAttribute 是用于获取已经存储的 session 数据,并且作用在方法的层面上。一般放在方法参数上。

拦截器

拦截器和先前学习的过滤器用法相似,都是用来做权限验证的,但是该组件属于 spring mvc (过滤器属于 tomcat),定义拦截器比过滤器要简单很多,只需要实现 HandlerInterceptor 接口,并实现相应的方法即可。其中方法返回 Boolean 类型的结果,若返回值为 true,则继续调用后续的拦截器和目标方法。若返回值为 false,则不会再调用后续的拦截器和目标方法。

public class FirstInterceptor implements HandlerInterceptor{
   /**
    * 该方法在目标方法之前被调用.
    */
   @Override
   public boolean preHandle(HttpServletRequest request,
         HttpServletResponse response, Object handler) throws Exception {
      System.out.println("[FirstInterceptor] preHandle");
      return true;//true 表示放行 false 表示不放行
   }

   /**
    * 调用目标方法之后, 但渲染视图之前. 
    */
   @Override
   public void postHandle(HttpServletRequest request,
         HttpServletResponse response, Object handler,
         ModelAndView modelAndView) throws Exception {
      System.out.println("[FirstInterceptor] postHandle");
   }

   /**
    * 渲染视图之后被调用. 释放资源
    */
   @Override
   public void afterCompletion(HttpServletRequest request,
         HttpServletResponse response, Object handler, Exception ex)
         throws Exception {
      System.out.println("[FirstInterceptor] afterCompletion");
   }

}

在 spring mvc 配置文件中配置一个或多个拦截器,同时还可以配置拦截器的拦截路径(mapping)和放行路径(exclude-mapping),spring 使用 ant 风格的路径。在开发中需要用到字符串字符串匹配时使用:AntPathMatcher。

AntPathMatcher matcher = new AntPathMatcher();
String path = "/test/a/b";
String pattern = "/test/**";
boolean isMatch=matcher.match(pattern, path);
  • ?(匹配单个字符)
  • *(匹配除/外任意字符)
  • **/(匹配任意多个目录)。

配置拦截器如下:

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/login"/>
        <mvc:exclude-mapping path="/register"/>
        <mvc:exclude-mapping path="/css/**"/>
        <mvc:exclude-mapping path="/js/**"/>
        <bean class="cn.hx.controller.FirstInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

 统一异常处理

在项目开发中,免不了出现各种大大小小的异常,spring mvc 提供一套非常完善的异常处理机制,以至于我们在项目中,所有的异常都可以往外抛出,最后由我们的异常处理类来完成异常处理。异常处理分为局部异常处理、全局异常处理和返回 json 的异常处理。

1.处理局部异常(在 Controller 内)

@ExceptionHandler
public ModelAndView exceptionHandler(Exception ex){
    ModelAndView mv = new ModelAndView("error");
    mv.addObject("exception", ex);
    System.out.println("in testExceptionHandler");
    return mv;
}
    
@RequestMapping("error")
public String error(){
    int i = 5/0;
    return "hello";
}

2.处理全局异常(在 ControllerAdvice 中)

@ControllerAdvice
public class MyControllerAdvice {
    @ExceptionHandler
    public ModelAndView exceptionHandler(Exception ex){
        ModelAndView mv = new ModelAndView("error");
        mv.addObject("exception", ex);
        System.out.println("in testControllerAdvice");
        return mv;
    }
}

3.返回 json 字符串的异常处理

@ResponseBody
@ControllerAdvice
public class MyControllerAdvice {

    @ExceptionHandler
    public Map exceptionHandler(Exception ex){
        Map map=new HashMap();
        map.put("exception", ex.getMessage());
        System.out.println("in testExceptionHandler");
        return map;
    }
}

文件上传

在 spring mvc 中想要完成文件上传需要配置 MultipartResolver,MultipartResolver 是一个接口,它的实现类分为 CommonsMultipartResolver 类和 StandardServletMultipartResolver 类。

其中 CommonsMultipartResolver 使用 commons Fileupload 来处理 multipart 请求,所以在使用时,必须要引入相应的 commons-fileupload 依赖;而 StandardServletMultipartResolver 是基于 Servlet 3.0 来处理 multipart 请求的,所以不需要引用其他依赖,但是必须使用支持 Servlet 3.0 的容器才可以,以 tomcat 为例,从 Tomcat 7.0.x 的版本开始就支持 Servlet 3.0 了。

1.配置 CommonsMultipartResolver

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

配置 CommonsMultipartResolver

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 设定默认编码 -->
    <property name="defaultEncoding" value="UTF-8"></property>
    <!-- 设定文件上传的最大值为5MB,5*1024*1024 -->
    <property name="maxUploadSize" value="5242880"></property>
</bean>

2.配置 StandardServletMultipartResolver 

<bean id="multipartResolver"
          class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>

这里并没有配置文件大小等参数,这些参数的配置在 web.xml 中

<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-mvc.xml</param-value>  
    </init-param>  
    <load-on-startup>1</load-on-startup>
    <multipart-config>
        <!-- 最大上传文件大小(配置不能限制) -->
        <max-file-size>297152</max-file-size>
        <!-- 最大请求大小(配置不能限制) -->
        <max-request-size>4194304</max-request-size>
    </multipart-config>
</servlet>

控制器接收文件使用 MultipartFile 对象接收

@PostMapping(value = "/upload")
public void index2(String desc, MultipartFile file) throws IOException {
    File file1 = new File("C://" + file.getOriginalFilename());
    file.transferTo(file1);
}

OriginalFilename 原始的文件名,其他和 jsp Servlet 章节一样,同时也支持 ajax 上传。

Lombok

IntelliJ IDEA 是一款非常优秀的集成开发工具,功能强大,而且插件众多。lombok 是开源的代码生成库,是一款非常实用的小工具,我们在编辑实体类时可以通过 lombok 注解减少 getter、setter 等方法的编写,在更改实体类时只需要修改属性即可,减少了很多重复代码的编写工作。本文小编只介绍 IntelliJ IDEA 中 lombok 插件的安装和配置以及简单的使用方法。

1.使用 lombok 需要安装 lombok 插件。

2.添加 maven 依赖。

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.8</version>
</dependency>

3.在实体上通过 @Data 注解完成。

import lombok.Data;

@Data
public class User {
    private Integer id;
    private String name;
    private String password;
}

4.编译后后的类文件

public class User {
    private Integer id;
    private String name;
    private String password;

    public User() {
    }

    public Integer getId() {
        return this.id;
    }

    public String getName() {
        return this.name;
    }

    public String getPassword() {
        return this.password;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof User)) {
            return false;
        } else {
            User other = (User)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label47: {
                    Object this$id = this.getId();
                    Object other$id = other.getId();
                    if (this$id == null) {
                        if (other$id == null) {
                            break label47;
                        }
                    } else if (this$id.equals(other$id)) {
                        break label47;
                    }

                    return false;
                }

                Object this$name = this.getName();
                Object other$name = other.getName();
                if (this$name == null) {
                    if (other$name != null) {
                        return false;
                    }
                } else if (!this$name.equals(other$name)) {
                    return false;
                }

                Object this$password = this.getPassword();
                Object other$password = other.getPassword();
                if (this$password == null) {
                    if (other$password != null) {
                        return false;
                    }
                } else if (!this$password.equals(other$password)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof User;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        Object $name = this.getName();
        result = result * 59 + ($name == null ? 43 : $name.hashCode());
        Object $password = this.getPassword();
        result = result * 59 + ($password == null ? 43 : $password.hashCode());
        return result;
    }

    public String toString() {
        return "User(id=" + this.getId() + ", name=" + this.getName() + ", password=" + this.getPassword() + ")";
    }
}

spring mvc 处理请求过程

第一步:用户发起 request 请求,请求至 DispatcherServlet 前端控制器

第二步:DispatcherServlet 前端控制器请求 HandlerMapping 处理器映射器查找 Handler。DispatcherServlet:前端控制器,相当于中央调度器,各各组件都和前端控制器进行交互,降低了各各组件之间耦合度。 

第三步:HandlerMapping 处理器映射器,根据 url 及一些配置规则(xml配置、注解配置)查找 Handler,将 Handler 返回给 DispatcherServlet 前端控制器 

第四步:DispatcherServlet 前端控制器调用适配器执行 Handler,有了适配器通过适配器去扩展对不同 Handler 执行方式(比如:原始 servlet 开发,注解开发) 

第五步:适配器执行 Handler。Handler 是后端控制器,当成模型。

第六步:Handler 执行完成返回 ModelAndView。ModelAndView:springmvc 的一个对象,对 Model 和 view 进行封装。

第七步:适配器将 ModelAndView 返回给 DispatcherServlet

第八步:DispatcherServlet 调用视图解析器进行视图解析,解析后生成 view 视图解析器根据逻辑视图名解析出真正的视图。 View:springmvc 视图封装对象,提供了很多 view 如:jsp、freemarker、pdf、excel。。。 

第九步:ViewResolver 视图解析器给前端控制器返回 view

第十步:DispatcherServlet 调用 view 的渲染视图的方法,将模型数据填充到 request 域 。 

第十一步:DispatcherServlet 向用户响应结果( jsp 页面、json 数据。。。。)

或者可以这样说:

Spring主要也是通过DispatcherServlet实现了Servlet这个接口,又叫前端控制器,来自前端的请求会先到达这里,它负责到后台去匹配合适的handler。DispatcherServlet的主要工作流程如下:

前端请求到达DispatcherServlet。

前端控制器请求HandlerMappering 查找Handler。

如果查找到存在的处理器,进一步去调用service和dao层

返回结果再到controller层,渲染具体的视图,返回结果给页面。

ssm 项目整合

1.添加 spring mvc(spring-webmvc:5.2.12.RELEASE、jackson-databind:2.12.3)、spring(spring-aspects、spring-jdbc):5.2.12.RELEASE 和 mybatis(mybatis:3.4.6、mybatis-spring:1.3.2、mysql-connector-java:5.1.38、logback-classic:1.2.3) 依赖。

2.在 web.xml 中配置启动 spring 容器的监听(ContextLoaderListener)及 spring 的配置文件位置(context-param)。配置 spring mvc 的 DispatcherServlet 和 CharacterEncodingFilter。

3.在 springmvc 配置文件中配置开启注解驱动(mvc:annotation-driven)、静态资源处理(mvc:default-servlet-handler)、组件扫描(context:component-scan)、视图解析(InternalResourceViewResolver)。

4.在 spring 配置文件中配置包扫描(context:component-scan)、 mybatis 的数据源(DriverManagerDataSource)、mybatis session 工厂(SqlSessionFactoryBean)、接口查找配置类(MapperScannerConfigurer)、事务配置(DataSourceTransactionManager)、事务注解驱动(tx:annotation-driven) 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值