Spring
官网HTTPs://spring.io
spring生态(全家桶)基于Spring Framework基础框架。但如果我们基于该基础框架开发,会面临配置繁琐,入门难度大的问题,SpringBoot则可以快速开发(简化配置,快速开发)。
1.SpringBootWeb入门
使用SpringBoot开发一个Web应用,浏览器发起请求/hello之后,给浏览器返回字符串"hello world~"
步骤:
-
创建SpringBoot工程,并勾选web开发相关依赖(New Project/Module的时候Spring Initializer)。
-
定义HelloController类,添加方法hello,并添加注解
@RestController public class HelloController { @RequestMapping("/hello") public String hello(){ System.out.println("Hello World~"); return "Hello World~"; } }
-
运行测试
2.HTTP协议
2.1HTTP概述(简略)
规定了浏览器与服务器之间数据传输的规则
基于tcp面向连接;
基于请求响应模型:一次请求一次响应;
http是无状态的协议:对事物处理没有记忆能力。每一次请求响应都是独立的(优点:速度快;缺点:多次请求之间不能共享数据)——可以通过web会话技术解决记忆问题(比如用户是否处于登录状态)。
2.2HTTP请求协议
GET /hello HTTP/1.1 请求行:方式,路径,协议
以下为请求头
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 浏览器接受的资源类型:text/*表示文本,image/*表示图片,*/*表示所有
Accept-Encoding: gzip, deflate, br 浏览器支持的压缩类型
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 浏览器偏好的语言
Cache-Control: max-age=0
Connection: keep-alive
Host: localhost:8080 主机名
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0 浏览器类型
sec-ch-ua: "Chromium";v="122", "Not(A:Brand";v="24", "Microsoft Edge";v="122"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
常见的请求头还包括:content-length请求主体大小,content-type请求主题数据类型,例如json
请求体是post请求特有的部分,post分为请求行,请求头,请求体;而get只有前两个部分?k=v&k=v
2.3HTTP响应协议
HTTP/1.1 200 相应行,响应状态码
Content-Type: text/html;charset=UTF-8
Content-Length: 12
Date: Mon, 25 Mar 2024 06:48:18 GMT
Keep-Alive: timeout=60
Connection: keep-alive
2.4HTTP协议解析
协议解析(浏览器作为客户端是不需用我们去解析协议的,而服务端则需要按照请求格式对http协议进行解析,并按照响应格式,对http协议进行响应)
服务端开发工程师用serverSocket接收请求,按照字符串组成规则进行解析。手写解析http的代码IO加socket是可以实现的,但很复杂。由于格式固定,就可以用jetty,weblogic以及tomcat等写好的软件进行解析。
3.Web服务器-Tomcat
web服务器是一个软件程序,对http协议的操作进行封装,使程序员不必直接对协议进行操作,使web开发更搞笑。主要功能是提供网上信息浏览服务
springboot已经内置了tomcat
3.1 tomcat简介
apache
支持少量javaEE规范servlet/jsp
也被成为web容器,servlet容器
3.2 基本使用
官网安装绿色版本之后改成gbk编码(conf/logging)
startup.bat双击就可以运行脚本文件/停止的话shutdown.bat
想要改端口号(conf/server.xml)端口号改为80(http默认端口的话,输入url就不用输入端口号了)
将项目放在webapps目录下
不用安装了,因为SpringBoot已经内置了
3.3 入门程序解析
maven有许多起步依赖starter,比如spring-boot-starter-aop,spring-boot-starter-web等(其余许多被依赖传递,而web就依赖了内嵌的tomcat)
本来只是自己编写一个Controller类,tomcat不识别;但是tomcat识别servlet(一项javaEE规范里的技术)DispatcherServlet(SpringBoot底层支持的一个核心的Servlet接口的实现类,称之为核心控制器或者前端控制器)
请求响应都会先经过Dispatcher:tomcat将接受到的协议信息进行解析,并将结果封装在一个httpServletRequest对象中,应用程序就可通过该对象获取请求数据了,然后对于该数据进行处理,处理完了之后再响应httpServletResponse。
BS架构(broswer-server)用户只要一个browser
CS架构,需要安装客户端。
4.请求响应
4.1请求
controller中获取请求数据(参数)
接口测试工具,postman。前后端分离之后,后端接口测试需求,浏览器直接地址测试只能get,又得单独去写前端post。
postman是一款功能强大的网页调试与发送网页http请求的chrome插件;作用:常用于进行接口测试(有一些apipost以及apifox的衍生大差不差)
可以下载vscode同名插件,可以用网页版(注意网页版访问本地地址需要下载代理),也可以下载到本地。
简单参数:
原始方式接收
@RestController
public class RequestController {
@RequestMapping("/simpleParam")
public String simpleParameter(HttpServletRequest request){
String name = request.getParameter("name");
String age = request.getParameter("age");
System.out.println("name:"+name+"&"+"age:"+age);
return "ok";
}
}
get方式request的时候
post方式request的时候
如果想操作上述获得的数据,还需要手动类型转换和分开写接受,比较麻烦;但是有更为简单的方式:基于SpringBoot框架。
@RestController
public class RequestController {
@RequestMapping("/simpleParam")
public String simpleParameter(String name,Integer age){//注意参数名与请求数据名保持一致!!!
//参数名对应不上的话,不会报错,但是值为null
//参数名不一致想要修改,需要使用@RequestParam(name = "name")String username来进行映射
//@RequestParam的属性required默认为true,表示参数必须传递,否则会报错
//age是Integer会自动类型转换
System.out.println("name:"+name+"&"+"age:"+age);
return "ok";
}
}
实体参数(将上述参数封装到一个对象中去),要求对象属性名与参数名保持一致
@RequestMapping("/simplePO")
public String simplePO(User user){
System.out.println(user);
return "OK";
}
public class User {
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在之前的类中写方法并写对应User类。
注意:如果是复杂实体,一个对象的属性就是对象,例如user有属性是Address类的实体address;则需要按照address.province,address.city传递
数组集合参数(B端传递的时候如果有多选,例如hobby可以勾选java以及game,就可以是?hobby=java&hobby=game)
服务器端就使用数组或集合来接收(默认用数组接收,但是如果要用集合来接受的话就需要用注解@RequestParam)
@RequestMapping("/arrayParam")
public String arrayParam(String[] hobby){
System.out.println(Arrays.toString(hobby));
return "OK";
}
@RequestMapping("/listParam")
public String listParam(@RequestParam List<String> hobby){
System.out.println(hobby);
return "OK";
}
时期时间类,封装到Date或1.8之后提供的LocalDateTime中由于前端日期格式也多种多样,则需要通过注解@DateTimeFormat(pattern = …)
注意:同样要求名字一致。
@RequestMapping("/dateParam")
public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime time){
System.out.println(time);
return "OK";
}
json格式
B端效果如下:
S端则需要通过对象接收,接收对象属性名需要与key保持一致,同时需要加注解@RequestBody
@RequestMapping("/jsonParam")
public String jsonParam(@RequestBody User user){
System.out.println(user);
return "OK";
}
路径参数,既是路径的一部分也是参数(like:localhost:8080/path/1),S端需要使用@PathVariable注解
//like:localhost:8080/path/1
@RequestMapping("/path/{id}")//用id来接收
public String pathParam(@PathVariable Integer id){//上面是id,这里就业的事id
System.out.println(id);
return "OK";
}
注意如果想要多个路径,like:localhost:8080/path/1/marc
//like:localhost:8080/path/1/marc
@RequestMapping("/path/{id}/{name}")//用id来接收
public String pathParam(@PathVariable Integer id,@PathVariable String name){//上面是id,这里就业的事id
System.out.println(id+","+name);
return "OK";
}
4.2响应
controller程序中获取响应数据
关键:ResponseBody注解
类型:类注解,方法注解
作用:将方法的返回值直接返回给B端,如果方法的返回值类型是对象或者是集合,将会先将其转换为json格式的数据再响应给客户端浏览器
注意:@RestController=@Controller+@ResponseBody(这也是在之前写请求代码的时候,类前面加了RestController就没加ResponseBody注解还依旧可以响应的原因),@RestController源码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(
annotation = Controller.class
)
String value() default "";
}
响应示例:
@RestController
public class ResponseController {
@RequestMapping("/helloResp")
public String hello(){
System.out.println("Hello World~");
return "HelloWorld~";
}
}
响应体:
HTTP/1.1 undefined
Content-Type: text/plain;charset=UTF-8
Content-Length: 11
Date: Mon, 25 Mar 2024 16:56:51 GMT
Keep-Alive: timeout=60
Connection: keep-alive
HelloWorld~
注意:如果返回值是一个实体,则会转为json格式再返回;返回集合的话,如list,和返回数组的json格式一样,这里就不举例了。
4.2.1统一响应
如果按照之前每个函数作为api,api的返回值各不相同,且自由度太高,会造成项目管理难度太大。
故而想出一种统一的返回值
public class Result{
//响应码,1代表成功,0代表失败
private Integer code;
//提示信息
private String msg;
//返回的数据
private Object data;
//...
}//然后把所有方法的返回值统一为result
改造如下:
public class ResponseController {
@RequestMapping("/helloResp")
public Result hello(){
System.out.println("Hello World~");
return new Result(1,"success","Hello World");//可以在Result里面加静态方法返回new Result
}
}
4.3分层解耦
依照上述方式写代码(如果要,将前后端分离+dom4j解析xml中的数据)依旧复用性差。难以维护。
->分层解耦
4.3.1 web开发三层架构
数据访问;逻辑处理;接受请求,响应数据
->分开(单一职责原则)
- Controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据
- Service:业务逻辑层,处理具体的业务逻辑
- Dao:数据访问层(持久层),负责数据访问操作,包括数据的增删改查(面向接口编程,获取不同类型的数据)
1->2->3->db/file
1<-2<-3<-db/file
4.3.2 IOC & DI入门
内聚:指的是软件中各个功能模块内部的功能联系。
耦合:衡量软件中各个层、模块之间的依赖关联程度。
controller层调用service层的实现类,所以一旦其类名发生变化就程序整体就会出错,称之为耦合。
设计原则是高内聚低耦合(模块内部功能联系越紧密越好,但是模块之间、层与层之间的耦合降低->最好是解除耦合
设置一个容器,把所需对象放在容器中,容器再为依赖提供所需资源。(实现起来需要用到Spring中两个重要的概念-控制反转/依赖注入)
- IOC:inversion of control,控制反转。把对象的创建控制权由程序自身转移到 外部(IOC容器或者叫Spring容器)
- DI:dependency injection,依赖注入。容器为应用程序提供运行时,所需要依赖的资源。
- Bean对象:IOC容器中创建、管理的对象,称之为Bean。
将service以及dao的实现类交给IOC管理->加上类注解@Component;删掉Controller以及Service对于Service和Dao实现类的new(只声明,不实例化)然后在成员变量上加上Autowired注解,表示运行时,IOC容器会提供该类型的bean对象,并赋值给该变量,称之为依赖注入。
这样是实现解耦的好处是,如果要切换Service实现类,主需要给新的类前面加Component注解,并注释掉之前的
4.3.3 IOC
Spring框架为了更好地标识bean对象到底归属于哪一层,还提供了Component的三个衍生注解——@Controller,@Service,@Repository(一般不属于三层的时候,用Component,一般是一些工具类;且Repository用得少了,与MyBatis整合)
(Service等衍生注解都只是封装了一个Component。)
bean对象命名
bean对象默认为类名首字母小写,也是可以人为规定名字like:@Repository(value="daoA")// or @Repository("daoA")
->可以通过IDEA 的Actuator->Beans查看(老版本为Endpoints->Beans)
注意
注意之前提到的RestController等价于ResponseBody和Controller两个注解;注意四个注解都可以声明bean,但是在SpringBoot集成Web中,声明控制器bean只能用@Controller.
组件扫描注解
声明的bean,不一定会生效,涉及bean组件扫描。想要生效,还需要被组件扫描注解@ComponentScan扫描(Spring框架提供的用来扫描前面所说的四个注解的)
之前没有显示配置ComponentScan注解是因为已经被包含在了启动类声明注解@SpringBootApplication中,默认扫描范围是启动类所在包及其子包。
也就是说,如果一个IOC注解写在了包外,就需要,自己在启动类前加ComponentScan注解,该注解是可以通过数组表示路径,来确定包名(但是要注意如果自己加了ComponentScan,原有的隐式就被覆盖,还需要再加包名)。
例如@ComponentScan({"dao","com.javaLearning"})
4.3.4 DI
对于autowired自动装配,默认按照类型装配,如果有多个类型的bean在容器中且类型符合要求,就会报错。解决方案有三种:
- @Primary:设置bean的优先级,谁优先,给谁加,在component处。
- @Qualifier:在装配处Autowired前面加的注解,指定bean对象名赋给value,like:
@Qualifier("serviceValName")
- @Resource:去掉Autowired注解,直接
@Resource(name = "beanName")
->前面的注解都是有Spring提供的,本注解是由jdk提供;与Autowired的不同是,Resource默认按照名称注解,而Autowired默认是按照类型注解。