浅谈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 Mybat
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
说明
java-web
说明
web
的概念摘自百度百科,阐述如下:
World Wide Web
,简称web
,意思是:全球广域网,我们也称之为万维网。它是一种基于超文本和HTTP的、全球性的、动态交互的、跨平台的分布式图形信息系统。
简单来说,web
就是一个网站。通过在浏览器地址栏输入指向该网站的URL,我们可以从该网站获取或上传相关的资源。如:淘宝/京东/知乎/微博等等的官网都可以认为是一个网站。
java-web
项目,就是以主体编程语言是java
,利用其他各种编程语言实现的辅助中间件,实现的网站项目。
1.2
java-web
前后端分离
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
的开发生命周期
java-web
的开发生命周期
我理解的web开发迭代流程图如下:
java-web开发生命周期详解如下:
- 由产品经理与客户进行沟通,确定客户需要实现的功能并撰写相关的需求文档;
- 基于需求文档,UI设计师与产品经理沟通协商后,UI设计师会设计出相应的UI原型。产品经理会基于UI原型再次跟客户沟通,确定是否满足需求,待UI设计师完善后,确定最终的UI原型。
- 项目经理基于UI原型,组织产品经理、UI设计师、前端工程师、后端工程师进行需求会议,确定接口文档的撰写规范。然后,由前端工程师和后端工程师协商撰写接口文档。
- 前端工程师基于UI原型和接口文档进行页面以及路由的开发,同时后端工程师基于接口文档进行业务的切分建库建表,并进行相关接口、初始化任务、定时任务的实现。
- 确定前后端功能均开发完善后,由测试工程师进行相关功能的测试。基于测试过程中出现的问题,前后端需要进行相关问题的修复解决,确保测试正常。
- 测试通过后,由运维工程师发布上线新服务,并时刻监测优化服务的相关配置参数。
实际的开发过程可能不会这么规范,但java-web项目的开发迭代生命周期,基本都是按照这个流程进行的。
2. 技术栈
2.1 前端技术栈
2.1.1
HTML
+
CSS
实现静态页面
HTML
+
CSS
实现静态页面
Hyper Text Mark-up Language
,超文本标记语言,简写为HTML。Cascading Style Sheet
,层叠样式表,简写为CSS。
html
和css
是网页的基础。
html
包含的是一系列统一规定的标准标签,用来定义网页的基础布局。如我们最常见的元素:块级元素<div>
标签和行内元素<span>
标签。
css
就是为了将页面内容与页面表现区分开来的,通过选中html
中的某块内容来按某种要求显示。其中最重要的是要理解盒子模型
,选择器
和部分样式定义方法
。
简单用实际的案例说明如下:
新建一个文件1.html
并编辑内容如下:
<html>
<body>
<div>
我是块级元素div,我会独占一行
</div>
<span>
-------我是行内元素span1,可与其他行内元素共同占用1行,长度自适应并会自动换行-------
</span>
<span id="test">
-------我是行内元素span2,可与其他行内元素共同占用1行,长度自适应并会自动换行-------
</span>
<span>
-------我是行内元素span3,可与其他行内元素共同占用1行,长度自适应并会自动换行-------
</span>
<style>
#test {
font-size: 18px;
color: red;
}
</style>
</body>
</html>
将该文件在浏览器中打开的效果如下:
如上图所示:
<div>
是块级元素,其中的内容会独占一行。<span>
是行内元素,多个行内元素会共占1行,并且其长度自适应并会自动换行。- 行内元素span2定义了属性id值为test,通过css的id选择器选中该元素并设置了字体大小为18px,字体颜色为红色。
2.1.2
JavaScript
动态渲染页面
JavaScript
动态渲染页面
JavaScript
是一门脚本编程语言。
这里简单谈一下我对JavaScript
的理解。它在浏览器主要做的事情有:监听浏览器网页的各种事件,基于各种事件的回调发起http请求
或者直接动态渲染DOM
(html
中的某个元素)。
简单用实际的案例说明如下:
新建一个文件2.html
并编辑内容如下:
<html>
<body>
<button onClick="toRed()">变红</button>
<button onClick="toBlue()">变蓝</button>
<p id="test">未点击前的DOM</p>
</body>
<script>
function toRed() {
document.getElementById('test').innerHTML = "<div style=\"color:red;\">点击了变红按钮</div>"
}
function toBlue() {
document.getElementById('test').innerHTML = "<div style=\"color:blue;\">点击了变蓝按钮</div>"
}
</script>
</html>
将该文件在浏览器中打开的效果如下:
如上图所示:
- 页面中有两个按钮
<button>变红</button>
,<button>变蓝</button>
。这两个按钮分别绑定了两个不同的点击事件。 - 变红按钮绑定的点击回调事件是
toRed()
,其处理内容是:通过全局文档对象document
查找到id为test
的dom
元素,并将其内部的html
修改为“点击了变红按钮”。 - 变蓝按钮绑定的点击回调事件是
toBlue()
,其处理内容是:通过全局文档对象document
查找到id为test
的dom
元素,并将其内部的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
急速前端页面开发
Vue
+
Element/Iview
急速前端页面开发
当前主流的三大前端开发框架是:React
,Vue
,Angular
。其中最好入门的就是Vue
框架。熟读一遍Vue官方文档,理解它的这种数据与页面双向绑定
的概念,实际上手写几个前端小项目就可入门。
Element
和Iview
是两个非常常用的UI组件库。在项目中引入相关依赖包后,可以直接使用相关组件快速进行前端页面的开发。
ElementUI
的官方文档详见element中文官方文档。
IviewUI
的官方文档详见iview中文官方文档。
简单使用ElementUI
提供的组件,查看其常用组件的样式效果如下:
新建一个文件3.html
并编辑内容如下:
<html>
<head>
<!-- import CSS -->
<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>
<!-- import Vue before Element -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script>
new Vue({
el: '#app',
data: function() {
return { visible: false }
}
})
</script>
</html>
将该文件在浏览器中打开的效果如下:
2.2 后端技术栈
2.2.1
Spring
框架
Spring
框架
基于百度百科查看可知:Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。
控制反转:Inversion of Control,简单来说就是把对象生命周期的管理交给Spring容器。诸如:数据库连接池对象、Redis连接池对象、RabitMQ连接池对象、Quartz定时任务调度对象等等,这些“大”对象的创建和销毁是一个很损耗资源的操作。我们可以通过XML
和JavaConfig
的方式把这些对象交给Spring容器管理。如果需要使用时,通过依赖注入(DI:Dependency Injection)的方式拿到这些对象的实例即可。这些对象的创建和回收都会有Spring容器帮我们处理。注:Spring容器管理的对象都是单例的。
下面以Spring容器加载quartz相关对象的实例
做出说明:
- 首先在
pom.xml
文件中导入必要依赖如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
- 运行定义打印当前时间的工作类
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()));
}
}
- 以XML方式使Spring容器管理quartz相关的实例bean,配置文件
spring-quartz.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"
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>
- 实例化
ClassPathXmlApplicationContext
即启动Spring容器,最终效果如下:
上述相关源码详见我的github
说明如下:
这里主要查看的是spring-quartz.xml
的内容。很明显地能看到,Spring管理的quartz相关的实例,有三层包裹关系。
printTimeJobDetail
指定了要交由quartz管理任务对应的方法即TimePrinterJob.printTime()
。printTimeTrigger
指定了该任务的触发器即执行时机为“每3秒执行一次”。scheduler
指定了要启用的触发器列表。
此时,业务逻辑比较简单,我们还能够迅速地梳理这些业务对象实例的关联关系。当随着业务的拓展,业务之间的耦合关联关系越来越复杂,使用Spring容器帮我们管理这些业务对象实例就很有必要了。
面向切面:Aspect Orient Programming,简单来说,将正常的业务流程当作一个维度,要新增的功能与业务无关,所以放在另外一个维度(切面)里面进行处理实现。
如日志、全局异常处理等相关功能与业务功能无关,此时就需要用到面向切面编程。很明显地,面向切面编程的核心原理是动态代理。Java支持使用jdk
或cglib
来实现动态代理,相关的内容这里就不再赘述。
这里就简单介绍一下面向切面编程的相关概念和一个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
框架
SpringMVC
框架
Spring Web MVC
是一种基于Java的实现了Web MVC
设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦。请求驱动,就是请求-响应这种设计模型。
相关的概念解释如下:
- 模型,
Model
指的是封装了应用程序数据的POJO。 - 视图,
View
指的是负责呈现模型数据的HTML页面。 - 控制器,
Controller
负责处理用户的请求,处理数据并生成相应的模型,然后把生成好的模型传递给相应的视图解析器,由视图解析器解析生成相关的HTML页面。
SpringMVC执行流程图如下:
实际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框架的一个底层对象,包括Model
和view
) - 前端控制器将模型视图对象
ModelAndView
传递给视图解析器,由视图解析器进行视图解析 (根据逻辑视图名解析成真正的视图(jsp))并返回视图View
,通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可 - 前端控制器进行视图渲染 (视图渲染将模型数据(在
ModelAndView
对象中)填充到request域)后,响应用户。
实际测试SpringMVC进行业务解耦
的源码内容如下(可以自行debug测试,核对SpringMVC执行流程):
- 首先在
pom.xml
文件中导入必要依赖如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>[2.9.10.1,)</version>
</dependency>
- 除了设置
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>.jsp</value>
</property>
</bean>
</beans>
- 基于视图解析器的配置,在目录
/WEB-INF/jsp/
下定义视图hello.jsp
如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>姓名:${user.name}</p>
<p>花名:${user.nick}</p>
<p>年龄:${user.age}</p>
<p>爱好:${user.hobby}</p>
</body>
</html>
- 定义好业务方法如下:
@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;
}
}
由于开启了注解驱动<mvc:annotation-driven/>
,Spring容器会自动检测所有的类注解@Controller
和@RestController
。
@Controller
表示需要使用视图解析器ViewResolver
,其所有方法的返回值都是映射/WEB-INF/jsp/
目录下查找所有的视图(如这里的viewName
是hello
,就是去找视图 /WEB-INF/jsp/
+ hello
+ .jsp
)。
@RestController
则表示不使用视图解析器,直接给用户返回相应格式的数据(这里的json字符串)。
- 打
war
包并部署到tomcat容器中后,测试接口的效果图如下:
上述相关源码详见我的github
2.2.3
Mybatis/JPA
框架
Mybatis/JPA
框架
Mybatis
和JPA
是两种ORM
(Object/Relational Mapping
,对象关系映射)的框架。简单来说,这两个框架来维护java-bean与数据表的关系,开发人员只需关注业务逻辑本身。
这两个框架各有优缺点。
JPA
,Java Persistence API
及Java持久层API。它是一种java持久层的实现规范。引入该框架后,开发人员可以不用写SQL语句,利用该框架提供的API直接操作数据库表。Mybatis
,其前身是ibatis
。它支持定制化 SQL、存储过程以及高级映射。
这两个框架优劣势及其明显。如果项目业务简单且后期扩展功能较少,推荐选用JPA
。绝大部分业务都是单表操作,使用该框架可以极大地提升开发效率。否则最好选用Mybatis
框架,其支持编写原生SQL语句,更加灵活,后期扩展功能时更易实现。
以Mybatis
框架为例,简单介绍如何使用这些ORM框架。
- 首先,在
pom.xml
文件中导入必须的依赖如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
</dependency>
</dependencies>
<!-- maven工程默认只加载java文件。要加载xml文件,必须添加如下额外配置 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
- 新建数据库表以及该表对应的实体类
建库建表语句如下:
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;
}
- 编辑Mybatis的配置文件
mybatis-config.xml
的内容如下:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<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>
- 编辑接口以及对应的映射XMl文件内容如下:
接口层代码如下:
public interface UserMapper {
List<User> selectAll();
int insertOne(User user);
}
对应映射文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<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>
- 测试代码如下:
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);
}
}
最终的测试效果图如下:
上述相关源码详见我的github
2.2.4
SpringBoot
框架
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
内容,如下图:
现以开发一个自定义的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
,其最终目录如下:
- 首先,在
pom.xml
文件中导入spring-boot自动配置实现必要的依赖包如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>1.5.2.RELEASE</version>
<optional>true</optional>
</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
导入上述自动配置依赖包如下图:
启动该测试项目,执行测试代码效果图如下:
很明显地能够看到,注入业务功能类PlanService
后,可以直接使用该业务功能类的各项功能方法。而且对应的配置项就是默认值。
在SpringBoot配置文件,修改这些配置项的内容(如:将早餐结束时间修改为"08:45",午餐结束时间修改为"12:15"),如下:
再次执行测试方法,效果如下:
很明显地看到,这个项目实现了自动配置的功能。而且也符合实际使用自动配置的逻辑。在项目配置文件中中新加了配置项内容后,其内容会覆盖配置项的默认值。
上述相关源码详见:
更多推荐
所有评论(0)