1. 课程介绍
2. Maven入门
Maven的作用:maven可以帮我们创建项目、编译项目、测试项目
2.1 设置阿里云maven镜像仓库
首先演示一下如何下载maven,而且因为默认会访问中央仓库,比较慢,所以我们还需要修改一下配置文件使其访问阿里云的镜像仓库。
进入官网下载maven压缩包
https://maven.apache.org/download.cgi
之后将下载好的压缩包进行解压
之后进入解压好的文件夹之中的conf目录修改settings.xml文件去使用阿里云的maven进行仓库
之后找到 146 行左右有一个 mirrors 标签,将下面这几行内容粘贴到标签内部,这样访问阿里云镜像仓库就设置好了。
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf> <!--表示它是谁(中央仓库)的镜像仓库-->
<name>aliyun maven</name> <!--取名仓库的名字-->
<url>https://maven.aliyun.com/repository/central</url> <!--镜像地址-->
</mirror>
2.2 maven的简单使用
maven默认是采用命令行的方式进行访问的,需要进入到bin目录下,然后打开命令行,cd 到这个目录,来用mvn.cmd这个工具。(工作中我们一般是不会直接敲命令行的,maven会与idea进行集成,到时候直接点击按钮就可以了,但是maven的命令行我们还是要简单的了解一下)
当然,为了方便使用,我们可以把这个bin路径配置到环境变量里,这样我们就不需要cd到这个目录了,可以随时随机的使用。
电脑 --> 属性 --> 高级系统设置
之后点击 高级 --> 环境变量
之后打开 cmd
在其中输入 mvn -version 查看maven版本,以检验环境变量是否设置成功
如果想要更详细了解一下 maven 的话可以看一下官方文档
https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html
maven可以帮我们创建项目、编译项目、测试项目,接下来我们来演示一下。
1. 创建项目
在使用maven命令行进行创建项目时,当我们想要将项目创建在指定目录下时我们首先要 cd 那个指定目录然后再敲 maven 命令,现在我要将项目放在 D:\work\workspace 路径下,所以首先要在cmd下去到 D盘, 然后 cd 到 指定目录,然后在cmd下输入下面的创建项目。
mvn archetype:generate -DgroupId=com.nowcoder.mavendemo1 -DartifactId=mavendemo1 -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
创建项目
这里要补充一点关于上面的创建项目命令的知识点
maven命令都是以mvn开头
archetype:generate意思是以模板原型的方式去生成一个文件
-DgroupId意思是组织ID,通常就是写公司域名的倒序,后面还要写上项目的名字,上面的com.nowcoder表示公司域名的倒序,mavendemo1表示项目的名字
-DartifactId表示项目的ID
-DarchetypeArtifactId表示生成项目模板的ID是什么
-DarchetypeVersion表示项目模板版本
-DinteractiveMode表示是否启用交互模式,我们写false,不启用,如果启用交互模式的话每执行一步都会问你
是/否比较麻烦
首先进入 指定目录下
然后输入创建项目的命令
其实点进去我们会发现里面的项目结构和我们使用 idea 集成 maven 创建项目的结构一模一样
项目结构
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- com
| `-- mycompany
| `-- app
| `-- App.java
`-- test
`-- java
`-- com
`-- mycompany
`-- app
`-- AppTest.java
2. 编译项目
我们要编译哪个项目,我们要 cd 到那个项目的目录下(带有 pom.xml 文件的那个目录下)
D:\work\workspace\mavendemo1
然后在cmd里输入下面的命令进行编译
mvn compile
编译之后会生成一个 target目录,编译的结果都会放在 target 下
然后补充一点
# 重新编译
mvn clean
是把刚才生成的target删掉,也就是把刚才编译的结果删掉
之后再
mvn compile
重新编译
或者我们可以直接输入
mvn clean compile 先清理再编译(重新编译)
那么如何运行测试代码呢
# 运行测试代码
mvn clean test
我们可以运行上面的代码执行测试的代码
2.3 安装 IntelliJ IDEA
因为 IntelliJ IDEA的安装比较简单,所以这里并没有详细介绍,只是简单的截了一些图
https://www.jetbrains.com/idea/
设置 IDEA 中的 maven
创建项目
点击 Finish 就会创建项目了
2.4 Spring Initalizr 的使用
Spring Initalizr 是什么呢?
我们在使用 maven 时,要导入依赖时需要一个一个的搜,但是使用 Spring Initalizr 我们只要搜一个 web 它就能把相关的几十个包都引进来,Spring Initalizr底层还是 maven。(社区版需要去网页使用,专业版 IDEA 直接继承了。)
官网
https://start.spring.io
补充需要的这些包
之后就会将生成的项目下载下来然后用IDEA打开就可以了
社区版IDEA没有直接使用 Spring Initalizr 的功能需要去网页上使用,但是专业版可以直接使用,比较方便,不用这么麻烦
接下来我们来演示一个 Spring Boot 入门案例
package com.nowcoder.community.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/alpha")
public class AlphaController {
@RequestMapping("/hello")
@ResponseBody
public String sayHello(){
return "Hello Spring Boot.";
}
}
修改配置文件之后
# 端口,不设置默认就是8080
server.port=8080
# 项目名
server.servlet.context-path=/community
3. Spring入门
https://spring.io
3.1 Spring 中关于在工厂中注册的四个注解
Spring中的四个关于在工厂中注册的注解
Spring主要有四种注解可以注册bean,每种注解可以任意使用,只是语义上有所差异:
@Component:可以用于注册所有bean
@Repository:主要用于注册dao层(数据库访问)的bean
@Controller:主要用于注册controller层(处理请求)的bean
@Service:主要用于注册service层(业务)的bean
当我们在创建对应类的时候需要加上对应的注解,只有这样,才会在工厂中注册这些类。
3.2 关于测试类
# 关于测试类上加的 @RunWith(SpringRunner.class) 注解问题
@RunWith注解作用:
--让测试在Spring容器环境下执行。如测试类中无此注解,将导致service,dao等自动注入失败。
--@RunWith就是一个运行器
--@RunWith(JUnit4.class)就是指用JUnit4来运行
--@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环 境,以便在测试开始的时候自动创建Spring的应用上下文
--@RunWith(Suite.class)的话就是一套测试集合
--@RunWith(SpringRunner.class),让测试运行于Spring测试环境,以便在测试开始的时候自动创建Spring的应用上下文
因为SpringRunner.class继承了SpringJUnit4ClassRunner.class且没有进行任何修改
所以@RunWith(SpringRunner.class)基本等同于@RunWith(SpringJUnit4ClassRunner.class)
# 网上问题
问题:
查了好多文章说@RunWith(SpringRunner.class)注解是一个测试启动器,可以加载Springboot测试注解。
本人好奇@RunWith(SpringRunner.class)的作用,于是在IDEA中把这个注解去掉后发现Bean也可以通过@Autowired注解进行注入。于是比较怀疑@RunWith注解的作用
解释:
在正常情况下测试类是需要@RunWith的,作用是告诉java你这个类通过用什么运行环境运行,例如启动和创建spring的应用上下文。否则你需要为此在启动时写一堆的环境配置代码。你在IDEA里去掉@RunWith仍然能跑是因为在IDEA里识别为一个JUNIT的运行环境,相当于就是一个自识别的RUNWITH环境配置。但在其他IDE里并没有。
所以为了让自己的代码不仅可以在 IDEA 中运行,也可以在其他编译软件中运行,还是建议加上
另外,为了可以使用 @RunWith(SpringRunner.class),我们需要引入下面的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
# 关于测试类上加的@ContextConfiguration(classes = CommunityApplication.class)注解的问题
我们正式运行程序运行的是正式环境中的入口类(配置类),而不是测试环境中的入口类(配置类)执行程序,
我们测试的时候肯定也希望使用正式环境中的配置类.希望和正式环境中的配置类是一样的.所以我们要在测试类上加上
@ContextConfiguration(classes = CommunityApplication.class)注解。
注意:CommunityApplication.class要写成自己项目中正式环境中的入口类的class类型
# 关于测试类实现ApplicationContextAware接口的问题
IOC核心是Spring容器,而容器又是被自动创建的,那我们怎么去得到这个容器呢?其实很简单,哪个类
想得到Spring容器,让它实现
注意:实现ApplicationContextAware接口需要去实现setApplicationContext方法
需要重写的方法初始模样
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
}
如果一个类实现了ApplicationContextAware接口的setApplicationContext方法,Spring容器会检测到,
Spring容器在扫描组件的时候会调用setApplicationContext方法把自身(容器)传进来,我们只需要把
这个容器暂存下来,后面就能够使用它了,所以在测试类中加一个成员变量(记录这个容器),然后在
setApplicationContext方法中将容器赋给它,当程序运行的时候容器自动被传进来,然后赋值给
成员变量applicationContext,然后在其他的地方就可以使用这个Spring容器了。
所以测试方法一般结构
@SpringBootTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
// 测试方法,测试方法的名字可以随便写,但是记得加上@Test注解
@Test
void contextLoads() {
}
}
3.3 演示使用容器创建Bean、销毁Bean
在测试方法中使用一下Spring容器
接下来写一个测试方法,在这个测试方法里面去使用一下这个Spring容器
用到的类:
AlphaDao:
public interface AlphaDao {
public String select();
}
AlphaDaoHibernateImpl:
@Repository // 访问数据库的话需要加上这个注解,加上这个注解使得这个类在工厂之中进行注册
// 这个类在工厂中的默认名字是类名首字母小写
@Primary // 加上这个类是如果还有其它的类与其实现了相同的接口,在工厂中获取接口的class时获取
// 的是这个实现类(解耦,不用修改逻辑代码),当然在工厂中获取的时候可以直接获取实现类的class
public class AlphaDaoHibernateImpl implements AlphaDao{
@Override
public String select() {
return "Hibernate";
}
}
AlphaDaoMyBatisImpl:
@Repository // 加上这个类使得这个类在工厂中进行注册:
public class AlphaDaoMyBatisImpl implements AlphaDao{
@Override
public String select() {
return "MyBatis";
}
}
测试类CommunityApplicationTests:
@SpringBootTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {
// 在接口的实现方法中将工厂赋给这个成员变量
private ApplicationContext applicationContext;
// 参数是Spring工厂
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
// 测试方法
@Test
public void testApplicationContext() {
System.out.println(applicationContext);
// 从容器中获取Alpha类(通过类型获取)
AlphaDao alphaDao = applicationContext.getBean(AlphaDao.class);
System.out.println(alphaDao.select()); // 这里通过接口获取的是Hibernate
// 从容器中获取Alpha类(通过名字获取),方法返回类型是Object,所以要强制类型转换
alphaDao = (AlphaDao) applicationContext.getBean("alphaDaoMyBatisImpl");
System.out.println(alphaDao.select()); // 这里通过实现类在工厂中的Id获取的是
}
}
/*
结果:
Hibernate
MyBatis
*/
上面演示的是创建Bean,接下来我们来演示一下初始化Bean、销毁Bean方法
AlphaService类:
@Service
public class AlphaService {
public AlphaService(){
System.out.println("实例化AlphaService");
}
@PostConstruct
// 让容器管理这个方法,也就是让容器在合适的时候自动的调用这个方法
// @PostConstruct注解的意思是这个方法会在构造器之后调用,初始化方法一般都是在构造之后调用的
public void init(){
System.out.println("初始化AlphaService");
}
@PreDestroy
// 加上这个方法之后会在对象销毁之前调用它可以去释放一些资源
public void destory(){
System.out.println("销毁AlphaService");
}
}
测试类CommunityApplicationTests:
@SpringBootTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
// 测试方法
@Test
public void testBeanManagement(){
AlphaService alphaService = applicationContext.getBean(AlphaService.class);
System.out.println(alphaService);
}
}
程序启动过程输出的内容
Spring容器管理对象默认是单例,如果想要修改为多例,可以加上相应注解修改
被Spring容器管理的类默认都是单例的,如果想要多例的话,在类上加上注解@Scope(“prototype”)
3.4 在工厂中注册第三方的类
上面一些情况都是我们在工厂中注册自己写的类的例子,但加入我们想要在工厂中注册第三方的类(jar包里的)该怎么做呢?
我们需要自己去写一个配置类,在配置类当中通过@Bean注解进行声明来解决这个问题
假如说我们现在要在工厂中注册第三方的类SimpleDateFormat ,我们来演示一下
// 配置类
@Configuration // 这个注解表明这个类是一个注解类,而不是普通的类
public class AlphaConfig {
@Bean // 这个Bean的名字就是方法的名字
public SimpleDateFormat simpleDateFormat(){
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //返回一个实例并指定日期格式
}
}
测试类
@SpringBootTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
// 测试方法
@Test
public void testApplicationContext() {
System.out.println(applicationContext);
// 从容器中获取Alpha类(通过类型获取)
AlphaDao alphaDao = applicationContext.getBean(AlphaDao.class);
System.out.println(alphaDao.select());
// 通过名字进行获取,方法返回类型是Object,所以要强制类型转换
alphaDao = (AlphaDao) applicationContext.getBean("alphaDaoMyBatisImpl");
System.out.println(alphaDao.select());
}
@Test
public void testBeanConfig(){
SimpleDateFormat simpleDateFormat = applicationContext.getBean(SimpleDateFormat.class);
System.out.println(simpleDateFormat.format(new Date()));
}
}
从上面我们可以看到,这种方法其实是有些笨拙的,因为我们要写配置类,配置类中要写方法,方法之上又要加注解,很麻烦。
3.5 依赖注入
我们说Spring另一个特性是依赖注入,依赖注入我们只需要在成员变量上加上 @Autowired 工厂就会自动将工厂中的类赋值给成员变量
@SpringBootTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
// 测试方法
@Test
public void testApplicationContext() {
System.out.println(applicationContext);
// 从容器中获取Alpha类(通过类型获取)
AlphaDao alphaDao = applicationContext.getBean(AlphaDao.class);
System.out.println(alphaDao.select());
// 通过名字进行获取,方法返回类型是Object,所以要强制类型转换
alphaDao = (AlphaDao) applicationContext.getBean("alphaDaoMyBatisImpl");
System.out.println(alphaDao.select());
}
@Autowired // 依赖注入
@Qualifier("alphaDaoMyBatisImpl") // 指明使用这个接口的哪个实现类(写实现类在工厂中的名字)
private AlphaDao alphaDao;
@Autowired
private AlphaService alphaService;
@Autowired
private SimpleDateFormat simpleDateFormat;
@Test
public void testDI(){
System.out.println(alphaDao.select());
System.out.println(alphaService);
System.out.println(simpleDateFormat.format(new Date()));
}
}
使用这种依赖注入的方式就不需要写上面那种配置类了
3.6 关于 dao 层、service层、controller层之间的关系解释
最后补充一点关于 dao 层、service层、controller层之间的关系,并演示一下
controller来处理浏览器的请求,它在处理浏览器的过程中会调用业务组件(service)去处理当前业务,业务组件会调用dao去访问数据库,总结下来就是 controller 调 service,service 调 dao,它们之间的关系是相互依赖的,它们之间依赖的关系就可以使用依赖注入的方式去实现
dao层是用来连接数据库的,service通过使用dao组件并处理业务逻辑,controller是通过使用service来与浏览器进行交互的。
演示一下
AlphaDao:
public interface AlphaDao {
public String select();
}
AlphaDaoHibernateImpl:
@Repository // 访问数据库的话需要加上这个注解,加上这个注解使得这个类在工厂之中进行注册
@Primary
public class AlphaDaoHibernateImpl implements AlphaDao{
@Override
public String select() {
return "Hibernate";
}
}
AlphaService:
@Service
public class AlphaService {
@Autowired
private AlphaDao alphaDao;
public String find(){
return alphaDao.select();
}
}
AlphaController:
@Controller
@RequestMapping("/alpha")
public class AlphaController {
@Autowired
private AlphaService alphaService;
@RequestMapping("/data")
@ResponseBody // 将数据转换为json数据
public String getData(){
return alphaService.find();
}
}
测试:
4.SpringMVC入门
4.1 SpringMVC简介
https://www.ietf.org
https://developer.mozilla.org/zh-CN
HTTP
**超文本传输协议(HTTP)**是一个用于传输超媒体文档(例如 HTML)的应用层协议。它是为 Web 浏览器与 Web 服务器之间的通信而设计的。HTTP 遵循经典的客户端 - 服务端模型,客户端打开一个连接以发出请求,然后等待直到收到服务器端响应。
HTTP 是一种能够获取如 HTML 这样的网络资源的 protocol(通讯协议)。它是在 Web 上进行数据交换的基础,是一种 client-server 协议,也就是说,请求通常是由像浏览器这样的接受方发起的。一个完整的 Web 文档通常是由不同的子文档拼接而成的,像是文本、布局描述、图片、视频、脚本等等。
HTTP 流
当客户端想要和服务端进行信息交互时(服务端是指最终服务器,或者是一个中间代理),过程表现为下面几步:
- 打开一个 TCP 连接:TCP 连接被用来发送一条或多条请求,以及接受响应消息。客户端可能打开一条新的连接,或重用一个已经存在的连接,或者也可能开几个新的 TCP 连接连向服务端。
- 发送一个 HTTP 报文:HTTP 报文(在 HTTP/2 之前)是语义可读的。在 HTTP/2 中,这些简单的消息被封装在了帧中,这使得报文不能被直接读取,但是原理仍是相同的。
- 读取服务端返回的报文信息
- 关闭连接或者为后续请求重用连接。
SpringMVC就是基于HTTP协议的框架
4.2 thymeleaf模板简介
https://www.thymeleaf.org
使用thymeleaf的时候记得在配置文件里设置将缓存关掉,因为如果不把缓存关掉的话,可能改了页面看到的还是旧的内容,没有刷新,有一个延迟。在开发的时候建议关闭缓存,但是在项目上线的时候建议开启,因为有缓存可以降低服务器的压力。
只需要在application.properties配置文件加上下面这句话就可以关闭thymeleaf缓存
spring.thymeleaf.cache=false
4.3 写一个SpringMVC的小demo
前提小知识
# 为什么我们写的方法不需要返回值并且不需要@ResponseBody注解了
之前我们写的方法都有返回值,并且加有@ResponseBody注解,是因为之前我们没有使用response对象,是将返回值
转换成json数据呈现在了页面上,但是现在我们可以通过 response 对象直接向浏览器输出任何数据,就不需要依赖
返回值了,也就不需要@ResponseBody注解将返回值转换成json数据了。
# 如何获取浏览器的请求对象以及获得controller的响应对象
只需要在方法上声明请求对象和响应对象即可。
声明了这两个对象之后,DispatcherServlet在调这个方法的时候就会自动的把请求、响应对象传给
方法的参数。request和response是接口,多层接口,我们常用的接口是HttpServletRequest、
HttpServletResponse。
我们可以利用request对象处理请求,处理请求就是读取请求当中包含的数据加以处理。
# 关于response如何响应网页
通过response响应请求其实就是通过它里面封装的输出流向浏览器输出
小demo
@Controller
@RequestMapping("/alpha")
public class AlphaController {
@Autowired
private AlphaService alphaService;
@RequestMapping("/data")
@ResponseBody
// 这个方法是通过返回参数转化为json数据呈现在浏览器上,而下面的http方式是通过响应对象呈现
// 数据在浏览器上
public String getData(){
return alphaService.find();
}
@RequestMapping("/http")
// 浏览器的请求对象 controller响应请求的响应对象
public void http(HttpServletRequest request, HttpServletResponse response){
// 获取浏览器的请求数据
System.out.println(request.getMethod()); // 获取请求方式
System.out.println(request.getServletPath()); // 请求路径
Enumeration<String> enumeration = request.getHeaderNames(); // 获取所有的请求行的key(请求行是key-value结构)
// while里面的其实是请求头的数据
while (enumeration.hasMoreElements()){
String name = enumeration.nextElement();
String value = request.getHeader(name);
System.out.println(name + ": " + value);
}
// 获取请求体当中的参数(业务数据)
System.out.println(request.getParameter("code"));
// 返回controller的响应数据的对象: response
response.setContentType("text/html;charset=utf-8"); // 设置返回的相应数据的类型和编码
PrintWriter writer = null;
try {
writer = response.getWriter(); // 获取响应对象的输出流去响应网页(异常try/catch处理)
writer.write("<h1>牛客网</h1>"); // 通过writer向浏览器输出一个一级标题
} catch (IOException e) {
e.printStackTrace();
} finally {
writer.close(); // 关闭响应对象的输出流
}
}
}
通过浏览器传参的其中一种方法
直接在地址栏上拼接数据
地址栏传参在url上用 ?拼接 传参多个的话多个参数之间用 & 拼接
4.4 浏览器从服务器获取数据:GET请求
1. 浏览器传的参数通过地址栏拼接在一起
浏览器传的参数通过地址栏拼接在一起如何接收
http://localhost:8080/community/alpha/students?current=1&limit=30
除了上面我们可以从浏览器的请求对象获取数据,我们还可以在controller方法的传参中声明一下数据(需要注意一点的是:成员方法的名字要和传过来的参数的名字一样),这样传的参数会自动赋值给方法的参数。
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// GET请求
// /students?current=1&limit=20
@RequestMapping(path = "/students", method = RequestMethod.GET) // 制定了浏览器请求的方式只能为GET方式
@ResponseBody
public String getStudents( int current,int limit){
System.out.println("current = " + current);
System.out.println("limit = " + limit);
return "some students";
}
}
# 注意
使用这种方式接收参数的话浏览器在请求 http://localhost:8080/community/alpha/students
时必须传参,不传参会出现异常,那么我们想要不传参的话也不会出现异常并且有默认值的话该怎么做,
这个时候我们就需要给方法的参数加上注解了。如下:
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// GET请求
// /students?current=1&limit=20
@RequestMapping(path = "/students", method = RequestMethod.GET) // 制定了浏览器请求的方式只能为GET方式
@ResponseBody
public String getStudents(
@RequestParam(name = "current", required = false, defaultValue = "1") int current,
@RequestParam(name = "limit", required = false, defaultValue = "20") int limit){
System.out.println("current = " + current);
System.out.println("limit = " + limit);
return "some students";
}
}
2.浏览器传的参数成为了路径的一部分
浏览器传的参数成为了路径的一部分
http://localhost:8080/community/alpha/student/456
这种方式需要用到 @PathVariable 注解
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// GET请求
// /student/123
@RequestMapping(path = "/student/{id}", method = RequestMethod.GET)
@ResponseBody
public String getStudent(@PathVariable("id") int id){
System.out.println("id = " + id);
return "a student";
}
}
# 关于GET方式是从服务器获取数据,POST方式是向浏览器提交数据怎么解释?
参考了一下别人的博客:
其实二者都是在向服务器提交请求,如果非要说get是获取数据,post是提交数据的话,你可以从二者的区别理解:
安全性:get请求的数据是url地址明文发送,不安全,而post的请求数据不会再地址栏出现,较为安全
数据量限制:get有数据量限制,而post没有
等幂性:get请求的数据不会修改服务器的状态,如读取静态文件(图片、html文件等),所以这里你可以理解为是获
取数据,而post一般会改变服务器的状态.比方说添加某一条数据(你所说的提交数据),那么此数据将会更改数据库
的记录 ,所以,你可以理解post是向服务器提交数据
另一种解释:
说get是向服务器获取数据,而post是向服务器传递数据 其实是英文文档翻译的时候断章取义了,文档中可出这点
(HTML规范在技术上定义了“GET”和“POST”之间的区别,前者意味着表单数据将(通过浏览器)编码成URL,而后者意
味着表单数据将出现在消息正文中。但是规范也给出了使用建议,当表单处理是“幂等”时,应该使用“GET”方法,并且
仅在那些情况下。作为一种简化,我们可以说“GET”基本上是为了获取(检索)数据,而“POST”可能涉及到存储或更新
数据、订购产品或发送电子邮件等任何事情。)
4.5 浏览器向服务器提交数据:POST请求
# 浏览器向服务器提交数据:POST请求
GET方式是从服务器获取数据那么如果浏览器是向服务器提交数据通常我们都用post,post就是浏览器向服务器
提交数据的时候使用的。
浏览器相向服务器提交数据,它得打开一个带有表单的网页,通过表单填写数据之后提交给服务器。
我们先创建一个html文件(静态资源):student.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>增加学生</title>
</head>
<body>
<form method="post" action="/community/alpha/student">
<p>
姓名:<input type="text" name="name">
</p>
<p>
年龄:<input type="text" name="age">
</p>
<p>
<input type="submit" value = "保存">
</p>
</form>
</body>
</html>
- form中的method指明浏览器提交数据的方式为“POST”
- form中的action表示这个表单提交给指定路径的controller
- 中的type表示类型,name表示这个提交数据的名字
- 中的type是submit表示提交,value是这个按钮在浏览器中显示的名字
接下来我们来创建一个controller来处理上面所提交的表单:
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// POST请求
@RequestMapping(path = "/student", method = RequestMethod.POST)
@ResponseBody
public String saveStudent(String name, int age){
System.out.println("name = " + name);
System.out.println("age = " + age);
return "success";
}
}
4.6 controller向浏览器返回html文件
4.7 themeleaf模板的方式返回网页
返回 ModelAndView 方式返回themeleaf资源
接下来演示一下如何向浏览器返回响应数据:
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// 响应html数据
@RequestMapping(path = "/teacher", method = RequestMethod.GET)
public ModelAndView getTeacher(){
ModelAndView modelAndView = new ModelAndView();
// 传值
modelAndView.addObject("name", "张三");
modelAndView.addObject("age", 30);
// 设置模板的路径和名字(模板会放到 templates 目录,这个路径不用写,)
modelAndView.setViewName("/demo/view"); // view.html这个文件,后面的 .html 后缀省略
return modelAndView;
}
}
接下来我们来常见一个模板文件:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.themeleaf.org">
<head>
<meta charset="UTF-8">
<title>teacher</title>
</head>
<body>
<p th:text="${name}"></p>
<p th:text="${age}"></p>
</body>
</html>
创建一个html文件之后需要声明,这个html文件不是静态的,得让服务器直到它是 themeleaf 模板(动态资源),而不是普通的html。
需要将第二行的
<html lang="en">
修改为
<html lang="en" xmlns:th="http://www.themeleaf.org">
这句话就声明当前这个html是一个模板,这个模板的语法来源于 themeleaf 官网。
返回String字符串来返回themeleaf模板资源
上面我们使用的是返回一个ModelAndView去返回一个themeleaf模板,接下来我们来试一下返回String字符串来返回themeleaf模板,返回的是字符串的话返回的是themeleaf模板的路径。
如果我们采用返回字符串方法声明中需要声明一个 Model ,这样 DispatcherServlet 会给这个Model传一个值,DispatcherServlet 持有这个 Model 的引用,也就是可以获得数据,然后通过返回字符串返回 themeleaf 的路径(View),这样 DispatcherServlet 持有 Mode 和 View 然后会把它们交给模板引擎然后模板引擎在浏览器上进行渲染。
@Controller
@RequestMapping("/alpha")
public class AlphaController {
@RequestMapping(path = "/school", method = RequestMethod.GET)
public String getSchool(Model model){
// 传值
model.addAttribute("name", "北京大学");
model.addAttribute("age", "80");
return "/demo/view"; // 返回 view
}
}
themeleaf模板还是上面那个view.html文件
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.themeleaf.org">
<head>
<meta charset="UTF-8">
<title>teacher</title>
</head>
<body>
<p th:text="${name}"></p>
<p th:text="${age}"></p>
</body>
</html>
4.8 服务器向浏览器返回JSON格式的数据
一般在异步请求当中会响应JSON数据
# 什么是异步请求
举个例子 :
注册用户时,输入的用户名重复,会立即显示当前用户名已被占用,当前网页不刷新,
但是浏览器访问了服务器得到了结果
# JSON作用
如果把java对象这个数据返回给浏览器,浏览器解析这个对象用的是JS,两者不兼容。
我们使用JSON可以保证两者的兼容 :
Java对象 -> JSON字符串 -> JS对象
当然下面并不会演示如何发送异步请求,现在我们关心的是如何向浏览器响应JSON数据
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// 响应JSON数据
// Java对象 -> JSON字符串 -> JS对象
@RequestMapping(path = "/emp", method = RequestMethod.GET)
@ResponseBody // 加上这个注解才会将返回的数据转换成JSON字符串
public Map<String, Object> getEmp(){
Map<String, Object> emp = new HashMap<>();
emp.put("name", "张三");
emp.put("age", 23);
emp.put("salary", 8000.00);
return emp;
}
}
5. MyBatis入门
对于牛客社区这个项目我们专门建一个数据库community,我们把项目中需要的表给它导入进去,一些测试数据也给它初始化好。
建表的sql语句以及表单的一些初始数据导入到community数据库中。
接下来正式学习MyBatis
https://mybatis.org/mybatis-3
https://mybatis.org/spring
- 在SpringBoot中MyBatis的前三个核心组件其实SpringBoot已经帮我们做了,我们需要做的只是写后两个组件:Mapper接口、Mapper映射器。
5.1 使用MyBatis对数据库进行操作
接下来我们来使用一下MyBatis对数据库进行操作
首先我们需要导入mysql的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
接下来我们导入MyBatis的依赖,这里建议使用SpringBoot整合MyBatis的依赖,因为我们用的是SpringBoot框架,我们用SpringBoot整合MyBatis的依赖肯定就更简单了,有自动的配置。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
接下来我们还要对MyBatis做一点配置,因为即便SpringBoot可以自动配置,它也不知道连接数据库的路径是什么、账号密码是什么、希望启动的最大连接是多少,这些我们都得自己配置。
在SpringBoot的整合下就不需要创建mybatis的主配置文件了,直接在 application.properties 配置文件中配置就可以了。
# DataSourceProperties 配置mysql数据库以及连接池(数据源)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/community?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
# MybatisProperties 配置MyBatis
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.nowcoder.community.entity
mybatis.configuration.useGeneratedKeys=true
mybatis.configuration.mapUnderscoreToCamelCase=true
接下来我们要对user表里的数据进行增删改查操作了
首先我们需要写一个实体类去封装表里的数据
User
public class User {
private int id;
private String username;
private String password;
private String salt;
private String email;
private int type;
private int status;
private String activationCode;
private String headerUrl;
private Date createTime;
// 为了以免影响阅读体验,get、set、toString方法就不粘了,但是其实是有的
}
接下来我们需要写一个DAO接口访问数据库(只需要去写接口不需要去写实现类),对于这个接口我们需要加一个注解才能让Spring容器装配这个 bean,访问数据库的话在工厂中注册需要加的注解是 @Repository ,但是MyBatis也有一个注解实现了在工厂中注册的功能 @Mapper,这两个注解用哪一个都可以。
@Mapper
public interface UserMapper {
User selectById(int id); // 根据id查询
User selectByName(String username); // 根据name查询
User selectByEmail(String email); // 根据email查询
int insertUser(User user); // 插入数据
int updateStatus(int id, int status); // 更新状态
int updateHeader(int id, String headerUrl);
int updatePassword(int id, String password);
}
接下来就要写这个DAO接口对应的Mapper配置文件了
简单类型的参数 parameterType 不用写
返回类型 resultType 一定要写
keyProperty 表示把 数据库自动生成的 主键值 回填到 对象对应的哪个属性(在这里是 id )
<?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.nowcoder.community.dao.UserMapper">
<sql id="insertFields">
username, password, salt, email, type, status, activation_code, header_url, create_time
</sql>
<sql id="selectFields">
id, username, password, salt, email, type, status, activation_code, header_url, create_time
</sql>
<select id="selectById" resultType="com.nowcoder.community.entity.User">
select <include refid="selectFields"></include>
from user
where id = #{id}
</select>
<select id="selectByName" resultType="com.nowcoder.community.entity.User">
select <include refid="selectFields"></include>
from user
where username = #{username}
</select>
<select id="selectByEmail" resultType="com.nowcoder.community.entity.User">
select <include refid="selectFields"></include>
from user
where email = #{email}
</select>
<insert id="insertUser" parameterType="com.nowcoder.community.entity.User" keyProperty="id">
insert into user (<include refid="insertFields"></include>)
values(#{username}, #{password}, #{salt}, #{email}, #{type}, #{status}, #{activationCode}, #{headerUrl}, #{createTime})
</insert>
<update id="updateStatus">
update user set status = #{status} where id = #{id}
</update>
<update id="updateHeader">
update user set header_url = #{headerUrl} where id = #{id}
</update>
<update id="updatePassword">
update user set password = #{password} where id = #{id}
</update>
</mapper>
接下来写一个测试类进行测试
@SpringBootTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = CommunityApplication.class)
public class MapperTests {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectById(){
User user = userMapper.selectById(101);
System.out.println(user);
user = userMapper.selectByName("liubei");
System.out.println(user);
user = userMapper.selectByEmail("nowcoder101@sina.com");
System.out.println(user);
}
@Test
public void testInsertUser() {
User user = new User();
user.setUsername("test");
user.setPassword("123456");
user.setSalt("abc");
user.setEmail("test@qq.com");
user.setHeaderUrl("http://www.nowcoder.com/101.png");
user.setCreateTime(new Date());
int rows = userMapper.insertUser(user);
System.out.println(rows);
System.out.println(user.getId());
}
@Test
public void updateUser() {
int rows = userMapper.updateStatus(150, 1);
System.out.println(rows);
rows = userMapper.updateHeader(150, "http://www.nowcoder.com/102.png");
System.out.println(rows);
rows = userMapper.updatePassword(150, "hello");
System.out.println(rows);
}
}
都运行成功了,然后经检验数据都正确操作了。
最后补充一点:
为了便于调试,我们可以在 application.properties 配置文件中设置日志的级别,便于调试,加上下面这句话即可
# logger
logging.level.com.nowcoder.community=debug