学习路线来自Handy前辈的规划。
技术栈暂定为SpringBoot+MySql。一段话前面加的星号*
表示这段话是我个人的理解。
一、理论知识
1. 复习Java基础知识。
略。
(接口interface,多线程,TCP编程,Daemon,Thread/Runnable,异常处理,注解annotation)
2. 理解前后端分离开发和不分离开发的区别。
前置知识:
脚本、脚本语言
脚本是为了重复一个设定好的规程。与编译型语言不同,脚本语言通常是解释型的,因此便于根据需求修改。在前端开发中,脚本语言一般是JS(JavaScript)。——知乎:如何用通俗易懂的语言解释脚本(script)是什么?
按我的理解,脚本就是小型的(至少最初是小型的?)、直接解释执行所以易修改的程序。
JSP
Jakarta Server Pages/JavaServer Pages的缩写。部署在服务器上,响应客户端的请求,并根据请求动态生成Web网页。以Java作为脚本语言,为HTTP请求提供服务,并能与服务器上其他Java程序共同处理业务需求。——维基百科 - JSP
JSP实际上是把Java代码嵌入HTML代码中。例如:
<html>
<head>
<title>Hello World - JSP</title>
</head>
<body>
<%-- JSP Comment --%>
<h1>Hello World!</h1>
<p>
<%
out.println("Your IP address is ");
%>
<span style="color:red">
<%= request.getRemoteAddr() %>
</span>
</p>
</body>
</html>
在.jsp文件中,<%
和%>
之间的代码是它与HTML最根本的区别。这些代码有三种:
<%--
与--%>
之间是注释。<%=
与%>
之间是一个变量,这个语句直接输出一个变量的值。- 其他情况就是普通的Java语句。这也是Java成为JSP的脚本语言的原因。
传统Java Web开发中,前端程序员完成HTML代码,后端程序员将其转为JSP再进行开发。如果后端遇到bug等问题,则难以与前端交流,因为前端看不懂后端用的JSP,而后端看不懂前端用的HTML;而且前端无法独自调试,因为页面的上线需要由后端程序员完成。
如果前端只负责数据展示和用户交互,后端只负责处理数据,前后端程序员约定好他们唯一的交流——接口的文档(参数、数据类型),然后独立并行开发,就能实现前后端解耦合,避免上述问题。——还搞不明白前后端分离?
*按我的理解,这与封装类似。两端都只需要处理接口处来的数据,而不需要管对方的细节,正如同调用封装好的函数不需要知道它的实现方式一样。当然,正如同有时候需要了解封装函数的内部细节一样,前后端可能也需要了解对方。
3. 理解JSON在前后端分离开发中的作用。
JSON
JavaScript Object Notation的缩写,即JavaScript对象表示法,用来传输由属性值或序列性的值组成的数据对象。虽然是JS的子集,但是是独立于语言的文本格式,即数据格式与语言无关。——维基百科 - JSON
JSON的文法:——json.org
*按我的理解,JSON是前后端交互的数据的形式,如同调用封装函数时的参数和返回值一样。对一端而言,另一端看起来只是大量JSON的流。
*由于JSON是语言无关的,并且现代编程语言基本都能支持array/vector/list/sequence等等与JSON中的array类似的数据形式之一,所以JSON很适合作为不同程序之间交互数据的形式。
4. 理解Tomcat/SpringMVC/Spring/SpringBoot/Servlet的关系。
Servlet
本质上,Servlet最狭义、最原始的意思是用Java语言实现的一个接口;广义地,Servlet指实现了这个接口的类。
——维基百科 - Java Servlet
既然是接口,它实际上就是一种规范,使得实现它的类都遵循这种规范。
——进击的算法
除了Servlet之外,还有ServletContext, ServletRequest, ServletResponse等诸多接口提供规范。
——简书 - servlet/tomcat等容器/springMVC之间的关系
既然是类,它也是可以运行在机器(服务器)上的程序。
——Introduction to Servlets and Servlet Containers
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package javax.servlet;
//在最新的Servlet API版本(Jakarta Servlet 5.0.0 M1)里,
//这个包被移动到了jakarta.servlet。
import java.io.IOException;
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
功能上,servlet是一个组件。它的功能很简单:接收请求,处理请求,发回响应。理论上,servlet可以通过任何客户端-服务器协议来对话,但最常处理的请求是HTTP协议的,所以servlet一词常常是对HTTP servlet的简称。
Servlet常被用来给一个web服务器添加动态内容,这些内容通常是HTML,但有时也可能是XML或者JSON。当输出HTML时,servlet的语句常常是这样:
PrintWriter pw = resp.getWriter();
pw.write("<html>");
pw.write("<body>");
pw.write("<h1>Welcome, " + name + "!</h1>");
pw.write("</body>");
pw.write("</html>");
pw.flush();
所以我们说,servlet把HTML语句嵌入Java代码中。
这和JSP正好相反。那么servlet和JSP之间是否有某种关系?
有。
JSP需要先编译成Java程序才能执行。比如上面举例JSP时的代码会被Tomcat编译为下面的样子:
package org.apache.jsp;
import ...
public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
...
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
...
out.write("<html>\n");
out.write("<head>\n");
out.write(" <title>Hello World - JSP</title>\n");
out.write("</head>\n");
out.write("<body>\n");
...
}
...
}
这实际上就是servlet。
Servlet处理来自客户端的请求,但并不直接与客户端互动。相反地,它(们)由容器管理,通过容器和web服务器进而和客户端互动。
容器
是一个组件和更底层的支持这个组件的功能之间的接口。——Java EE Containers
书中代言:
[A CONTAINER provides] an execution environment that is responsible for adding the technical concerns to the COMPONENTS…Conceptually, it wraps the COMPONENTS, thus giving clients the illusion of of tightly-integrated functional and technical concerns.
——Server Component Patterns by M.Volter, et al.
意思大概是说:容器把一些部件给包起来,为这些部件提供运行环境,对外提供一种功能和技术高度整合的假象。
Servlet容器是Java EE服务器中很常见的一种容器。它的内部运行着servlet,外部与web服务器对接。
——StackOverflow - Definition of a Java Container
*容器像是房间,而servlet是房间中的职员。
Servlet处理HTTP请求时的一个典型的事件序列如下:
- 客户端(例如一个web浏览器)访问web服务器,发出一个HTTP请求。
- web服务器收到请求,交给servlet容器。此处的容器可以和主机web服务器运行在同一个进程中,或者同一个主机上的不同进程中,或者不同主机上。
- 根据servlets的具体配置,servlet容器决定要调用哪个servlet,然后用代表着请求和响应的对象调用它。
- servlet根据请求对象查明用户是谁、请求里可能送来了哪些HTTP
POST
参数,以及其他相关数据,然后做其他事,最后生成数据发回客户端(通过响应对象)。 - servlet处理完请求后,容器妥善处置servlet,然后把控制权交给主机web服务器。
上面这段翻译自官方说明Jakarta Servlet Specification。顾名思义,该网站也对一个servlet应该实现的功能做了规范。
*第4步显然隐藏了许多细节,那些细节暂时不是我能考虑的……不过显然和上面代码中定义的void service(ServletRequest var1, ServletResponse var2)
有关。
待解决问题:容器使用servlet的过程;servlet的生命周期。
Tomcat
全称Apache Tomcat,是一个免费、开源的对Java Servlet, JSP, Java Expression Language和WebSocket技术的实现,也是最常用的servlet容器。
如前所述,Tomcat被用于管理servlet并为之提供接口。具体地,它代替程序员执行TCP编程、HTTP编解码等繁重的底层工作,解析客户端发起的请求,组装出HttpRequest
,创建HttpRespoonse
,将它们交给程序员编写的servlet处理。
需要注意的是,Tomcat代替servlet接收请求,但并不发回响应。响应是servlet自己发回的。
本来应该在此处继续了解Spring/SpringBoot/Spring MVC,但我看了一会发现,首先要知道框架是什么。
框架
框架之于应用开发,如同库之于功能实现。当大家需要砍很多树的时候,锯子(库)应运而生;当大家需要造很多桌子的时候,工厂(框架)应运而生。
框架既是一种规范,规定开发某种软件的基本组件;也是帮助程序员实现这种规范的工具,提供并实现最基础的软件架构。
但正如同桌子工厂难以制造椅子,锯子不能拧螺丝一样,String
类的函数不能建立哈希表,框架使得工作的灵活性减少。
YouTube - What is the difference between frameworks and libraries?
*这个视频里还说“人调用库,框架调用人”,没太明白这句话是什么意思。
Spring
Spring framework(Spring框架)是最受欢迎的Java应用开发框架。它提供的功能中有三种是主要的:
- 应用上下文(application context)与依赖注入(dependency injections)。这两个功能帮助程序员管理服务和对象之间的联系,在可以共享同一个实例时避免创建新的实例。
- 数据访问(data access)。有98.7%的Java应用需要建立数据库连接,因此提供这一功能的框架能大大提高工作效率。
- Spring MVC。更容易地开发web应用。
——YouTube - What is the Spring framework really all about?
*事实上,除了这三者之外,Spring框架还提供一些其他功能,如事务管理;除了Spring框架之外,Spring还有很多其他服务,如Spring Data/Spring Cloud/Spring Security等几十种,在官网Spring Projects上有列举。但我一时没法学这么多……姑且认为Handy前辈指的是Spring框架吧。
MVC是Java Web开发的一种模式,指的是在Web服务器上建立Model(模型层,如Java Bean)、View(视图层,如JSP)、Controller(控制器)这三个耦合度低、逻辑清晰的部分。
Spring MVC则是实现了MVC设计模式的一个Web框架,是Spring框架的一个部分。
*在组合使用Tomcat和Spring MVC之后,对于Tomcat而言,整个Spring MVC就像是一个servlet;而对于Spring MVC而言,Tomcat的意义只在于上图中的1.HTTP请求和8.HTTP响应,就像是浏览器一样,因为Tomcat创建了请求和响应各一个对象供Spring MVC来交互。
待解决问题:处理器,控制器。
Spring Boot
一个开源的基于Spring框架的微框架。
Spring框架提供了依赖注入,因而提供灵活性,但代价是开发者要给每个部件设置大量的XML配置语句。Spring Boot则相反,采用自动配置(可以手动重写),尽量减少代码长度,让开发、测试和部署更加便捷。随之而来的是开发者对程序的控制力的下降,这是Spring Boot的一个局限性。
——Stackify - What is Spring Boot?
Spring Boot中内置的web容器就是Tomcat。
*回到开始的问题。这几者的关系大概是Spring Boot>Spring>Tomcat/Spring MVC(大于表示包含),Tomcat管理Spring MVC,Spring MVC是一种/实现了servlet。
5. 理解Spring Boot,重点理解一个请求从接收到返回会经过哪些流程,以及Spring Boot的注解。
REST API
REST是Representational State Transfer的缩写,意为表现层状态转换。是基于HTTP的一种软件架构风格(而非标准)。这种风格的Web API即为REST API/RESTful API,其具有以下特点:
- 由客户端、服务器、资源组成的客户端-服务器架构,请求通过HTTP处理。
- 无状态通信。请求里没有客户端的信息,并且每个请求都独立。
- 缓存。
- 统一接口。
- 分层系统。客户端不知道自己是否连接到了服务器,因为中间可能有很多层服务器实现缓存等机制。
- 按需代码。服务器可以发送给客户端可执行代码,来扩展客户端的功能。
请求处理流程
- 一个Tomcat服务器运行中。
- 客户端发送请求到Tomcat,给出URL。
- 一般情况下,Tomcat根据配置文件
web.xml
找到URL对应的servlet或者filter,并委托其处理该请求。但在基于Spring MVC的Java应用中,所有请求都被移交给同一个servlet,即DispathcerServlet
,因为它处理的URL格式是*
,也就是所有URL。 - 在Spring MVC内部,
DispatcherServlet
作为前端总控制器,把不同的请求移交给不同类型的控制器处理。
4.1. 请求首先进入DispatcherServlet
的doService
方法。
4.2.doService
中调用doDispatch
方法。
4.3.doDispatch
中,遍历所有的handlerMapping
来寻找可以处理该请求的控制器(亦即处理器,handler),并移交请求。
4.4. 具体的处理器映射由注解决定。各种控制器类都带有有参数的注解@RequestMapping
以表明它处理哪些请求。例如:
@Controller
@RequestMapping("/appointments")
public class AppointmentController{
@GetMapping
public Map Get(){
return appointmentBook.getAppointmentsForToday();
}
@PostMapping
public String add(@Valid AppointmentForm appointment, BindingResult result){
if(result.hasErrors()){
return "appointments/new";
}
appointmentBook.addAppointment(appointment);
return "redirect:/appointments";
}
}
该控制器会处理任何带有"/appointments"
的URI的请求,再根据请求类型调用不同的方法来处理。对于GET
请求,它调用Get()
方法。对于POST
请求同理。
- 控制器处理完请求后,向servlet返回一个模型与视图(ModelAndView)。
- servlet将模型交给视图解析器(view resolvers,例如JSP)。
- 解析器渲染模型,返回视图,发送给客户端作为响应。
——How Spring MVC Framework works? How HTTP Request is processed?
——springboot请求处理流程
——Spring MVC flow with Example
关于Spring Boot的注解:
Bean
JavaBean指的是符合如下条件的类:
- 有无参构造函数。
- 可序列化。
- 提供设置和获得属性值的方法,即setter和getter方法。
根据Java白皮书,JavaBean是可重复使用的软件组件。一个bean能把许多对象封装进同一个,于是我们可以从多个地方访问到这个对象,并且还提供了便捷的维护。
——JavaTPoint - JavaBean
*“把许多对象封装进同一个”这句话我是真不太懂,然而维基百科和JavaTPoint都是这么说的。我在StackOverflow上查了一下,发现有人有一样的疑问,但回答都模棱两可,只有一条解释最通的评论说这句话的重点在于“封装”而非“许多”。这就很好理解了。
Spring中的bean则泛指各种组件。
*也许是因为Spring里的组件都封装得很好吧。
IoC
即Inversion of Control,控制反转。如果没有IoC,程序的控制完全在开发者手中。这固然提供很大的自由度,但在不同对象需要使用同一个组件时,常常需要重复创建,也就是说对象的创建、配置与使用过于耦合。
反转控制后,控制权由IoC容器掌握。容器能自动地创建和配置组件,并且管理其生命周期。在有对象需要使用组件时注入,在没有对象需要使用组件时销毁。
那么自然有必要告诉容器组件之间的依赖关系。最简单的方式是在XML文件中描述。
Spring的IoC容器支持字段注入、构造方法注入、方法注入。
Spring的IoC容器具有无侵入性,即容器中的组件不需要使用和Spring有关的特定接口。
Spring Boot的注解
主要有以下几种。
核心Spring框架注解
@Required
:类级注解。被注解的bean在配置时必须要被设置指定属性,否则抛出异常BeanInitializationException
。@Autowired
:不需要在XML中写明依赖关系,该注解就可以让Spring把被注解的组件注入此处的方法/构造方法/字段。@Configuration
:类级注解。被注解的类是配置类。@Component
:类级注解。被注解的类是一个组件(可能不清楚在哪个层)。与@ComponentScan
配合使用。@Repository
:类级注解。被注解的类是一个持久层(Data Access Object, DAO)组件,直接访问数据库。@Service
:类级注解。被注解的类是一个服务层组件。@Controller
:类级注解。被注解的类是一个控制层组件。常被用来提供web页面,配合@RequestMapping
使用。以上三种都是特别的@Component
。@RequestMapping
:类/方法级注解。用于映射web请求。@ComponentScan
:标注在启动容器类前面,使得启动容器自动搜索当前包及其所有子包,创建所有被注解了@Component
的bean的实例,并根据@Autowired
装配。@Bean
:方法级注解。XML中<bean>
标签的替代。被注解的方法给Spring容器产生一个bean。
Spring Boot注解
@SpringBootApplication
:启动类的注解,默认加上。相当于
@EnableAutoConfiguration
@ComponentScan
@Configuration
包含了自动配置和自动扫描。
——Spring Boot Annotations
——Spring/Spring Boot常用注解总结
6.理解Maven在Java项目中的作用与用法。
Maven:是一个软件(主要是Java程序)项目管理和自动构建工具,使用名为pom.xml的项目对象模型文件来配置。
maven一词原意为【某领域的专家,致力于向其他人传授相关知识】。该词来自犹太语meyvn,意为【专家、行家】,而这个词又来自希伯来语מבין mēvīn,意为’person with understanding, teacher’
——维基百科 - Maven
Maven的首要目的是让开发者在最短时间里理解一个项目的完整状态。为实现这一目标,Maven处理以下问题:
- 简化构建过程。虽然使用Maven并没有消除了解底层机制的需要,但Maven依然为开发者屏蔽了许多细节。
- 提供标准化的构建系统。Maven使用项目对象模型(project object model, POM)和一系列插件来构建项目。一旦你熟悉了一个Maven项目,你就明白了所有Maven项目的构建过程。
- 提供高质量的项目信息。Maven提供的项目信息非常有用,其中一部分来自你的POM,一部分来自你的项目源码。
- 鼓励更好的开发实践。
——What is Maven
POM:是Maven的基本工作单元,是一个包含项目信息和配置细节的XML文件。在执行一个任务时,Maven在当前目录下寻找POM,读取POM,获得所需信息,然后执行任务。
POM中可以指定一些配置,比如项目依赖,可用插件,构建概要,等到。POM也可以指定项目版本、描述、开发者等信息。
Super POM是Maven的默认POM。除非特别设置,所有POM都继承自Super POM。
一个POM中最少要有以下信息:
project
根。modelVersion
应该被设置为4.0.0。groupId
当前项目的组的id。artifactId
当前项目(artifact)的id。version
指定组下的项目的版本。
下面是一个例子(minimal POM):
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>
POM要求groupId, artifactId和version都设置好,这三个值唯一确定项目名,形式为<groupId>:<artifactId>:<version>
。如上面例子中的项目名为com.mycompany.app:my-app:1
。
如果配置细节没有设置,则按照默认值。如打包类型,如果POM中没有指定,则使用默认值jar
。
POM中还包括如下元素:依赖,开发者与贡献者,插件列表,插件执行,插件配置,资源。
POM能实现如下功能:
- 项目继承。
parent
项可以引入一个父项目,其同样需要上述三元组指定。额外的,可以用relativePath
参数指定相对路径;如果项目与父项目的groupId
,version
相同,则可以省略不写。 - 项目整合。
module
项可以引入一个模组,其参数值为模组相对当前文件夹的路径。 - 项目依赖。
dependencies
项可以引入若干项目,每个以一个dependency
项指定。 - 变量。文件中任何一个单值的字段都可以被作为变量引用,如
${project.groupId}
等。除了project.
之外,也可以pom.
作为前缀。有一些特别的变量:project.basedir
,project.baseUri
,maven.build.timestamp
,${mavenVersion}
等。
——Introduction to the POM
下面是一个更完整的例子:
<project>
<!-- model version is always 4.0.0 for Maven 2.x POMs -->
<modelVersion>4.0.0</modelVersion>
<!-- project coordinates, i.e. a group of values which uniquely identify this project -->
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0</version>
<!-- library dependencies -->
<dependencies>
<dependency>
<!-- coordinates of the required library -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<!-- this dependency is only used for running and compiling tests -->
<scope>test</scope>
</dependency>
</dependencies>
</project>
Maven的一个生命周期(lifecycle)包含若干阶段(phase),每个阶段包含若干目标(goal)。运行mvn <lifecycle>
命令时,Maven按顺序执行给定的生命周期(不指定则为default)中的所有阶段中的所有目标。也可以指定多个生命周期。
常用的生命周期有clean
, clean compile
, clean test
, clean package
等。
每个目标由插件和参数两部分按照<plugin>:<parameter>
的格式组成。如阶段compile
中唯一的目标compiler:compile
,阶段test
中的两个目标compiler:testCompile
, surefile:test
等。
插件也可以自定义。
*Maven可以把项目发布到本地/Maven中央仓库/GitHub Packages。第三个是最可能用于我的示例的,但我被给到的连接似乎的GithHub库而非Packages,所以似乎无法用Maven上传。
7.复习关系型数据库SQL语法,重点理解如何设计一个关系。
略。
二、搭建开发环境
- IDE:idea。
- 在Windows上安装MySQL。
- MySQL连接工具:Navicat Premium。
- 前后端调试工具:postman。
- Git。
三、例子:简单的登录注册
SpringBoot+Mysql做登陆接口
SpringBoot+MyBatis做注册接口
*本文的参考远远不止给出了超链接的那些文章,不给链接的原因主要有两个。1. 很多文章的内容大同小异,我是综合它们做好的笔记,将它们的链接全部给出既费力又没有什么意义。2. 提供了很多参考但很分散,就在下面统一写出吧。
*实际上就一个:廖雪峰的官方网站。其中的讲述非常丰富并且易于理解,可惜时间所限,我不会的东西又太多……无法细细学习。