【参考视频】https://www.bilibili.com/video/BV1m84y1w7Tb?p=167&vd_source=38a16daddd38b4b4d4536e9c389e197f
SpringBoot项目的创建和接口配置
做一个springboot项目,从创建项目到实现浏览器访问localhost:8080/hello返回字符串hello world的全流程
1. 创建项目
- idea
- 新建项目
- Spring initializr->配置选择语言:java类型:maven组:com.jwz包名:com.jwz打包:jar
- 选择web->spring web
- finish
创建完成项目后可以把.idea、.mvn、.gitignore、HELP.md、mvnw、mvnw.cmd等无关项目的文件和文件夹删除
2.配置接口
在项目的com.jwz下新建controller包再创建HelloController.java文件,放入以下内容
package com.jwz.sprdemo02.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
// 请求处理类
@RestController
public class HelloController {
// 定义接口名
@RequestMapping("/hello")
public String hello(){
System.out.println("Hello World");
return "Hello World";
}
}
然后找到项目默认生成的Application.java的文件,右键运行即可
浏览器访问:http://localhost:8080/hello 即可显示Hello World
获取参数
原始方式
package com.jwz.sprdemo02.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
// 请求处理类
@RestController
public class HelloController {
// 定义接口名
@RequestMapping("/hello")
public String hello(HttpServletRequest request){
// 浏览器访问:http://localhost:8080/hello?name=萧寂&age=18
// 获取get请求参数
String name = request.getParameter("name");
String age = request.getParameter("age");
int ageInt = Integer.parseInt(age);
System.out.println(name+ageInt);
return name+ageInt;
}
}
SpringBoot方式
package com.jwz.sprdemo02.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
// 请求处理类
@RestController
public class HelloController {
// 定义接口名
@RequestMapping("/hello")
public String hello(@RequestParam("name") String name ,@RequestParam("age") Integer age){
System.out.println(name+":"+age);
return name+age;
}
}
// get请求:http://localhost:8080/hello?name=萧寂&age=18
// post请求:http://localhost:8080/hello 在body的x-www-form-urlencoded里面添加对应参数即可
// 上面获取参数方法可以获取get和post参数的,只是请求方法和参数的传递方式有点区别
参数对应不上的情况
// 下面这个方法,比如我请求地址和传参为:http://localhost:8080/hello?name=萧寂&age=18
// 可以发现,参数没有username 只有name,那么RequestParam这个注解就可以指定参数,当匹配到RequestParam指定的属性的值时也默认这个值为username的值,如果匹配不到RequestParam指定的参数,则会抛出400错误,如下所示,第二个参数改为false,代表此参数不是必填的,则不会报错,会将当前参数为null
public String hello(@RequestParam(value = "name",required = false) String username ,@RequestParam("age") Integer age){
// 此时匹配到name="萧寂" 也就相当于username="萧寂"
System.out.println(username+":"+age);
return username+age;
}
// @ReguestParam中的required属性默认为true,代表该请求参数必须传递,如果不传递将报错。如果该参数是可选的,可以将required属性设置为false。
参数接收
实体参数
package com.jwz.sprdemo02.user;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
public class simplePojo {
private String name;
private Integer age;
public simplePojo() {
}
public simplePojo(String name, Integer age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public Integer getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "simplePojo{name = " + name + ", age = " + age + "}";
}
}
@RestController
class ControllerSimplePojo{
// 实体参数
@RequestMapping("/simplePojo")
public String simplePojo(simplePojo simplePojo){
// 访问如下地址: http://localhost:8080/simplePojo?name=萧寂&age=18
System.out.println(simplePojo); // simplePojo{name = 萧寂, age = 18}
// 访问如下地址: http://localhost:8080/simplePojo?age=18
// 不传哪个哪个就为null
System.out.println(simplePojo); // simplePojo{name = null, age = 18}
return "OK";
}
}
数组集合参数
@RequestMapping("/arrayHobby")
public String arrayHobby(@RequestParam("hobby") String[] hobby){
// 请求方式get,地址为:http://localhost:8080/arrayHobby?hobby=篮球&hobby=跳绳&hobby=足球
System.out.println(Arrays.toString(hobby)); // [篮球, 跳绳, 足球]
return "OK";
}
// 使用集合接收
@RequestMapping("/listHobby")
public String listHobby(@RequestParam("hobby") List<String> hobby){
// 请求方式get,地址为:http://localhost:8080/listHobby?hobby=篮球&hobby=跳绳&hobby=足球
System.out.println(hobby); // [篮球, 跳绳, 足球]
return "OK";
}
日期参数
@RequestMapping("/dateParams")
public String dateParams(@RequestParam("updateTime") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime){
// 请求方式get,地址为:http://localhost:8080/dateParams?updateTime=2024-04-27 10:00:25
System.out.println(updateTime); // 2024-04-27T10:00:25
return "OK";
}
JSON参数
package com.jwz.sprdemo02.json;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
class Address {
private String province;
private String city;
public Address(String province, String city) {
this.province = province;
this.city = city;
}
public Address() {
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
}
class User {
private String name;
private Integer age;
private Address address;
public User() {
}
public User(String name, Integer age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public Integer getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(Integer age) {
this.age = age;
}
/**
* 获取
* @return address
*/
public Address getAddress() {
return address;
}
/**
* 设置
* @param address
*/
public void setAddress(Address address) {
this.address = address;
}
public String toString() {
return "User{name = " + name + ", age = " + age + ", address = " + address + "}";
}
}
@RestController
class ControllerUser{
@RequestMapping("/jsonParams")
public String jsonParams(@RequestBody User jsonParams){
// 接收到的参数交给User去处理
// 请求方式post,地址为:http://localhost:8080/jsonParams 参数在apifox的body的json里面填写json数据{"name":"萧寂","age":16,"address":{"province":"北京","city":"北京"}}
// 这里有个小问题,json数据格式和raw的一样,如果在json填写数据后再点击raw然后去发请求,此时参数是raw里面的,我这里返回来了415错误,也就是说在json里面写完参数必须直接发请求,不要切换到其他栏,都会报错
System.out.println(jsonParams); // User{name = 萧寂, age = 16, address = Address{province='北京', city='北京'}}
return "OK";
}
}
路径参数
就是http 😕/localhost:8080/add/1/123 这个1和123就是路径上面携带的参数
@RequestMapping("/pathParams/{id}/{preId}")
public String pathParams(@PathVariable("id") Integer id,@PathVariable("preId") Integer preId){
// 请求方式get,地址为:http://localhost:8080/pathParams/1/1234
System.out.println(id+":"+preId); // 1:1234
return "OK";
}
封装统一的返回格式
{
code:0,
msg:"成功",
data:[...]
}
工具类
package com.jwz.sprdemo02.controller;
public class Result {
private Integer code;
private String msg;
private Object data;
public Result() {
}
public Result(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
/**
* 获取
* @return code
*/
public Integer getCode() {
return code;
}
/**
* 设置
* @param code
*/
public void setCode(Integer code) {
this.code = code;
}
/**
* 获取
* @return msg
*/
public String getMsg() {
return msg;
}
/**
* 设置
* @param msg
*/
public void setMsg(String msg) {
this.msg = msg;
}
/**
* 获取
* @return data
*/
public Object getData() {
return data;
}
/**
* 设置
* @param data
*/
public void setData(Object data) {
this.data = data;
}
// 响应成功调用
// 这里使用了方法重载,成功的情况分为三种(自定义成功后的消息内容和接口返回的数据,使用默认的success文字和接口返回的数据,还有一种情况只响应成功结果但不需要返回数据)
public static Result success(Object data,String msg){
return new Result(1,msg,data);
}
public static Result success(Object data){
return new Result(1,"success",data);
}
public static Result success(){
return new Result(1,"success",null);
}
// 响应失败调用
// 失败只会有一种情况(自定义提示失败的消息,数据为null)
public static Result error(String msg){
return new Result(0,msg,null);
}
public String toString() {
return "Result{code = " + code + ", msg = " + msg + ", data = " + data + "}";
}
}
三层架构
项目结构
dao daoImpl service serviceImpl controller
dao和service均是接口
daoImpl和serviceImpl和controller是三层架构的核心
daoImpl负责数据访问操作 serviceImpl负责数据的逻辑层处理 controller负责接收数据响应数据
这里我需要实现一个逻辑,运用三层架构的思路实现
浏览器访问参数num为多少,则返回当前传入的num的值+1
1.项目三层架构的结构如下
2.对各层进行代码编写,从前往后写
dao代码如下
package com.jwz.sprdemo02.dao.service.dao.dao;
public interface dao {
public Integer daoA(Integer num);
}
daoImpl代码如下
package com.jwz.sprdemo02.dao.service.dao.dao;
public class daoImpl implements dao{
@Override
public Integer daoA(Integer num) {
// 这里用于对数据进行初始化和访问
// 这里是做demo,我返回的值是传入进来的数据,后期要拿这个传入的参数做数据库查询数据,查询到了返回出去的
return num;
}
}
service代码如下
package com.jwz.sprdemo02.dao.service.dao.service;
public interface service {
public String serviceA(Integer num);
}
serviceImpl代码如下
package com.jwz.sprdemo02.dao.service.dao.service;
import com.jwz.sprdemo02.dao.service.dao.dao.daoImpl;
public class serviceImpl implements service{
// 由于这里service和dao未关联,所以拿不到dao的数据,因此要在这里创建daoImpl的对象,用于拿到数据
private daoImpl dao = new daoImpl();
@Override
public String serviceA(Integer num) {
// 这里用于对数据进行业务的处理
// 例如,调用daoImpl里面的方法,拿到初始化访问到的数据
// 其实这里已经拿到传入的参数值了,我其实可以直接返回这个数据的,但是要演示三层架构功能还是把参数传给dao,让dao返回数据给我们,后期学完数据库后,这些参数传给dao用于进行数据查询,然后会返回查询到的数据,这里就先模拟下,传入num就返回num了,写个注释避免被说多此一举
Integer i = dao.daoA(num);
int sum = i+1;
return "传入的num+1的值是"+sum;
}
}
分层解耦
内聚:软件中各个功能模块内部的功能联系。
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合。
控制反转: Inyersion OfControl,简称I0C。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
依赖注入:DependencyIniection,简称Dl。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
Bean对象:I0C容器中创建、管理的对象,称之为bean。
依赖注入和控制反转
上面的三层架构的案例,如果我们的serviceImpl改了个名字比如叫serviceImpl1则controller层的代码也要改变,当项目架构比较大,项目比较多的话,也就相当于所有依赖serviceImpl的类都要去手动修改,使项目变得难以维护,依赖注入控制反转的思想就是将所有的实现类Impl(daoImpl和serviceImpl)的类上面都加个@Component注解,这样的话相当于把所有的实现类都加入到容器内部了,在我们需要创建这个实现类的时候,只需要在这个代码上面加一个@Autowired即可,这样就实现了自动注入,即即使代码改了名,项目都是去容器内部去自动注入的,而改代码的实现类又被加入到容器内部了,相当于可以自动更新了,这样的话项目维护成本就低了
将上面的三层架构的daoImpl和serviceImpl和controller代码进行小小改造,改造如下
daoimpl
package com.jwz.sprdemo02.dao.service.dao.dao;
import org.springframework.stereotype.Component;
@Component //将当前类交给IOC容器管理,成为IOC容器中的bean
public class daoImpl implements dao{
@Override
public Integer daoA(Integer num) {
// 这里用于对数据进行初始化和访问
// 这里是做demo,我返回的值是传入进来的数据
return num;
}
}
serviceImpl
package com.jwz.sprdemo02.dao.service.dao.service;
import com.jwz.sprdemo02.dao.service.dao.dao.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
//将当前类交给IOC容器管理,成为IOC容器中的bean
@Component
public class serviceImpl implements service{
// 由于这里service和dao未关联,所以拿不到dao的数据,因此要在这里创建daoImpl的对象,用于拿到数据
@Autowired //运行时,IOC容器会提供该类型的bean对象,并值给该变依赖注入
private dao dao; // 类型是接口的类型(多态写法)
@Override
public String serviceA(Integer num) {
// 这里用于对数据进行业务的处理
// 例如,调用daoImpl里面的方法,拿到初始化访问到的数据
Integer i = dao.daoA(num); // 1234
int sum = i+1;
return "传入的num+1的值是"+sum; // 1235
}
}
controller
package com.jwz.sprdemo02.dao.service.dao.controller;
import com.jwz.sprdemo02.dao.service.dao.service.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class controller {
// 创建serviceImpl对象
@Autowired //运行时,IOC容器会提供该类型的bean对象,并赋值给该变量 -- 依赖注入
private service s; // 类型是接口类型
// 定义接口,请求和响应数据
@RequestMapping("/helloNum")
public String hello(@RequestParam("num") Integer num) {
String i = s.serviceA(num);
return i;
}
}
@Component的注解有三个不同的衍生注解,@Repository,标注daoImpl数据访问层,@Service标注业务实现层,@Controller标注在数据响应层,建议不属于这三类的层再使用@Component注解,上面的衍生注解功能和@Component一致,主要为了区分业务
Bean注入存在的问题
- @Autowired注解,默认是按照类型进行,如果存在多个相同类型的bean,将会报出如下错误:
可以通过以下方案来解决
-
@Primary (想让哪个Bean生效就在哪个Bean上面价格@Primary,如下
-
package com.jwz.sprdemo02.dao.service.dao.service; import com.jwz.sprdemo02.dao.service.dao.dao.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Primary //将当前类交给IOC容器管理,成为IOC容器中的bean @Component public class serviceImpl implements service{ // 由于这里service和dao未关联,所以拿不到dao的数据,因此要在这里创建daoImpl的对象,用于拿到数据 @Autowired //运行时,IOC容器会提供该类型的bean对象,并值给该变依赖注入 private dao dao; // 类型是接口的类型(多态写法) @Override public String serviceA(Integer num) { // 这里用于对数据进行业务的处理 // 例如,调用daoImpl里面的方法,拿到初始化访问到的数据 Integer i = dao.daoA(num); // 1234 int sum = i+1; return "传入的num+1的值是"+sum; // 1235 } }
-
-
@Qualifier(在使用bean时上面价格@Qualifier注解,表明使用哪个bean,如下)
-
package com.jwz.sprdemo02.dao.service.dao.controller; import com.jwz.sprdemo02.dao.service.dao.service.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class controller { // 创建serviceImpl对象 @Qualifier("serviceImpl") // bean名字,不指定的话默认类名首字母小写 @Autowired //运行时,IOC容器会提供该类型的bean对象,并赋值给该变量 -- 依赖注入 private service s; // 类型是接口类型 // 定义接口,请求和响应数据 @RequestMapping("/helloNum") public String hello(@RequestParam("num") Integer num) { String i = s.serviceA(num); return i; } }
-
-
@Resource(功跟autowire一样,只是autowire是自动注入,而Resource是根据Bean名称进行注入的)
-
package com.jwz.sprdemo02.dao.service.dao.controller; import com.jwz.sprdemo02.dao.service.dao.service.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class controller { // 创建serviceImpl对象 @Resource(name = "serviceImpl") // bean名字,不指定的话默认类名首字母小写 private service s; // 类型是接口类型 // 定义接口,请求和响应数据 @RequestMapping("/helloNum") public String hello(@RequestParam("num") Integer num) { String i = s.serviceA(num); return i; } }
-
Mysql相关
mysql所有相关命令
-- 终端连接远程mysql命令
-- mysql -h远程地址 -P端口号 -u用户名 -p密码
mysql -h139.196.214.124 -P3306 -uceshaiaa -pxtZA8p7xEKBWLd6Wnd
-- 一:DDL部分相关命令(下述语法中的database,也可以替换成schema。如:create schema db01;select schema();)
-- 1:数据库操作
-- 1.1 查询
-- 1.1-1 查询所有的数据库
show databases;
-- 1.1-2 查询当前数据库
select database();
-- 1.2 使用
-- 1.2-1 使用数据库
use 数据库名;
-- 1.3 创建
-- 1.3-1 创建数据库(if not exists这是可选的,如果加上则代表:数据库存在不会报错也不会做任何操作,如果数据库不存在则会去创建)
create database [ if not exists ] 数据库名;
-- 1.4 删除
-- 1.4-1 删除数据库(if exists是可选参数,数据库不存在则不会做任何操作,也不会报错,如果存在就会去执行删除数据库操作)
drop database [ if exists ] 数据库名;
-- 2:表操作(带[]的都是可选参数)
-- 2.1 创建表
create table 表名(字段1 字段类型 [约束] [comment 字段1注释],.....,字段n 字段类型 [约束] [comment 字段n注释])[comment 表注释];
-- 2.1-1 示例(id主键自增,用户名非空且唯一,姓名非空,性别默认为男,都是加了约束的)
create table user(
id int primary key auto_increment comment 'ID字段,唯一标识',
username varchar(20) not null unique comment '用户名',
name varchar(10) not null comment '姓名',
age int comment '年龄',
gender char(1) default '男' comment '性别'
) comment '用户表';
-- 2.2 查询表
-- 2.2-1 查询当前数据库的所有表
show tables;
-- 2.2-2 查询表结构
desc 表名;
-- 2.2-3 查询建表语句
show create table 表名;