HTTP协议
简介
Hyper Text Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则
- http协议基于TCP协议:面向连接,安全
- 基于请求-响应模型:一次请求对应一次响应
- HTTP协议是无状态的协议:对事务处理没有记忆能力。每次请求-响应都是独立的,优点是速度快,缺点是多次请求间不能共享数据
请求协议
请求行:请求数据第一行:请求方式(get、post)、资源路径、协议
请求头:第二行开始,格式:key:value
常见请求头:
- Host:请求的主机名
- User-Agent:浏览器版本
- Accept:表示浏览器能接受的资源类型,如text/,image/
- Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页
- Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip、deflate
- Content-Type:请求主题的数据类型
- Content-Length:请求主体的大小(单位:字节)
请求体:post请求
,存放请求参数
请求方式-GET:请求参数在请求行中,没有请求体。GET请求大小在浏览器中是有限制的。
请求方式-POST:请求参数在请求体中,POST请求大小是没有限制的。
请求数据获取
浏览器端发送请求数据后,服务器端需要接受并获取请求数据。Web服务器(Tomcat)对http协议的请求数据进行解析,并进行了封装(HttpServletRequest),在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让web开发更加便捷。
响应协议
响应行:响应数据第一行(协议、状态码、描述)
响应头:第二行开始,格式(key:value)
常见响应头
Content-Type:表示响应内容的类型,例如text/html,application/jsonContent-Length:表示改响应内容的长度
Content-Encoding:表示改响应压缩算法,例如gzip
Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒
Set-Cookie:告诉浏览器为当前页面所在的域设置cookie
响应体:最后一部分,存放响应数据
响应数据设置
web服务器对HTTP协议的响应数据进行了封装(HttpServletResponse),并在调用Controller方法的时候传递给了该方法。
分层解耦
三层架构
对代码进行拆分的原因是要遵循单一职责原则,便于复用和后期维护
分层解耦
拆分完三层架构之后会发现,Service调用Dao,Controller调用Service都是通过在本层new一个另外一层的对象来实现功能的调用,这样三层之间的耦合度就比较高,下面需要进行解耦合。
解耦合主要是通过Spring框架两项关键的技术控制反转(Inversion Of Control\IOC)和依赖注入(Dependency Injection \DI)
控制反转IOC:对象的创建控制权由程序自身转移到外部(容器),这种思想为控制反转
依赖注入DI:容器为应用程序提供运行时所以来的资源称之为依赖注入
Bean对象:IOC容器中创建、管理的对象,称之为Bean
下面是如何具体实现分层解耦
- 将Dao及Service层的实现类,交给IOC容器管理Component注解
- 为Controller及Service注入运行时依赖的对象Autowired注解
UserController的代码
@RestController
public class UserController {
@Autowired
private Userservice userservice;
@RequestMapping("/list")
public List<User> list(){
List<User> userList = userservice.findAll();
return userList;
}
}
UserServiceImpl的代码
@Component
public class UserServiceImpl implements Userservice {
@Autowired
private UserDao userDao;
@Override
public List<User> findAll() {
List<String> lines = userDao.findAll();
//2.解析数据,封装成对象 --> 集合
List<User> userList = lines.stream().map(line -> {
String[] parts = line.split(",");
Integer id = Integer.parseInt(parts[0]);
String username = parts[1];
String password = parts[2];
String name = parts[3];
Integer age = Integer.parseInt(parts[4]);
LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
return new User(id, username, password, name, age, updateTime);
}).collect(Collectors.toList());
return userList;
}
}
UserDaoImpl的代码
@Component
public class UserDaoImpl implements UserDao {
public List<String> findAll(){
//1.加载并读取文件
InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
return lines;
}
}
IOC
衍生注解,是为了将类划分到三层架构,哪一层就是用对应的衍生注解
代码中声明bean的四大注解,想要生效,还需要被组件扫描注解@ComponentScan扫描
虽然在上面代码中注解@ComponentScan没有被显示配置,但实际上已经包含在启动类声明注解@SprintBootApplication中,默认扫描的范围时启动类所在包及其子包。
DI
基于**@Autowired**进行依赖注入的常见方式有三种
第一种:属性注入
@RestController
public class UserController {
@Autowired
private UserService userService;
}
优点:代码简洁、方便快速开发
缺点:隐藏了类之间的依赖关系,可能破坏类的封装性
第二种:构造函数注入
@RestController
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
}
优点:能清晰地看到类的依赖关系、提高代码的安全性
缺点:代码繁琐、如果构造参数过多,可能会导致构造函数臃肿
第三种:setter注入
@RestController
public class UserController {
private UserService userService;
@Autowired
public setUserUserService(UserService userService) {
this.userService = userService;
}
}
优点:保证了类的封装性,依赖关系更清晰
缺点:代码繁琐
实战中很多企业会选择基于属性的注入,第一种和第二种用的更多一点
@Autowired是根据类型注入的
如果一个类型有多个Bean对象,则是不允许的,会直接报错
需要指定具体注入哪个Bean对象
@Resoure和@Autowired
@Resoure是javeee中提供的注解,@Autowired是Spring框架中提供的注解
@Resoure默认是按名称注入的,@Autowired默认是按类型注入