SpringBoot工程中业务逻辑实践及MVC应用实践

公告业务设计

业务描述

本章节通过对公告业务对象的分析,进行业务逻辑操作的具体设计和实现。

API 设计分析

SpringBoot工程中业务逻辑实践及MVC应用实践_第1张图片
说明,在这张图中我们从右侧向左看,最右边是数据层,中间是业务层,最左边是对业务层的测试。

代码设计及实现

业务接口设计

定义通知业务逻辑接口及相关方法,关键代码如下:

package com.cy.pj.sys.service;

import com.cy.pj.sys.pojo.SysNotice;

import java.util.List;

/**
 * 此对象为通告业务逻辑对象负责对通告业务做具体落地实现
 * 1)调用数据逻辑对象执行数据操作
 * 2)日志记录
 * 3)事务处理
 * 4)缓存
 * 5)权限
 * 6).............
 */
public interface SysNoticeService {
    /**
     * 新增通告信息
     * @param notice
     * @return
     */
    int saveNotice(SysNotice notice);

    /**
     * 基于条件查询通告信息
     * @param notice
     * @return
     */
    List<SysNotice> selectNotices(SysNotice notice);

    /**
     * 基于通告删除通告信息
     * @param ids
     * @return
     */
    int deleteById(Long… ids);

    /**
     * 基于id查询某条通告
     * @param id
     * @return
     */
    SysNotice selectById(Long id);
    /**
     * 更新通告信息
     * @param notice
     * @return
     */
    int updateNotice(SysNotice notice);
}

日志规约描述

日志是进行数据分析,异常分析的一种有效手段,我们在业务执行过程中对业务相关操作,进行日志记录是必须的一道程序。在进行日志的记录是,日志API选型也会影响到后续日志操作的可维护性和可扩展性。本次业务日志我们选择SLF4J,这是一套Java中基于门面模式而设计的一套日志API规范,其关键应用如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
    
  /**创建日志门面API对象*/
    private static final Logger log=
            //通过工厂创建日志对象
            LoggerFactory.getLogger(SysNoticeServiceImpl.class);

其中,SLF4J基于此标准的实现有logback,log4j等具体的实现,我们编程时,建议耦合与规范或标准进行日志记录。

业务实现类设计

定义通知业务逻辑接口的实现类,并重写业务方法,重写过程中对相关业务进行日志记录,关键代码如下:

package com.cy.pj.sys.service.impl;

import com.cy.pj.sys.dao.SysNoticeDao;
import com.cy.pj.sys.pojo.SysNotice;
import com.cy.pj.sys.service.SysNoticeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
/**
 * @Service注解由spring提供,一般用于描述分层架构中的业务逻辑对象,这样的类
 * 会交给spring管理
 */
@Service
public class SysNoticeServiceImpl implements SysNoticeService {
    /**创建日志门面API对象*/
    private static final Logger log=
            //通过工厂创建日志对象
            LoggerFactory.getLogger(SysNoticeServiceImpl.class);

    @Autowired
    private SysNoticeDao sysNoticeDao;
    //重写方法的生成 (选中类,然后alt+enter)
    @Override
    public int saveNotice(SysNotice notice) {
        int rows=sysNoticeDao.insertNotice(notice);
        return rows;
    }

    @Override
    public List<SysNotice> selectNotices(SysNotice notice) {
            //System.out.println(
"system.out.start:"+System.currentTimeMillis());
        log.debug("start: {}",System.currentTimeMillis());
//这里的{}表示占位符号
        List<SysNotice> list=sysNoticeDao.selectNotices(notice);
        log.debug("end: {}",System.currentTimeMillis());
        return list;
    }
    @Override
    public int deleteById(Long... ids) {
        int rows= sysNoticeDao.deleteById(ids);
        return rows;
    }

    @Override
    public SysNotice selectById(Long id) {
        SysNotice notice=sysNoticeDao.selectById(id);
        return notice;
    }

    @Override
    public int updateNotice(SysNotice notice) {
        int rows=sysNoticeDao.updateNotice(notice);
        return rows;
    }
}

单元测试分析

定义单元测试类,对业务方法进行单元测试

package com.cy.pj.sys.service;

import com.cy.pj.sys.pojo.SysNotice;
import org.junit.jupiter.api.Test;

@SpringBootTest
public class SysNoticeServiceTests {
    @Autowired
    private SysNoticeService sysNoticeService;

    @Test  void testFindNotices(){
        SysNotice notice=new SysNotice();
        notice.setType("1");
        notice.setModifiedUser("tony");
        sysNoticeService.selectNotices(notice);
    }
}

重难点分析

分层架构中service是如何定义的?
公告业务接口及方法的设计
公告业务实现与公告数据层接口的关系

FAQ分析

@Service注解是谁定义的,有什么作用?
为什么要记录日志,一般都会记录什么信息?
@Service描述的类中是如何应用日志API的?
你知道哪些日志级别,在哪里进行配置?
日志要写到文件的基本配置是什么?
….

Bug分析

NoSuchBeanDefinitionException
NullPointerException

SpringBoot工程中MVC应用实践

Spring MVC 概述

Spring MVC是MVC设计思想在Spring框架中的一种实现,基于这样的设计思想,Spring框架设计了一些相关对象,最后将这些对象组装在一起,构建了一个专门用于处理Web请求的模块,称之为spring web模块,这个模块底层基于MVC设计思想,封装了对Servlet的技术的应用,简化了程序员对请求和响应过程中数据的处理,其简易架构分析如图所示:
SpringBoot工程中业务逻辑实践及MVC应用实践_第2张图片
其中,核心组件分析:

DispatcherServlet :前端控制器, 请求处理入口(你去银行营业厅有一个大堂经理负责问你需求一样,你去公司有前台接待一样)。
HandlerMapping:映射器对象, 管理url与controller的映射关系(就类型于基于身份证号能够查到对应人的信息一样)。
Interceptors:拦截器,实现请求响应的共性处理(类似与你进大楼要看你的健康码,你上地铁要进行安检一样)。
Controller:后端控制器-handler, 负责处理请求的控制逻辑(银行用于处理各种请求的业务的窗口)。
ViewResolver:视图解析器,解析对应的视图(类似别人给了你一个地址,你知道这个地址再哪,有人帮你指路)。
备注:先鸟瞰其全貌,然后基于实践逐步进行渗透、学习每个组件的应用原理。

快速入门实践

业务描述

客户端向服务端发送一个请求,服务端响应一个字符串到客户端,页面上呈现hello spring web mvc。

创建项目

第一步:创建maven项目模块,名字为04-mvc 其初始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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.2.RELEASE</version>
	</parent>
	
	<groupId>com.cy</groupId>
	<artifactId>04-mvc</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>04-mvc</name>
	<description>Demo project for Spring Boot</description>
	
	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

添加项目依赖

打开pom.xml文件并添加Spring Web依赖(提供了spring mvc依赖支持),代码如下:

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

其中,这个依赖添加时,会关联下载一个tomcat依赖(这个tomcat为一个嵌入式tomcat服务器)

创建项目配置文件

创建项目配置文件application.properties并配置服务启动端口为80(默认为8080)server.port=80 #以application.properties为例

创建启动类对象
package com.jt;
@SpringBootApplication
public class  WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }
}

创建请求处理器对象

创建一个客户端请求处理器对象,并将此对象交给spring管理,代码如下:

package com.jt.demo.controller;
@RestController
public class HelloController {
   @RequestMapping("/doSayHello")
   public String doSayHello(){
      return "hello spring web mvc";
   }
}

其中:

@RestController注解描述的类为spring web模块的请求处理类型,此类型的对象会交给spring管理。
@RequestMapping注解描述的方法为请求处理方法,此注解内容定义的字符串为访问此方法需要的请求url。

启动服务并访问测试

打开浏览器,在浏览器地址栏输入http://localhost/doSayHello,检测日志输出。

请求参数处理实践

准备工作

创建一个请求参数处理器,例如:

package com.jt.demo.controller;
@RestController
@RequestMapping("/param")
public class ParameterController {
}

直接量方式

在ParameterController中添加方法,基于直接量方式接受客户端请求参数,例如:

//访问方式:http://localhost/param?id=10
//@RequestParam 注解描述方法参数时,默认必须为id参数传值
@RequestMapping
public String doParam01(@RequestParam String id){
 return "request params id is "+id;
}

//访问方式:http://localhost/param/rest/10/jack
//{id}{name}表示占位符,用于接收请求url中/rest/后面的内容,注意顺序
//@PathVariable描述方法参数时,表示接收与参数名相同的/rest/{id}/{name}表达式中{id} {name}的值
@RequestMapping("/rest/{id}/{name}")
public String doParam02(@PathVariable Integer id,@PathVariable String name){
 return "request params id is"+id+" ; name is "+name;
}

POJO方式

第一步:定义pojo对象,用于接受客户端请求参数,例如:

package com.jt.pojo;
public class ParameterWrapper {
    private Integer id;
    private String name;
    //自己添加set/get/toString方法
}

第二步:定义请求处理方法

//访问方式:http://localhost/param/pojo?id=10&name=jack
//系统底层在使用pojo对象接收请求参数时,底层会通过反射构建参数对象,
//并通过反射调用参数名对应的pojo对象set方法将值封装到pojo对象。
@RequestMapping("/pojo")
public String doParam03(ParameterWrapper parameterWrapper ){
 return "request params id is "+parameterWrapper;
}

//访问方式:基于ajax或postman发送post请求,参数类型为application/json,传递数据为json格式:{"id":10,"name":"jack"}
//@RequestBody描述pojo对象时表示参数的值类来自自一个json格式数据
@RequestMapping("/pojo")
public String doParam04(@RequestBody ParameterWrapper parameterWrapper ){
 return "request params id is "+parameterWrapper;
}

Map对象方式

有时候为了简单,我们不想使用pojo对象接收请求参数,还可以使用map对象来接收,又该如何实现呢?
定义Controller方法,基于map对象接收请求参数,例如:

//访问方式:http://localhost/param/map?id=10&name=jack
//注意:当使用map对象接收请求参数时,必须实用@RequestParam注解对参数进行修饰,
//也可以实用@RequestBody进行描述接收json格式数据
@RequestMapping("/map")
public String doParam05(@RequestParam Map<String,Object> param){
 return "request params "+param.toString();
}

响应数据处理

准备工作

定义请求处理器,重点演示响应数据的封装,例如:

package com.jt.controller;
@RestController
@RequestMapping("/resp/")
public class ResponseController {
}

响应数据封装

实际项目中我们通常会定义一个pojo对象,封装响应数据(状态吗,消息,业务数据),例如:

public class JsonResult {
    /**状态码*/
    private Integer state=1;//1 表示OK,0表示Error
    /**状态码信息*/
    private String message="ok";
    /**封装正确的查询结果*/
    private Object data;

    public JsonResult(){}
    public JsonResult(String message){
        this.message=message;
    }
    public JsonResult(Integer state,String message){
        this(message);
        this.state=state;
    }
    public JsonResult(Object data){//new JsonResult(list)
        this.data=data;
    }
    //当出现异常时,可以通过此构造方法对异常信息进行封装
    public JsonResult(Throwable exception){//new JsonResult(exception);
        this(0,exception.getMessage());
    }
    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}


请求处理器定义

案例1:在ResponseController中添加请求处理方法,将响应对象封装为pojo对象,例如

   //访问方式 http://localhost/resp/pojo
   //将响应数据封装为pojo对象,然后spring底层会将这个对象转换为json格式字符串
   @RequestMapping("/pojo)
   public ResponseResult doResp01(){
        Map<String,Object> map=new HashMap<>();
        map.put("id",101);
        map.put("title","spring mvc");
        JsonResult rs=new JsonResult();
        rs.setState(1);
        rs.setMessage("OK");
        rs.setData(map);
        return rs;
    }

案例2:直接基于response对象响应数据

    //访问方式:http://localhost/resp/print
    @RequestMapping("/print")
    public void doPrint(HttpServletResponse response)throws Exception{
        Map<String,Object> map=new HashMap<>();
        map.put("id",101);
        map.put("title","hello spring web mvc");
        //将map中的数据转换为json格式字符串
        ObjectMapper om=new ObjectMapper();
        String jsonStr=om.writeValueAsString(map);
        System.out.println("jsonStr="+jsonStr);
        //将字符串响应到客户端
        //设置响应数据的编码
        response.setCharacterEncoding("utf-8");
        //告诉客户端,要向它响应的数据类型为application/json,编码为utf-8.请以这种编码进行数据呈现
        response.setContentType("application/json;charset=utf-8");
        PrintWriter pw=response.getWriter();
        pw.println(jsonStr);
        pw.flush();
    }

响应异常统一处理

Spring框架中web模块定义了一套全局异常处理规范,基于这个套规范处理Controller中抛出的异常,例如:

package com.jt.advice;
/** @RestControllerAdvice 注解描述的类为全局异常处理类,启动时会交给spring管理*/
@RestControllerAdvice
public class GlobalExceptionHandler {
       private static final Logger log=LoggerFactory.getLogger(GlobalExceptionHandler.class);
      /**@ExceptionHandler注解描述的方法为异常处理方法,注解中定义的异常类型为方法可以处理的异常类型.*/
       @ExceptionHandler(RuntimeException.class)
       public JsonResult doHandleRuntimeException(RuntimeException e){
           e.printStackTrace();
           log.error("exception msg is {}",e.getMessage());
           return new JsonResult(e);
       }
       //可以定义多个异常处理方法
}

其中,全局异常处理过程,如图所示:
SpringBoot工程中业务逻辑实践及MVC应用实践_第3张图片

拦截器技术应用

概述

Spring Web中的拦截器(Interceptor)基于回调机制,可以在目标方法执行之前,先进行业务检测,满足条件则放行,不满足条件则进行拦截,拦截器原理分析如下图所示:
SpringBoot工程中业务逻辑实践及MVC应用实践_第4张图片

拦截器定义

基于Spring MVC模块提供的HandlerInterceptor接口规范定义拦截器,对特定请求进行拦截,例如检测请求访问时间,关键代码如下,

package com.jt.web.interceptor;
public class TimeAccessInterceptor  implements HandlerInterceptor {
	/**
	 * preHandle在控制层目标方法执行之前执行
	 */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {
       //testRequestInfo(request,handler);
       LocalTime now=LocalTime.now();//JDK8中的时间对象
       int hour=now.getHour();//获取当前时间对应小时
       //System.out.println("hour="+hour);
       log.info("hour {}",hour);
       if(hour<=6||hour>=22)
           throw new RuntimeException("请在6~10点进行访问");
        return true;
   }
}

拦截器配置

定义配置类,实现对拦截器的注册,关键代码如下。

package com.jt.web.config;
@Configuration
public class SpringWebConfig implements WebMvcConfigurer{//web.xml

	 //配置spring mvc 拦截器
	 @Override
	 public void addInterceptors(InterceptorRegistry registry) {
		 registry.addInterceptor(new TimeAccessInterceptor())
		         .addPathPatterns("/param/*");
	 }
}

访问测试

启动服务,对于url中path以/param/开头的进行访问测试,检测请求是否被拦截处理了。

系统公告Web业务实践

业务描述

定义Controller对象处理客户端的Notice数据请求。

API设计分析

@Controller对象在项目API设计如图所示:SpringBoot工程中业务逻辑实践及MVC应用实践_第5张图片

Controller类及方法

本次Controller中请求处理方式采用Rest风格,REST不是一个单词,是Representational State Tramsfer 的缩写,直译过来就是表述性状态转移的缩写。概念理解起来很晦涩,其实其核心就是资源,用URL定位资源,用HTTP动作表述要做的操作,HTTP动作通常会包含GET、PUT、POST、DELETE等,每一种动作,从本质上而言就是对资源的操作,例如GET就是获取资源,用于查询资源,POST用于新增资源,PUT请求用于更新资源,DELETE请求用于删除资源。

创建SysNoticeController类,通过此类对象处理控制层分发的请求对象,关键代码如下:

package com.jt.web.controller;
@RequestMapping("/notice/")  
public class SysNoticeController {// handler
    //关联service,将业务逻辑交给service对象处理
    @Autowired
    private SysNoticeService sysNoticeService;

    @GetMapping
    public JsonResult doSelectNotices(SysNotice notice){
        return new JsonResult(sysNoticeService.findNotices(notice));
    }
    @PostMapping
    public JsonResult doInsertNotice(@RequestBody SysNotice notice){
          sysNoticeService.saveNotice(notice);
        return new JsonResult("save ok");
    }
    @GetMapping("{id}")
    public JsonResult doSelectById(@PathVariable Long id){
       return  new JsonResult(sysNoticeService.findById(id));
    }
    @PutMapping
    public JsonResult doUpdateNotice(@RequestBody SysNotice notice){
        sysNoticeService.updateNotice(notice);
        return new JsonResult("update ok");
    }
    
    @DeleteMapping("{ids}")
    public JsonResult doDeleteById(@PathVariable  Long... ids){
        sysNoticeService.deleteById(ids);
        return new JsonResult("delete ok");
    }
}

重难点分析

MVC设计思想的理解
Spring Web模块中MVC设计的设计
Spring MVC中核心组件及其作用。
基于Spring MVC实现请求处理的过程。

FAQ分析

什么是MVC(分层设计思想,一种编程套路)
MVC具体指的是哪些单词的缩写(Model,View,Controller)
举个生活中MVC的例子(全聚德饭店:菜单-view,服务员-controller,厨师-model)
Spring Web模块是如何基于MVC设计思想做具体实现的?
@RestController的作用是什么?
@ResponseBody注解的作用是什么?
@RequestMapping的作用是什么,注解内部都有什么属性?
@RestController描述的类中,其方法返回值是如何转换为json的。

BUG分析

404 (资源没有找到)
400 (请求参数与服务端参数不匹配)
405 (请求方式与服务端不匹配)
500 (服务端处理请求的过程中出现了问题)

你可能感兴趣的:(spring,boot,mvc,后端)