第十章:Java Web开发入门(中)- Spring框架和SpringBoot入门
在前面的部分中,我们学习了JSP技术。本部分将介绍Spring框架和SpringBoot,它们是现代Java Web开发中最流行和强大的技术框架,极大地简化了企业级应用的开发。
1. Spring框架入门
1.1 什么是Spring框架
Spring框架是一个开源的Java平台,为开发Java企业级应用提供了全面的基础设施支持。Spring框架的核心特性是依赖注入(DI)和面向切面编程(AOP)。
想象一下,如果你要组装一台电脑,你需要自己找到各种配件(CPU、内存、硬盘等),并将它们正确连接起来。而Spring框架就像一个电脑装配专家,你只需要告诉它你需要什么配件,它会自动帮你找到合适的配件并组装好,让你专注于使用电脑而不是组装电脑。
1.2 Spring框架的核心特性
1.2.1 依赖注入(DI)
依赖注入是Spring最核心的特性,它允许我们将对象之间的依赖关系交给Spring容器管理,而不是在代码中硬编码。
例如,传统方式创建对象:
public class UserService {
private UserDao userDao = new UserDaoImpl(); // 硬编码依赖
// 业务方法
}
使用Spring依赖注入:
public class UserService {
private UserDao userDao; // 不直接创建实例
// 通过构造函数注入
public UserService(UserDao userDao) {
this.userDao = userDao;
}
// 或者通过setter方法注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 业务方法
}
依赖注入的优势:
- 降低组件之间的耦合度
- 更容易进行单元测试
- 更灵活的配置
1.2.2 面向切面编程(AOP)
AOP允许将横切关注点(如日志记录、事务管理、安全控制)与业务逻辑分离,提高代码的模块化程度。
例如,不使用AOP记录方法执行时间:
public void businessMethod() {
long startTime = System.currentTimeMillis();
try {
// 业务逻辑
System.out.println("执行业务逻辑");
} finally {
long endTime = System.currentTimeMillis();
System.out.println("方法执行时间:" + (endTime - startTime) + "ms");
}
}
使用AOP:
// 业务方法 - 只关注业务逻辑
public void businessMethod() {
System.out.println("执行业务逻辑");
}
// 切面 - 处理横切关注点
@Aspect
public class PerformanceAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println(joinPoint.getSignature() + " 执行时间:" + (endTime - startTime) + "ms");
return result;
}
}
1.3 Spring的核心模块
Spring框架由多个模块组成,主要包括:
- Spring Core:提供框架的基础功能,包括IoC和DI
- Spring AOP:提供面向切面编程的实现
- Spring JDBC:简化JDBC编程
- Spring ORM:整合主流ORM框架(如Hibernate、JPA)
- Spring Web:提供Web应用开发的基础功能
- Spring MVC:实现MVC设计模式的Web框架
- Spring Security:提供安全框架
- Spring Data:简化数据访问
1.4 Spring MVC简介
Spring MVC是Spring框架的一个模块,专门用于开发Web应用。它实现了MVC(Model-View-Controller)设计模式,将应用分为模型、视图和控制器三个部分。
1.4.1 Spring MVC的工作流程
- 客户端发送请求到DispatcherServlet(前端控制器)
- DispatcherServlet根据请求找到对应的Handler(Controller)
- Controller处理请求并返回ModelAndView
- ViewResolver解析视图名称,找到对应的View
- View渲染数据,生成响应
- 返回响应给客户端
1.4.2 Spring MVC示例
// 控制器
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello, Spring MVC!");
return "hello"; // 视图名称
}
}
对应的视图(hello.jsp):
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>Spring MVC示例</title>
</head>
<body>
<h1>${message}</h1>
</body>
</html>
1.5 Spring配置方式
Spring提供了多种配置方式:
1.5.1 XML配置
<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 定义bean -->
<bean id="userDao" class="com.example.dao.UserDaoImpl" />
<bean id="userService" class="com.example.service.UserServiceImpl">
<!-- 注入依赖 -->
<property name="userDao" ref="userDao" />
</bean>
</beans>
1.5.2 注解配置
// 配置类
@Configuration
@ComponentScan("com.example")
public class AppConfig {
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
@Bean
public UserService userService() {
return new UserServiceImpl(userDao()); // 构造函数注入
}
}
组件类:
@Repository
public class UserDaoImpl implements UserDao {
// 实现方法
}
@Service
public class UserServiceImpl implements UserService {
private final UserDao userDao;
@Autowired // 自动注入
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
// 业务方法
}
2. SpringBoot入门
2.1 什么是SpringBoot
SpringBoot是Spring框架的扩展,它简化了Spring应用的初始搭建和开发过程。SpringBoot采用了"约定优于配置"的原则,大量减少了配置文件的需求。
如果说Spring框架是一套工具箱,那么SpringBoot就是一个已经预装了常用工具的工具箱,你拿到就可以直接使用,不需要自己去组装。
2.2 SpringBoot的主要特性
- 自动配置:根据项目依赖自动配置Spring应用
- 起步依赖:简化依赖管理
- 内嵌服务器:内置Tomcat、Jetty或Undertow,无需部署WAR文件
- Actuator:提供生产级别的监控和管理功能
- 无需XML配置:使用Java配置或属性文件
2.3 创建SpringBoot应用
2.3.1 使用Spring Initializr
最简单的方式是使用Spring官方提供的Spring Initializr网站生成项目骨架。
2.3.2 使用IDE插件
主流IDE(如IntelliJ IDEA、Eclipse)都提供了SpringBoot项目创建向导。
2.3.3 使用Maven或Gradle
手动创建Maven项目,添加SpringBoot依赖:
<!-- pom.xml -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.4 SpringBoot应用结构
一个典型的SpringBoot应用结构如下:
src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── demo
│ │ ├── DemoApplication.java # 主类
│ │ ├── controller # 控制器
│ │ ├── service # 服务层
│ │ ├── repository # 数据访问层
│ │ └── model # 实体类
│ └── resources
│ ├── application.properties # 配置文件
│ ├── static # 静态资源
│ └── templates # 模板文件
└── test # 测试代码
2.5 SpringBoot核心组件
2.5.1 主类和@SpringBootApplication注解
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@SpringBootApplication
注解是一个组合注解,它包含:
@Configuration
:标记该类为配置类@EnableAutoConfiguration
:启用自动配置@ComponentScan
:启用组件扫描
2.5.2 配置文件
SpringBoot支持多种配置方式,最常用的是application.properties
或application.yml
:
# application.properties
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password
# application.yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
2.5.3 起步依赖
SpringBoot提供了多种起步依赖(Starter),简化依赖管理:
spring-boot-starter-web
:Web应用开发spring-boot-starter-data-jpa
:JPA数据访问spring-boot-starter-security
:安全框架spring-boot-starter-test
:测试框架spring-boot-starter-thymeleaf
:Thymeleaf模板引擎
2.6 SpringBoot实战:RESTful API
下面我们将创建一个简单的图书管理RESTful API。
2.6.1 创建实体类
package com.example.bookstore.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
private String isbn;
private double price;
// 构造函数、getter和setter方法
public Book() {}
public Book(String title, String author, String isbn, double price) {
this.title = title;
this.author = author;
this.isbn = isbn;
this.price = price;
}
// Getter和Setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public String getIsbn() { return isbn; }
public void setIsbn(String isbn) { this.isbn = isbn; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
}
2.6.2 创建Repository
package com.example.bookstore.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.bookstore.model.Book;
public interface BookRepository extends JpaRepository<Book, Long> {
// Spring Data JPA会自动实现基本的CRUD操作
// 可以添加自定义查询方法
Book findByIsbn(String isbn);
}
2.6.3 创建Service
package com.example.bookstore.service;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.bookstore.model.Book;
import com.example.bookstore.repository.BookRepository;
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public List<Book> findAllBooks() {
return bookRepository.findAll();
}
public Optional<Book> findBookById(Long id) {
return bookRepository.findById(id);
}
public Book findBookByIsbn(String isbn) {
return bookRepository.findByIsbn(isbn);
}
public Book saveBook(Book book) {
return bookRepository.save(book);
}
public void deleteBook(Long id) {
bookRepository.deleteById(id);
}
}
2.6.4 创建Controller
package com.example.bookstore.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.example.bookstore.model.Book;
import com.example.bookstore.service.BookService;
@RestController
@RequestMapping("/api/books")
public class BookController {
private final BookService bookService;
@Autowired
public BookController(BookService bookService) {
this.bookService = bookService;
}
// 获取所有图书
@GetMapping
public List<Book> getAllBooks() {
return bookService.findAllBooks();
}
// 根据ID获取图书
@GetMapping("/{id}")
public ResponseEntity<Book> getBookById(@PathVariable Long id) {
return bookService.findBookById(id)
.map(book -> ResponseEntity.ok().body(book))
.orElse(ResponseEntity.notFound().build());
}
// 创建新图书
@PostMapping
public ResponseEntity<Book> createBook(@RequestBody Book book) {
Book savedBook = bookService.saveBook(book);
return ResponseEntity.status(HttpStatus.CREATED).body(savedBook);
}
// 更新图书
@PutMapping("/{id}")
public ResponseEntity<Book> updateBook(@PathVariable Long id, @RequestBody Book book) {
return bookService.findBookById(id)
.map(existingBook -> {
existingBook.setTitle(book.getTitle());
existingBook.setAuthor(book.getAuthor());
existingBook.setIsbn(book.getIsbn());
existingBook.setPrice(book.getPrice());
Book updatedBook = bookService.saveBook(existingBook);
return ResponseEntity.ok().body(updatedBook);
})
.orElse(ResponseEntity.notFound().build());
}
// 删除图书
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteBook(@PathVariable Long id) {
return bookService.findBookById(id)
.map(book -> {
bookService.deleteBook(id);
return ResponseEntity.noContent().<Void>build();
})
.orElse(ResponseEntity.notFound().build());
}
}
2.6.5 配置文件
# application.properties
server.port=8080
# 数据库配置
spring.datasource.url=jdbc:h2:mem:bookstore
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# JPA配置
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
# H2控制台
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
2.6.6 主类
package com.example.bookstore;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BookstoreApplication {
public static void main(String[] args) {
SpringApplication.run(BookstoreApplication.class, args);
}
}
2.7 SpringBoot的高级特性
2.7.1 配置文件属性注入
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private String description;
private int version;
// Getter和Setter
}
对应的配置:
app.name=我的应用
app.description=这是一个示例应用
app.version=1
2.7.2 条件注解
SpringBoot提供了多种条件注解,用于根据条件创建Bean:
@Configuration
public class DatabaseConfig {
@Bean
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "mysql")
public DataSource mysqlDataSource() {
// 创建MySQL数据源
}
@Bean
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "h2")
public DataSource h2DataSource() {
// 创建H2数据源
}
}
2.7.3 自定义启动器
可以创建自己的SpringBoot启动器,封装特定功能:
- 创建自动配置类
- 创建配置属性类
- 注册自动配置类
- 创建启动器模块
总结
在本章中,我们学习了Spring框架和SpringBoot的基础知识。Spring框架通过依赖注入和面向切面编程简化了Java企业级应用的开发,而SpringBoot则进一步简化了Spring应用的配置和部署。
我们了解了Spring的核心特性、主要模块和配置方式,以及SpringBoot的特性、应用结构和核心组件。通过一个图书管理RESTful API的实例,我们实践了SpringBoot的使用。
这些技术在现代Java Web开发中扮演着重要角色,掌握它们将使你能够更高效地开发企业级应用。
在下一部分,我们将学习RESTful API设计和前后端分离开发,这是现代Web应用开发的重要趋势。