浅谈java-web开发技术栈

1. 前言

1.1 java-web说明

1.2 java-web前后端分离

1.3 java-web的开发生命周期

2. 技术栈

2.1 前端技术栈

2.1.1 HTML + CSS 实现静态页面

2.1.2 JavaScript动态渲染页面

2.1.3 Vue + Element/Iview 急速前端页面开发

2.2 后端技术栈

2.2.1 Spring框架

2.2.2 SpringMVC框架

2.2.3 Mybatis/JPA框架

2.2.4 SpringBoot框架

1. 前言

1.1 java-web说明

web的概念摘自百度百科,阐述如下:

World Wide Web,简称web,意思是:全球广域网,我们也称之为万维网。它是一种基于超文本和HTTP的、全球性的、动态交互的、跨平台的分布式图形信息系统。

简单来说,web就是一个网站。通过在浏览器地址栏输入指向该网站的URL,我们可以从该网站获取或上传相关的资源。如:淘宝/京东/知乎/微博等等的官网都可以认为是一个网站。
java-web项目,就是以主体编程语言是java,利用其他各种编程语言实现的辅助中间件,实现的网站项目。

1.2 java-web前后端分离

前后端分离已经是互联网项目开发的业界标准使用方式。
其核心思想是:前端HTML页面通过AJAX调用后端的Restful API接口,并统一使用JSON格式进行数据的交互。
把一个web项目切分成前端项目和后端项目的好处:

  • 术业有专攻。

后端java工程师对服务的追求是:高并发、高可用、高性能

这样后端java工程师就可以把更多的精力放在学习java基础,设计模式,jvm原理,spring+springmvc原理及源码,linux,mysql事务隔离与锁机制,http/tcp,多线程,分布式架构,弹性计算架构,微服务架构,java性能优化等等方面。

前端工程师对服务的追求是:页面表现,速度流畅,兼容性

这样前端工程师就可以把更多的精力放在学习html5,css3,jquery,angularjs,bootstrap,reactjs,vuejs,webpack,less/sass,gulp,nodejs,Google V8引擎,javascript多线程,模块化,面向切面编程,设计模式,浏览器兼容性,性能优化等等方面。

  • 耦合低,职权明确,开发速度快。

在开需求会议时,由前端工程师和后端工程师商议并统计要实现的接口。需要按照统一的标准,将每个接口的请求参数、方法、路径以及返回值都确定下来,并汇总成最终的接口文档。

前端工程师只需要关注页面以及路由的实现,后端工程师只需要关注接口实现以及性能优化。两者可以基于接口文档并行开发。而且这种开发方式在后期测试以及上线后,更有利于问题的排查定位。

  • 可扩展性强。

前端服务器使用nginx,后端服务器使用tomcat。前端的css/js/图片等静态资源可以放在特定的文件服务器上(如阿里的oss服务器)并使用cdn加速。然后,除接口以外的所有http请求全部会转移到nginx上,能够极大地减少后端服务器的压力。后端服务也可以按数据库、缓存、消息队列、应用切割等方式进行拆分并发布到不同的服务器上,能够降低单个节点的服务器宕机造成的影响。

按我自己的理解,前后端分离之后,

  • 前端工程师主要负责的工作就是:静态页面及路由的实现,以事件驱动的动态页面渲染实现
  • 后端工程师主要负责的工作就是:合理切分业务并建库建表,相关接口、初始化任务、定时任务的实现

1.3 java-web的开发生命周期

我理解的web开发迭代流程图如下:

浅谈java-web开发技术栈_第1张图片
java-web开发生命周期详解如下:

  • 由产品经理与客户进行沟通,确定客户需要实现的功能并撰写相关的需求文档;
  • 基于需求文档,UI设计师与产品经理沟通协商后,UI设计师会设计出相应的UI原型。产品经理会基于UI原型再次跟客户沟通,确定是否满足需求,待UI设计师完善后,确定最终的UI原型。
  • 项目经理基于UI原型,组织产品经理、UI设计师、前端工程师、后端工程师进行需求会议,确定接口文档的撰写规范。然后,由前端工程师和后端工程师协商撰写接口文档。
  • 前端工程师基于UI原型和接口文档进行页面以及路由的开发,同时后端工程师基于接口文档进行业务的切分建库建表,并进行相关接口、初始化任务、定时任务的实现。
  • 确定前后端功能均开发完善后,由测试工程师进行相关功能的测试。基于测试过程中出现的问题,前后端需要进行相关问题的修复解决,确保测试正常。
  • 测试通过后,由运维工程师发布上线新服务,并时刻监测优化服务的相关配置参数。

实际的开发过程可能不会这么规范,但java-web项目的开发迭代生命周期,基本都是按照这个流程进行的。

2. 技术栈

2.1 前端技术栈

2.1.1 HTML + CSS 实现静态页面

  • Hyper Text Mark-up Language,超文本标记语言,简写为HTML。
  • Cascading Style Sheet,层叠样式表,简写为CSS。
    htmlcss是网页的基础。
    html包含的是一系列统一规定的标准标签,用来定义网页的基础布局。如我们最常见的元素:块级元素
    标签和行内元素标签。
    css就是为了将页面内容与页面表现区分开来的,通过选中html中的某块内容来按某种要求显示。其中最重要的是要理解盒子模型选择器部分样式定义方法

简单用实际的案例说明如下:
新建一个文件1.html并编辑内容如下:


  
    
我是块级元素div,我会独占一行
-------我是行内元素span1,可与其他行内元素共同占用1行,长度自适应并会自动换行------- -------我是行内元素span2,可与其他行内元素共同占用1行,长度自适应并会自动换行------- -------我是行内元素span3,可与其他行内元素共同占用1行,长度自适应并会自动换行-------

将该文件在浏览器中打开的效果如下:

浅谈java-web开发技术栈_第2张图片

如上图所示:

  • 是块级元素,其中的内容会独占一行。是行内元素,多个行内元素会共占1行,并且其长度自适应并会自动换行。
  • 行内元素span2定义了属性id值为test,通过css的id选择器选中该元素并设置了字体大小为18px,字体颜色为红色。

2.1.2 JavaScript动态渲染页面

JavaScript是一门脚本编程语言。
这里简单谈一下我对JavaScript的理解。它在浏览器主要做的事情有:监听浏览器网页的各种事件,基于各种事件的回调发起http请求或者直接动态渲染DOM(html中的某个元素)。

简单用实际的案例说明如下:
新建一个文件2.html并编辑内容如下:

<html>
  	<body>
   		<button onClick="toRed()">变红button>
   		<button onClick="toBlue()">变蓝button>
   		<p id="test">未点击前的DOMp>
  	body>
	<script>
		function toRed() {
			document.getElementById('test').innerHTML = "
点击了变红按钮
"
} function toBlue() { document.getElementById('test').innerHTML = "
点击了变蓝按钮
"
}
script> html>

将该文件在浏览器中打开的效果如下:

浅谈java-web开发技术栈_第3张图片

如上图所示:

  • 页面中有两个按钮。这两个按钮分别绑定了两个不同的点击事件。
  • 变红按钮绑定的点击回调事件是toRed(),其处理内容是:通过全局文档对象document查找到id为testdom元素,并将其内部的html修改为“点击了变红按钮”。
  • 变蓝按钮绑定的点击回调事件是toBlue(),其处理内容是:通过全局文档对象document查找到id为testdom元素,并将其内部的html修改为“点击了变蓝按钮”。

使用javascript在浏览器/客户端编程(CS/BS中的Client/Browser端)时,我们能够监听用户操作触发的事件,基于预先定义好的事件回调处理,动态地修改页面的内容和样式。

javascript中最重要的一个技术点是AJAX。客户端可以使用AJAX技术向服务端发送HTTP请求。

相关的基础代码如下:

// 创建XMLHttpRequest对象
var request = new XMLHttpRequest();

// 当http请求的状态发生变化时,会调用onreadystatechange()方法
request.onreadystatechange = function(){
  // 当readyStatue = 4且status = 200时,表示http请求成功完成
  if (request.readyState === 4 && request.status === 200) {
    // 获取http请求返回的字符串类型的响应文本
    var response = request.responseText;
    // 通过json解析后,将响应文本的值填充到页面对应的元素中
    var jsonObj = JSON.parse(response);
    // todo 将响应文本解析之后的内容填充到页面中
  }  
}
// 规定http请求的的类型、路径、是否异步
request.open('method_type','url',async);
// 如果是post请求提交表单数据,需要设置请求头
request.setRequestHeader("Content-type","application/x-www-form-urlencoded");
// 发送http请求
request.send();

简单说明一下:
我们可以利用XMLHttpRequest对象实例发送HTTP请求。其中主要用到该实例的三个方法:open(),send()onreadystatechange()

  • open()用来指定发起HTTP请求的方法类型、路径和是否异步。
  • send()用来真正发送HTTP请求。
  • onreadystatechange(),当该HTTP请求的状态发生改变时,会回调该方法。我们可以利用该实例判断服务器是否正确处理该请求(状态码为200时),如果正确响应则可将返回值解析后填充到当前页面的某个标签中。

2.1.3 Vue + Element/Iview 急速前端页面开发

当前主流的三大前端开发框架是:React,Vue,Angular。其中最好入门的就是Vue框架。熟读一遍Vue官方文档,理解它的这种数据与页面双向绑定的概念,实际上手写几个前端小项目就可入门。
ElementIview是两个非常常用的UI组件库。在项目中引入相关依赖包后,可以直接使用相关组件快速进行前端页面的开发。
ElementUI的官方文档详见element中文官方文档。
IviewUI的官方文档详见iview中文官方文档。
简单使用ElementUI提供的组件,查看其常用组件的样式效果如下:

新建一个文件3.html并编辑内容如下:

<html>
  <head>
    
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
  head>
  <body>
    <div id="app">
      <el-input placeholder="Element输入框">el-input>
      <el-divider>Element分割线el-divider>
      <el-button type="primary" @click="visible = true">Element按钮el-button>
      <el-dialog :visible.sync="visible" title="Element对话框">
        <p>对话框内部内容p>
      el-dialog>
    div>
  body>
  
  <script src="https://unpkg.com/vue/dist/vue.js">script>
  
  <script src="https://unpkg.com/element-ui/lib/index.js">script>
  <script>
    new Vue({
      el: '#app',
      data: function() {
        return { visible: false }
      }
    })
  script>
html>

将该文件在浏览器中打开的效果如下:

浅谈java-web开发技术栈_第4张图片

2.2 后端技术栈

2.2.1 Spring框架

基于百度百科查看可知:Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。

控制反转:Inversion of Control,简单来说就是把对象生命周期的管理交给Spring容器。诸如:数据库连接池对象、Redis连接池对象、RabitMQ连接池对象、Quartz定时任务调度对象等等,这些“大”对象的创建和销毁是一个很损耗资源的操作。我们可以通过XMLJavaConfig的方式把这些对象交给Spring容器管理。如果需要使用时,通过依赖注入(DI:Dependency Injection)的方式拿到这些对象的实例即可。这些对象的创建和回收都会有Spring容器帮我们处理。注:Spring容器管理的对象都是单例的。

下面以Spring容器加载quartz相关对象的实例做出说明:

  1. 首先在pom.xml文件中导入必要依赖如下:
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-context-supportartifactId>
    <version>4.3.7.RELEASEversion>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-jdbcartifactId>
    <version>4.3.7.RELEASEversion>
dependency>
<dependency>
    <groupId>org.quartz-schedulergroupId>
    <artifactId>quartzartifactId>
    <version>2.2.3version>
dependency>
  1. 运行定义打印当前时间的工作类TimePrinterJob内容如下:
public class TimePrinterJob {
    private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");

    public void printTime() {
        System.out.println("测试spring-xml配置quartz定时任务 TimePrinterJob.printTime() " + sdf.format(new Date()));
    }
}
  1. 以XML方式使Spring容器管理quartz相关的实例bean,配置文件spring-quartz.xml内容如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="timePrinterJob" class="com.netopstec.spring_quartz.TimePrinterJob"/>
    <bean id="printTimeJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="timePrinterJob"/>
        <property name="targetMethod" value="printTime"/>
    bean>
    <bean id="printTimeTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="printTimeJobDetail"/>
        <property name="cronExpression" value="/3 * * * * ?"/>
    bean>

    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="printTimeTrigger"/>
            list>
        property>
    bean>
beans>
  1. 实例化ClassPathXmlApplicationContext即启动Spring容器,最终效果如下:
    浅谈java-web开发技术栈_第5张图片

上述相关源码详见我的github
说明如下:
这里主要查看的是spring-quartz.xml的内容。很明显地能看到,Spring管理的quartz相关的实例,有三层包裹关系。

  • printTimeJobDetail指定了要交由quartz管理任务对应的方法即TimePrinterJob.printTime()
  • printTimeTrigger指定了该任务的触发器即执行时机为“每3秒执行一次”。
  • scheduler指定了要启用的触发器列表。
    此时,业务逻辑比较简单,我们还能够迅速地梳理这些业务对象实例的关联关系。当随着业务的拓展,业务之间的耦合关联关系越来越复杂,使用Spring容器帮我们管理这些业务对象实例就很有必要了。

面向切面:Aspect Orient Programming,简单来说,将正常的业务流程当作一个维度,要新增的功能与业务无关,所以放在另外一个维度(切面)里面进行处理实现。
如日志、全局异常处理等相关功能与业务功能无关,此时就需要用到面向切面编程。很明显地,面向切面编程的核心原理是动态代理。Java支持使用jdkcglib来实现动态代理,相关的内容这里就不再赘述。
这里就简单介绍一下面向切面编程的相关概念和一个SpringBoot简单切面编程案例

  • 切面:Aspect,整个额外功能的整体;
  • 切点:PointCut,要添加额外功能对应于正常业务流程所在维度的位置;
  • 连接点:JointPoint,通过连接点我们可以获取正常业务流程中的相关信息。
  • 通知:Adivce,有如下类型的通知:前置通知before、后置通知after,环绕通知around,异常通知AfterThrowing等等。

一个SpringBoot简单切面编程使用案例如下:

@Component
@Aspect
public class LogAspect {

    @Pointcut("execution(* com.netopstec.test.service.impl..*.*(..)")
    private void pointCut(){}

    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("调用业务方法之前,可以执行添加一些其他的功能");
        // 实际调用方法
        Object[] args = joinPoint.getArgs();
        Object proceed = joinPoint.proceed(args);
        System.out.println("调用业务方法之后,可以执行添加一些其他的功能");
        return proceed;
    }
}

上面这个小案例,介绍的是功能最强大的通知—环绕通知。它是扫描com.netopstec.test.service.impl下的所有业务方法,在系统执行业务方法joinPoint.proceed(args)的前后,添加自定义额外功能(这里只是在控制台打印两句话,可以根据实际情况添加一些自己想要的额外功能)。

2.2.2 SpringMVC框架

Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦。请求驱动,就是请求-响应这种设计模型。

相关的概念解释如下:

  • 模型,Model指的是封装了应用程序数据的POJO。
  • 视图,View指的是负责呈现模型数据的HTML页面。
  • 控制器,Controller负责处理用户的请求,处理数据并生成相应的模型,然后把生成好的模型传递给相应的视图解析器,由视图解析器解析生成相关的HTML页面。

SpringMVC执行流程图如下:

浅谈java-web开发技术栈_第6张图片

实际SpringMVC处理请求,主要是查看DispatcherServlet.doDispatch()源码的处理,先截取其部分关键源码如下:

public class DispatcherServlet extends FrameworkServlet {
    // ...

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
		ModelAndView mv = null;
        Object dispatchException = null;
        try {
	        processedRequest = this.checkMultipart(request);
	        // 基于请求request获取相应的处理执行链(责任链)
	        mappedHandler = this.getHandler(processedRequest);
	        if (mappedHandler == null || mappedHandler.getHandler() == null) {
	            this.noHandlerFound(processedRequest, response);
	            return;
	        }
	        // 获取相应的处理适配器
            HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
            // handle方法内部是调用实际的业务处理方法(动态代理)
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            // 获取对应的视图名称
            this.applyDefaultViewName(processedRequest, mv);
            // 处理拦截器定义的业务
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception var20) {
            dispatchException = var20;
        } catch (Throwable var21) {
            dispatchException = new NestedServletException("Handler dispatch failed", var21);
        }
        // 加载模型,渲染视图
        this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
	}

  // ...
}

基于源码和执行流程图,详解流程步骤如下:

  • 客户端或浏览器发起请求到前端控制器DispatcherServlet
  • 前端控制器请求处理器映射器HandlerMapping,查找出相应的处理执行链对象HandlerExecutionChain

项目启动加载时会初始化处理器映射器。它是一个包含所有业务对应的处理执行链的容器((一个处理执行链肯定会包含如业务标识URI、业务方法名、业务拦截器列表等信息)。这个处理执行链对象可能会包含多个HandlerInterceptor拦截器对象。真正执行业务方法时,会逐步执行拦截器。这是典型的责任链设计模式,处理器执行对象拦截器对象可以自行添加。

  • 前端控制器基于处理执行链对象HandlerExecutionChain,获取相应的处理器适配器对象HandlerAdapter
  • 基于处理器适配器HandlerAdapter.handle()去执行真正的业务方法,并返回模型视图对象ModelAndView(业务方法的执行,这里很明显使用的就是动态代理设计模式。ModelAndView是springmvc框架的一个底层对象,包括Modelview
  • 前端控制器将模型视图对象ModelAndView传递给视图解析器,由视图解析器进行视图解析 (根据逻辑视图名解析成真正的视图(jsp))并返回视图View,通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可
  • 前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)后,响应用户。

实际测试SpringMVC进行业务解耦的源码内容如下(可以自行debug测试,核对SpringMVC执行流程):

  1. 首先在pom.xml文件中导入必要依赖如下:
<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-webmvcartifactId>
  <version>4.3.7.RELEASEversion>
dependency>
<dependency>
  <groupId>org.projectlombokgroupId>
  <artifactId>lombokartifactId>
  <version>1.16.8version>
dependency>
<dependency>
  <groupId>com.fasterxml.jackson.coregroupId>
  <artifactId>jackson-databindartifactId>
  <version>[2.9.10.1,)version>
dependency>
  1. 除了设置web.xml的内容,还需要在spring-mvc-servlet.xml中定义视图解析器并开启注解驱动;

<beans>
  <mvc:annotation-driven/>

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

    <property name="suffix">
      <value>.jspvalue>
    property>
  bean>
beans>
  1. 基于视图解析器的配置,在目录/WEB-INF/jsp/下定义视图hello.jsp如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>


    Title


姓名:${user.name}

花名:${user.nick}

年龄:${user.age}

爱好:${user.hobby}

  1. 定义好业务方法如下:
@Controller
@RequestMapping("/users")
public class UserPageController {

    @RequestMapping("/page")
    public ModelAndView showPage(){

        User user = new User();
        user.setName("戴振焱");
        user.setNick("zhenye");
        user.setAge(27);
        user.setHobby("动漫、炉石");
        ModelAndView modelAndView = new ModelAndView();
        // 处理并生成相应的模型user
        modelAndView.addObject(user);

        //指定视图
        modelAndView.setViewName("hello");
        return modelAndView;
    }
}
@RestController
@RequestMapping("/users")
public class UserJsonController {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    @GetMapping(value = "/json", produces="text/html;charset=UTF-8")
    public String showJson() {
        User user = new User();
        user.setName("戴振焱");
        user.setNick("zhenye");
        user.setAge(27);
        user.setHobby("动漫、炉石");
        String userInfo = null;
        try {
            userInfo = OBJECT_MAPPER.writeValueAsString(user);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return userInfo;
    }
}

由于开启了注解驱动,Spring容器会自动检测所有的类注解@Controller@RestController
@Controller表示需要使用视图解析器ViewResolver,其所有方法的返回值都是映射/WEB-INF/jsp/目录下查找所有的视图(如这里的viewNamehello,就是去找视图 /WEB-INF/jsp/ + hello + .jsp)。
@RestController则表示不使用视图解析器,直接给用户返回相应格式的数据(这里的json字符串)。

  1. war包并部署到tomcat容器中后,测试接口的效果图如下:

浅谈java-web开发技术栈_第7张图片

上述相关源码详见我的github

2.2.3 Mybatis/JPA框架

MybatisJPA是两种ORMObject/Relational Mapping,对象关系映射)的框架。简单来说,这两个框架来维护java-bean与数据表的关系,开发人员只需关注业务逻辑本身。
这两个框架各有优缺点。

  • JPAJava Persistence API及Java持久层API。它是一种java持久层的实现规范。引入该框架后,开发人员可以不用写SQL语句,利用该框架提供的API直接操作数据库表。
  • Mybatis,其前身是ibatis。它支持定制化 SQL、存储过程以及高级映射。
    这两个框架优劣势及其明显。如果项目业务简单且后期扩展功能较少,推荐选用JPA。绝大部分业务都是单表操作,使用该框架可以极大地提升开发效率。否则最好选用Mybatis框架,其支持编写原生SQL语句,更加灵活,后期扩展功能时更易实现。

Mybatis框架为例,简单介绍如何使用这些ORM框架。

  1. 首先,在pom.xml文件中导入必须的依赖如下:
<dependencies>
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-jdbcartifactId>
      <version>4.3.7.RELEASEversion>
    dependency>
    <dependency>
      <groupId>org.mybatisgroupId>
      <artifactId>mybatisartifactId>
      <version>3.4.2version>
    dependency>
    <dependency>
      <groupId>mysqlgroupId>
      <artifactId>mysql-connector-javaartifactId>
      <version>6.0.6version>
    dependency>
    <dependency>
      <groupId>org.projectlombokgroupId>
      <artifactId>lombokartifactId>
      <version>1.16.8version>
    dependency>
  dependencies>
  
  <build>
    <resources>
      <resource>
        <directory>src/main/javadirectory>
        <includes>
          <include>**/*.xmlinclude>
        includes>
        <filtering>truefiltering>
      resource>
    resources>
  build>
  1. 新建数据库表以及该表对应的实体类

建库建表语句如下:

CREATE DATABASE IF NOT EXISTS easy-mybatis;
USE easy-mybatis;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) DEFAULT NULL,
  `nick` varchar(32) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `hobby` text,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

user表对应的实体类如下:

@Getter
@Setter
@ToString
public class User {
  private Long id;
  private String name;
  private String nick;
  private Integer age;
  private String hobby;
}
  1. 编辑Mybatis的配置文件mybatis-config.xml的内容如下:


<configuration>
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/easy-mybatis?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            dataSource>
        environment>
    environments>
    <mappers>
        <package name="com/netopstec/easy_mybatis/mapper"/>
    mappers>
configuration>
  1. 编辑接口以及对应的映射XMl文件内容如下:

接口层代码如下:

public interface UserMapper {
  List<User> selectAll();
  int insertOne(User user);
}

对应映射文件内容如下:



<mapper namespace="com.netopstec.easy_mybatis.mapper.UserMapper">
    <select id="selectAll" resultType="com.netopstec.easy_mybatis.entity.User">
        SELECT * FROM `user`
    select>

    <insert id="insertOne" parameterType="com.netopstec.easy_mybatis.entity.User">
        INSERT INTO `user` (id, name, nick, age, hobby)
        VALUES (#{id}, #{name}, #{nick}, #{age}, #{hobby})
    insert>
mapper>
  1. 测试代码如下:
public class MybatisTest {

    public static void main(String[] args) throws IOException {
        InputStream reader = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.selectAll();
        System.out.println("新增之前,user表数据:" + userList);
        User user = new User();
        user.setName("戴振焱");
        user.setNick("zhenye");
        user.setAge(27);
        user.setHobby("动漫,炉石");
        userMapper.insertOne(user);
        userList = userMapper.selectAll();
        System.out.println("新增之后,user表数据:" + userList);

    }
}

最终的测试效果图如下:

浅谈java-web开发技术栈_第8张图片

上述相关源码详见我的github

2.2.4 SpringBoot框架

SpringBoot框架的核心思想是:“约定优于配置”。
整合一个基于Java-Spring开发处理的项目,一般都需要进行非常多的额外配置。如上面需要使用Mybatis框架时,需要新建并完善mybatis-config.xml配置文件中的内容。这对于对该框架或其配置项不熟的新手来说,非常的不友好。
SpringBoot就对一些常用的第三方工具包的常用配置进行了整合。基于SpringBoot框架开发项目,当导入SpringBoot整合后提供的依赖包(官方项目常常以spring-boot-starter开头)后,它会为我们开发的项目添加一下默认的配置。如上面的数据源的配置,我们可以导入spring-boot-starter-jdbc对应的依赖包,在配置文件中添加必要的四个配置(spring.datasource.url,spring.datasource.driver-class-name,spring.datasource.username,spring.datasource.password)即可使用。
当前SpringBoot版本支持自动配置的第三方工具参考源码spring.factories内容,如下图:

浅谈java-web开发技术栈_第9张图片

现以开发一个自定义的starter项目为例,进行说明。

假设有一个日常作息时间表如下:

默认时间节点 事项 对应配置项字段
08:00 起床时间/睡觉结束时间 sleepTimeEnd
08:30 早餐开始时间 breakfastTimeStart
09:00 早餐结束时间 breakfastTimeEnd
12:00 午餐开始时间 luntchTimeStart
12:30 午餐结束时间 luntchTimeEnd
19:00 晚餐开始时间 dinnerTimeStart
19:30 晚餐结束时间 dinnerTimeEnd
00:00 睡觉开始时间 sleepTimeStart

现在开发一个相应项目plan-spring-boot-starter,需要该项目提供如下功能:

  • formatPlanList(),打印现在的作息时间表。
  • doing(Date time),确认按照作息时间,该时间点正在做什么。
  • nextToDo(Date time),确认按照作息时间,该时间点下一步要做的什么。
  • 所有事项对应的时间节点都可以自行配置。

新建一个maven项目—plan-spring-boot-starter,其最终目录如下:

浅谈java-web开发技术栈_第10张图片

  • 首先,在pom.xml文件中导入spring-boot自动配置实现必要的依赖包如下:
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-autoconfigureartifactId>
    <version>1.5.2.RELEASEversion>
dependency>
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-configuration-processorartifactId>
    <version>1.5.2.RELEASEversion>
    <optional>trueoptional>
dependency>
  • 然后,编辑可自动配置的配置类PlanProperties如下:
@Getter
@Setter
@ConfigurationProperties(prefix = "spring.plan")
public class PlanProperties {

    /**
     * 起床时间/睡觉结束时间
     */
    private String sleepTimeEnd = "08:00";

    /**
     * 早餐开始时间
     */
    private String breakfastTimeStart = "08:30";

    /**
     * 早餐结束时间
     */
    private String breakfastTimeEnd = "09:00";

    /**
     * 午餐开始时间
     */
    private String lunchTimeStart = "12:00";

    /**
     * 午餐结束时间
     */
    private String lunchTimeEnd = "12:30";

    /**
     * 晚餐开始时间
     */
    private String dinnerTimeStart = "19:00";

    /**
     * 晚餐结束时间
     */
    private String dinnerTimeEnd = "19:30";

    /**
     * 睡觉开始时间
     */
    private String sleepTimeStart = "00:00";
}
  • 然后,编写实现了上述三个功能(打印现在的作息时间表formatPlanList(),确认当前正在做什么doing(Date time),确认下一步要做什么nextToDo(Date time))的业务功能类PlanService如下:
public class PlanService {

    private PlanProperties planProperties;

    public PlanService(){}

    public PlanService(PlanProperties planProperties) {
        this.planProperties = planProperties;
    }

    public String formatPlanList() {
        // ... //
        return plan;
    }

    public String doing(Date date) {
        // ... //
        return doingThing;
    }

    public String nextTodo(Date date) {
        // ... //
        return nextTodo;
    }
}
  • 编辑由Spring容器帮我们管理的自动配置类PlanServiceAutoConfiguration如下:
@Configuration
@EnableConfigurationProperties(PlanProperties.class)
@ConditionalOnClass(PlanService.class)
@ConditionalOnProperty(prefix = "spring.plan", value = "enabled", matchIfMissing = true)
public class PlanServiceAutoConfiguration {
    @Autowired
    private PlanProperties properties;

    @Bean
    @ConditionalOnMissingBean(PlanService.class)
    public PlanService planService(){
        PlanService planService = new PlanService(properties);
        return planService;
    }
}
  • 最后,确认让SpringBoot加载自动配置类,编辑文件spring.factories如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.netopstec.plan.PlanServiceAutoConfiguration

执行mvn clean install命令,对该自动配置项目进行打包即可。

新建一个测试该自动配置的SpringBoot项目—test-for-plan,然后在pom.xml导入上述自动配置依赖包如下图:

浅谈java-web开发技术栈_第11张图片

启动该测试项目,执行测试代码效果图如下:

浅谈java-web开发技术栈_第12张图片
很明显地能够看到,注入业务功能类PlanService后,可以直接使用该业务功能类的各项功能方法。而且对应的配置项就是默认值。

在SpringBoot配置文件,修改这些配置项的内容(如:将早餐结束时间修改为"08:45",午餐结束时间修改为"12:15"),如下:

浅谈java-web开发技术栈_第13张图片

再次执行测试方法,效果如下:

浅谈java-web开发技术栈_第14张图片
很明显地看到,这个项目实现了自动配置的功能。而且也符合实际使用自动配置的逻辑。在项目配置文件中中新加了配置项内容后,其内容会覆盖配置项的默认值。

上述相关源码详见:

  • 自动配置项目plan-spring-boot-starter
  • 自动配置测试项目test-for-plan

你可能感兴趣的:(Java基础,Vue框架,SpringBoot)