java-web/docs/chapter05.md
2024-08-07 01:40:55 +08:00

495 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 5. 基于Spring框架的后端应用开发
### 5.1 Java基础增强(掌握)
#### 5.1.1 Java异常处理
- **定义**: 异常是在程序执行过程中发生的问题或意外情况。
- **java异常处理结构**:
![](./resources/exception-hierarchy.png)
> Throwable类是Java所有错误和异常的父类。
> 非检查异常类
>> Error 和 RuntimeException 以及他们的子类
>
>> 通俗得理解就是:这些异常,和自己写的代码逻辑有很大关系,这类异常在编译得时候一般能通过,在程序运行过程中会抛出这些异常 如IndexOutOfBoundsException NullPointerException
> 检查异常类
>> 除了Error 和 RuntimeException以及他们的子类外的其它异常类
>
>> 可查异常虽然是异常状况,但在一定程度上它的发生是可以预计的,而且一旦发生这种异常状况,就必须采取某种方式进行处理。
这种异常的特点是Java编译器会检查它也就是说当程序中可能出现这类异常要么用try-catch语句捕获它要么用throws子句声明抛出它否则编译不会通过。
- **java异常使用**:
关键词: try、catch、finally、throw、throws
try-catch-finally: 将可能要出现异常的代码放入try中catch 捕获 try 中的异常,并处理,不管有没有异常finally中的代码都会执行。(finally不是必须)
throw: 一般是语句抛出一个异常
throws:一般是方法抛出一个异常
- **自定义异常类**
一般自定义异常会继承Exception或RuntimeException类。
但具体继承哪一个类呢 还是要视情况而定,若继承受检查的异常,会与方法耦合若不抛异常就要try-catch操作不够灵活;
若继承RuntimeException 就相对上面的就简洁多了。
#### 5.1.2 注解(了解)
参考[java注解是怎么实现的?](https://www.zhihu.com/question/24401191)
- **定义**:
> java注解又叫做java标注是java的一种注释机制在jdk5.0引入。其可以作用在类、方法、变量、参数和包上。注解是用于向编译器或框架提供元数据的一种机制。
- **注解定义的语法**
```java
public @interface 注解名称{
属性列表;
}
```
- **类型**:
- **元注解**: 用于修饰其他注解。
- **内置注解**: 如`@Override`, `@Deprecated`, `@SuppressWarnings`等。
- **自定义注解**: 自定义注解就是我们自己写的注解,来满足特定需求。
- **第三方框架定义的注解**: 比如SpringMVC的@Controller等,也可归于自定义注解。
- **使用场景**:
- **元数据**: 为框架提供元数据。
- **代码生成**: 用于生成代码。
- **编译时处理**: 用于编译时的代码处理。
- **使用位置**
实际开发中,注解常常出现在类、方法、成员变量、形参位置。当然还有其他位置。
- **作用**
如果说注释是写给人看的,那么注解就是写给程序看的。它更像一个标签,贴在一个类、一个方法或者字段上。它的目的是为当前读取该注解的程序提供判断依据及少量附加信息。比如程序只要读到加了@Test的方法就知道该方法是待测试方法又比如@Before注解程序看到这个注解就知道该方法要放在@Test方法之前执行。有时我们还可以通过注解属性为将来读取这个注解的程序提供必要的附加信息比如@RequestMapping("/user/info")提供了Controller某个接口的URL路径。
- **级别**
注解和类、接口、枚举是同一级别的。注解的本质@interface和interface从名字上看非常相似。
- **注解三角**
要牢记,只要用到注解,必然有三角关系:
- 定义注解
- 使用注解
- 读取注解
**小结**
- 注解就像标签,是程序判断执行的依据。比如,程序读到@Test就知道这个方法是待测试方法而@Before的方法要在测试方法之前执行
- 注解需要三要素:定义、使用、读取并执行
- 注解分为自定义注解、JDK内置注解和第三方注解框架。自定义注解一般要我们自己定义、使用、并写程序读取而JDK内置注解和第三方注解我们只要使用定义和读取都交给它们
- 大多数情况下,三角关系中我们只负责使用注解,无需定义和执行,框架会将注解类和读取注解的程序隐藏起来,除非阅读源码,否则根本看不到。平时见不到定义和读取的过程,光顾着使用注解,久而久之很多人就忘了注解如何起作用了!
#### 5.1.3 Optional的介绍和使用(了解)
- **定义**: `Optional`是Java 8引入的一个容器类用于避免null值。
- **用途**:
- **封装可能为null的对象**: 避免空指针异常。
- **提供链式操作**: 支持方法链式调用。
- **示例**:
```java
Optional<String> optional = Optional.ofNullable("Hello, World!");
optional.ifPresent(System.out::println);
```
#### 5.1.4 Stream API的介绍和使用(了解)
- **定义**: Stream API是Java 8引入的新特性用于处理集合数据。
- **用途**:
- **数据流处理**: 提供了一种声明式的处理集合数据的方式。
- **并行处理**: 支持并行流处理。
- **示例**:
```java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().filter(n -> n % 2 == 0).mapToInt(Integer::intValue).sum();
System.out.println(sum); // 输出 6
```
#### 5.1.5 泛型(了解)
##### 什么是泛型?
Java 中的泛型是一种通用的编程技术,它可以让我们在写代码时更加灵活、安全和高效。
泛型是为了解决Java中类型安全的问题而被引入的。在早期版本的Java中所有的集合类都是基于Object类型实现的这意味着我们可以将任意类型的对象添加到集合中但是取出时需要进行类型转换如果转换错误就会抛出ClassCastException异常。当我们使用泛型时可以在编译期间检查类型从而避免这类异常的发生。
##### 泛型的优点
- 类型安全性:通过泛型可以在编译时检查类型,避免了运行时出现类型转换异常等问题。
- 代码重用性:通过泛型可以编写通用的代码,提高代码的可重用性和可维护性。
- 性能优化:通过泛型可以避免了不必要的类型转换,提高程序的运行效率。
##### 泛型的语法
泛型主要包括三个部分:泛型类、泛型接口和泛型方法。
###### 泛型类和泛型接口
泛型类和泛型接口是最常见的泛型形式。我们可以通过在类或接口名后面添加一对尖括号来声明一个或多个类型参数。
```java
public class MyClass<T> {
// ...
}
public interface MyInterface<K, V> {
// ...
}
```
当我们使用泛型类或泛型接口时,我们可以使用具体的类型来替换类型参数。
```java
MyClass<String> myClass = new MyClass<>();
MyInterface<Integer, String> myInterface = new MyInterface<>();
```
###### 泛型方法
除了类和接口之外,我们还可以定义泛型方法,它们在方法返回类型前面加上一对尖括号,表示该方法有一个或多个类型参数。
```java
public class MyUtils {
public static <T> T getLast(List<T> list) {
return list.get(list.size() - 1);
}
}
```
当我们使用泛型方法时,我们可以在方法调用时,为类型参数传入具体的类型。
```java
List<String> names = List.of("Alice", "Bob", "Charlie");
String last = MyUtils.<String>getLast(names);
```
##### 泛型的应用
泛型是一种非常通用的编程技术,我们可以用它来编写各种各样的通用数据结构和工具类。
###### List 和 Map
Java 内置的 List 和 Map 类型都支持泛型,它们可以用来存储任意类型的对象。
```java
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
Map<String, Integer> scoreMap = new HashMap<>();
scoreMap.put("Alice", 90);
scoreMap.put("Bob", 80);
scoreMap.put("Charlie", 70);
```
### 5.2 Spring框架简介(了解)
#### 5.2.1 Spring框架的历史和发展
- **起源**: Spring framework最初由Rod Johnson在2003年开始编写并于2004年发布了第一个版本1.0。
- **目标**: 创立Spring framework的主要目的是解决企业级Java应用中的复杂问题特别是解决依赖管理和面向切面编程AOP的问题。
- **版本演进**:
- **1.x**: 初期版本主要关注于依赖注入和面向切面编程。
- **2.x**: 添加了对Web应用程序的支持包括Spring MVC框架。
- **3.x**: 引入了对Java 5特性的支持如注解和泛型并且增强了对RESTful Web服务的支持。
- **4.x**: 增强了对Java 8的支持包括Lambda表达式和Stream API。
- **5.x**: 重点改进了对反应式编程模型的支持引入了Spring WebFlux框架。
- **最新版本**: 截至2024年Spring框架的最新版本为5.x系列持续增加了对现代Web开发趋势的支持如微服务架构、云原生应用等。
#### 5.2.2 Spring框架的核心特性
- **依赖注入 (DI)**:
- **定义**: 是一种设计模式,用于实现对象之间的解耦,让对象不需要直接创建依赖对象,而是由容器负责创建并注入。
- **好处**: 降低了组件之间的耦合度,提高了代码的可测试性和可维护性。
- **面向切面编程 (AOP)**:
- **定义**: 一种编程范式,用于将横切关注点(如日志记录、事务管理)从业务逻辑中分离出来。
- **好处**: 通过切面编程,可以在不修改业务逻辑代码的情况下添加或修改功能。
- **模块化**: Spring框架被设计成高度模块化的用户可以根据需要选择使用不同的模块。
- **可扩展性**: Spring框架提供了许多扩展点允许开发者根据需要自定义行为。
- **广泛的应用场景**: 从简单的Web应用到复杂的企业级应用Spring都能提供解决方案。
#### 5.2.3 Spring的家族框架
- **Spring Boot**: 提供了一个快速搭建Spring应用的方式通过自动化配置、依赖管理和启动器简化了开发过程。
- **Spring Cloud**: 用于构建分布式系统和服务网格,提供了一系列工具和服务,如服务注册与发现、配置中心、熔断器等。
- **Spring Data**: 提供了一套数据访问抽象层,简化了数据访问层的开发工作,支持各种数据库技术。
- **Spring Security**: 为Spring应用提供了全面的安全保护包括认证、授权等功能。
- **Spring Integration**: 提供了基于消息驱动的企业集成模式的支持,适用于构建复杂的集成系统。
- **Spring Batch**: 用于批量处理任务,支持大规模数据处理作业。
- **Spring WebFlux**: 用于构建非阻塞的、事件驱动的Web应用和服务器端应用支持反应式编程模型。
- **Spring Web**: 提供了构建Web应用程序所需的支持包括HTTP消息处理、多部分文件上传等功能以及与Spring MVC的紧密集成。
#### 5.2.4 Spring的应用场景和优势
- **应用场景**:
- **Web应用**: Spring MVC框架为构建高性能的Web应用提供了基础。
- **企业级应用**: Spring框架适合构建复杂的企业级应用包括业务流程管理、数据处理等。
- **微服务**: Spring Boot和Spring Cloud为构建微服务架构提供了便利。
- **移动应用后端**: Spring框架可以作为移动应用后端的服务端框架。
- **优势**:
- **松耦合**: 通过依赖注入实现了组件之间的松耦合。
- **易于测试**: Spring框架的设计使得单元测试变得简单。
- **广泛的社区支持**: 拥有庞大的开发者社区和丰富的文档资源。
- **丰富的生态**: Spring框架有一整套家族产品几乎覆盖了企业级应用的所有方面。
- **灵活性**: Spring框架非常灵活可以很容易地与其他技术栈集成。
- **性能**: Spring框架经过优化能够高效地运行大规模应用。
- **社区活跃**: Spring框架有一个活跃的社区不断更新以适应新的技术和标准。
#### 5.2.5 Spring Web
- **概述**: Spring Web 是 Spring Framework 的一部分它为构建Web应用程序提供了必要的支持。Spring Web 包含了多个子模块旨在提供一套完整的Web开发解决方案。
- **子模块**:
- **Spring Web**: 为Web开发提供了一些基本的支持比如HTTP消息转换、多部分文件上传、Web应用上下文等。
- **Spring Web MVC**: 一个基于Model-View-Controller (MVC) 模式的Web框架用于构建动态Web应用程序。
- **Spring Web Flows**: 一个用于构建复杂的Web应用程序的工作流框架。
- **Spring WebSocket**: 提供了WebSocket的支持用于实现全双工通信。
- **Spring Web Services**: 提供了构建和消费Web服务的支持。
- **关键特性**:
- **HTTP Message Converters**: 提供了HTTP消息转换器用于序列化和反序列化HTTP请求和响应。
- **MultiPart File Upload**: 支持多部分文件上传功能。
- **Web Application Context**: 提供了Web应用程序上下文的支持包括初始化监听器等。
- **MVC Framework**: Spring MVC 提供了一个基于MVC模式的Web框架用于构建动态Web应用程序。
- **File Upload Support**: 支持多部分文件上传,用于处理文件上传请求。
- **Web Services Support**: 支持构建和消费Web服务包括SOAP和RESTful服务。
- **WebSocket Support**: 提供了WebSocket的支持用于实现实时双向通信。
### 5.3 Spring IoC(掌握)
#### 5.3.1 Spring Bean的定义和生命周期
- **定义**: Spring Bean是Spring容器管理的对象。
- **生命周期**:
- **初始化**: 包括默认初始化、自定义初始化等。
- **销毁**: 包括默认销毁、自定义销毁等。
- **示例**:
```java
@Component
public class MyBean {
public MyBean() {
System.out.println("MyBean created.");
}
@PostConstruct
public void init() {
System.out.println("MyBean initialized.");
}
@PreDestroy
public void destroy() {
System.out.println("MyBean destroyed.");
}
}
```
#### 5.3.2 XML和注解方式的Bean配置
- **XML配置**:
- **定义Bean**: `<bean>`元素定义一个Bean。
- **依赖注入**: 使用`<property>`元素注入依赖。
- **注解配置**:
- **组件扫描**: 使用`@ComponentScan`注解来自动发现和配置组件。
- **依赖注入**: 使用`@Autowired`、`@Qualifier`等注解来注入依赖。
- **示例**:
```java
@Service
public class MyService {
private final MyRepository repository;
@Autowired
public MyService(MyRepository repository) {
this.repository = repository;
}
public void doSomething() {
// 使用repository
}
}
```
#### 5.3.3 Bean的作用域和装配方式
- **作用域**:
- **Singleton**: 单例模式每个Spring容器中只有一个实例。
- **Prototype**: 原型模式,每次请求都会创建一个新的实例。
- **Request**: 每次HTTP请求都会创建一个新的实例。
- **Session**: 每个HTTP Session都会创建一个新的实例。
- **装配方式**:
- **构造器注入**: 通过构造器参数注入依赖。
- **setter注入**: 通过setter方法注入依赖。
- **属性注入**: 通过属性直接注入依赖。
#### 5.3.4 使用Spring管理依赖关系
- **定义**: Spring通过依赖注入来管理对象之间的依赖关系。
- **目的**: 降低组件之间的耦合度,提高代码的可测试性和可维护性。
- **示例**:
```java
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
```
### 5.4 Spring MVC(掌握)
#### 5.4.1 Spring MVC概述
- **定义**: Spring MVC是Spring web框架的一部分基于Model-View-Controller (MVC) 模式,用于构建Web应用。
- **特点**:
- **请求处理**: 通过@Controller注解来定义控制器。
- **视图解析**: 通过ViewResolver来解析视图。
#### 5.4.2 使用@Controller定义控制器
- **定义**: 使用`@Controller`注解定义控制器类。
- **示例**:
```java
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello, Spring MVC!");
return "hello";
}
}
```
#### 5.4.3 使用@RequestMapping处理请求
- **定义**: 使用`@RequestMapping`注解来映射请求路径。
- **示例**:
```java
@Controller
public class HelloController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello(@RequestParam String name, Model model) {
model.addAttribute("message", "Hello, " + name + "!");
return "hello";
}
}
```
#### 5.4.4 使用@ResponseBody返回JSON响应
- **定义**: 使用`@ResponseBody`注解来直接返回JSON响应。
- **示例**:
```java
@Controller
public class HelloController {
@RequestMapping(value = "/hello-json", method = RequestMethod.GET)
@ResponseBody
public Map<String, String> helloJson(@RequestParam String name) {
Map<String, String> response = new HashMap<>();
response.put("message", "Hello, " + name + "!");
return response;
}
}
```
#### 5.4.5 使用Spring Data JPA进行数据访问
- **定义**: Spring Data JPA是Spring Data项目的一部分用于简化JPA的数据访问。
- **示例**:
```java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
```
### 5.5 Spring AOP(了解)
#### 5.5.1 AOP的基本概念
- **定义**: AOP是一种编程范式用于将横切关注点从业务逻辑中分离出来。
- **用途**:
- **日志记录**: 在方法执行前后记录日志。
- **事务管理**: 管理方法的事务边界。
- **安全控制**: 控制方法的访问权限。
- **术语**
- **切点 (Pointcut)**: 定义了哪些连接点将被执行通知。
- **连接点 (Joinpoint)**: 程序执行的某个特定点,如方法调用。
- **通知 (Advice)**: 在连接点处执行的动作,如前置通知、后置通知等。
- **切面 (Aspect)**: 包含切点和通知的组合。
#### 5.5.2 使用AspectJ进行切面编程
- **定义**: AspectJ是一种常用的AOP框架提供了强大的切面编程能力。
- **示例**:
```java
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Executing: " + joinPoint.getSignature());
}
}
```
### 5.6 Spring事务管理(了解)
#### 5.6.1 事务管理的基本概念
- **定义**: 事务是一组操作的集合,要么全部成功,要么全部失败。
- **ACID属性**:
- **原子性 (Atomicity)**: 事务中的所有操作要么全部完成,要么一个也不完成。
- **一致性 (Consistency)**: 事务的执行不会破坏数据的一致性。
- **隔离性 (Isolation)**: 事务之间是隔离的,不会相互影响。
- **持久性 (Durability)**: 一旦事务提交,它的效果是持久的。
#### 5.6.2 Spring事务管理器
- **定义**: Spring提供了多种事务管理器如`DataSourceTransactionManager`。
- **示例**:
```java
@Configuration
@EnableTransactionManagement
public class AppConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
```
#### 5.6.3 使用@Transactional进行声明式事务管理
- **定义**: 使用`@Transactional`注解来声明式地管理事务。
- **示例**:
```java
@Service
@Transactional
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void createUser(User user) {
userRepository.save(user);
}
}
```
#### 5.6.4 使用编程式事务管理
- **定义**: 通过编程的方式显式地管理事务。
- **示例**:
```java
@Service
public class UserService {
private final UserRepository userRepository;
private final PlatformTransactionManager transactionManager;
@Autowired
public UserService(UserRepository userRepository, PlatformTransactionManager transactionManager) {
this.userRepository = userRepository;
this.transactionManager = transactionManager;
}
public void createUser(User user) {
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
userRepository.save(user);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
```