Spring框架可以说是Java生态里最重量级的”国民框架”了。如果你刚开始学Java,或者准备找工作,Spring几乎是绕不过去的一道坎。很多初学者看到Spring就头大——XML配置、注解满天飞、各种样板代码……但别慌,我把Spring的学习路径给你理清楚,让你从入门到能独立写企业级项目。
一、Spring到底是什么?为什么它这么火?
在Spring出现之前,Java世界用的是EJB(企业JavaBean),那玩意儿复杂到让人怀疑人生。你要写一个小小的web应用,就得配置一堆XML、继承一堆父类、还得部署到应用服务器上。开发者们苦不堪言。
Rod Johnson在2002年写了《Expert One-on-One J2EE Design and Development》,里面吐槽了EJB的种种问题,然后提出了一个叫Spring的解决方案。这个方案的核心思想很简单:用简单的方式做复杂的事情。
Spring最核心的价值在于两个概念:IoC(控制反转)和AOP(面向切面编程)。
IoC说白了就是”不要自己管对象了,交给Spring来管”。比如你需要一个数据库连接,以前是自己new一个出来,现在是在Spring配置里声明一下,Spring会自动帮你创建并注入进来。这听起来好像没什么,但当你有几十个类互相依赖的时候,这个好处就太大了。
AOP则是解决那些”跟业务逻辑无关,但又不得不做”的事情,比如日志记录、事务管理、权限检查。你用AOP可以把这些横切关注点从业务代码里分离出来,让代码更干净。
二、Spring学习路径:先搞懂这些基础概念
1. IoC容器——Spring的基石
IoC容器是Spring的核心。它就是一个大工厂,负责创建和管理所有的Bean(也就是Java对象)。你只需要告诉Spring你要什么,它就把对象创建好给你。
有两种方式告诉Spring你要什么:XML配置和注解。现代Spring项目几乎都用注解了,XML配置基本绝迹。你只需要在类上加@Component,Spring就会自动扫描并把你的类注册成一个Bean。
比如你写了一个用户服务:
@Component
public class UserService {
public void createUser(String username) {
// 业务逻辑
}
}
Spring启动时会自动扫描到它,创建一个实例。如果你想用这个服务,只需要:
@Autowired
private UserService userService;
Spring会自动把UserService注入进来。这就是IoC——控制反转,你不需要自己new对象了,Spring帮你管。
2. Bean的作用域——你到底有几个对象?
这是初学者最容易搞混的地方。Spring里的Bean默认是单例的,也就是说整个应用只有一个实例。这对无状态的服务类很合适,比如用户服务、订单服务。
但有些场景你需要新的实例,比如每次请求创建一个视图对象。Spring提供了五种作用域:
- singleton:单例,默认值,整个应用一个实例
- prototype:每次请求都创建新实例
- request:每次HTTP请求创建一个实例(只在Web应用里有效)
- session:每次HTTP会话创建一个实例
- application:整个应用生命周期内一个实例
大部分情况下你用默认的singleton就够了。只有当你需要状态时,才用prototype。
3. 依赖注入——Spring的灵魂
依赖注入是IoC的具体实现方式。有四种注入方式:
构造器注入(推荐):
@Component
public class OrderService {
private final PaymentGateway paymentGateway;
@Autowired
public OrderService(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
}
setter注入:
@Component
public class OrderService {
private PaymentGateway paymentGateway;
@Autowired
public void setPaymentGateway(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
}
字段注入(最不推荐,但很多人用):
@Component
public class OrderService {
@Autowired
private PaymentGateway paymentGateway;
}
接口注入(Spring已经不推荐了)
我的建议是:优先用构造器注入。这样代码更健壮,更容易测试,而且能保证依赖不为null。
三、Spring MVC——构建Web应用的利器
Spring MVC是Spring框架的Web层实现。它基于Model-View-Controller模式,帮你快速构建RESTful API或者传统的MVC应用。
1. 请求处理流程
当一个HTTP请求到来时,Spring MVC的处理流程是:
- DispatcherServlet(前端控制器)接收请求
- HandlerMapping找到处理这个请求的Controller
- Controller执行业务逻辑,返回ModelAndView
- ViewResolver解析视图名称,找到实际的视图
- View渲染结果,返回给客户端
2. 最简单的Controller
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userService.create(user);
}
}
@RestController是@Controller和@ResponseBody的组合,表示这个类里的方法都直接返回数据(JSON),不渲染视图。
3. 参数绑定
Spring MVC很聪明,能自动把请求参数绑定到你的方法参数上:
@PathVariable:路径变量,比如/users/{id}里的id@RequestParam:查询参数,比如/users?page=1里的page@RequestBody:请求体,通常是JSON@RequestHeader:请求头@CookieValue:Cookie值
四、Spring Data JPA——持久层的神器
数据库操作是任何应用的核心。Spring Data JPA帮你简化了JPA(Java持久化API)的使用。
1. 定义Entity
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String email;
// getter和setter
}
2. 定义Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
List<User> findByEmailContaining(String keyword);
}
你只需要定义接口,Spring会自动实现。这个方法名遵循一定的命名规范:findBy + 属性名 + 条件。Spring会解析这个方法名,生成对应的SQL。
3. 使用Repository
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User findById(Long id) {
return userRepository.findById(id).orElseThrow(
() -> new RuntimeException("User not found")
);
}
public User findByUsername(String username) {
return userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("User not found"));
}
}
五、Spring Security——权限控制的标配
安全是任何应用都不能忽视的问题。Spring Security提供了完整的安全解决方案。
1. 基础配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll();
return http.build();
}
}
2. 用户DetailsService
你需要实现UserDetailsService接口,告诉Spring Security怎么加载用户信息:
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(
"User not found: " + username
));
return User.builder()
.username(user.getUsername())
.password(user.getPassword())
.roles("USER")
.build();
}
}
六、Spring Boot——让Spring更简单
Spring Boot是Spring的”快速开发”版本。它通过”约定优于配置”的理念,让你几乎不需要写任何配置文件就能启动一个Spring应用。
1. 自动配置
Spring Boot会扫描classpath里的依赖,自动配置Spring应用。比如你加了spring-boot-starter-data-jpa,Spring Boot会自动配置JPA的EntityManager、DataSource等。
2. 起步依赖
Spring Boot提供了很多起步依赖(starter),一键引入你需要的功能:
spring-boot-starter-web:构建Web应用spring-boot-starter-data-jpa:使用JPAspring-boot-starter-security:使用Spring Securityspring-boot-starter-test:测试支持
3. 第一个Spring Boot应用
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
就这么简单,一个完整的Spring应用就启动了。
七、实战项目:用户管理系统
学Spring最好的方式就是动手写项目。我们来做一个用户管理系统,涵盖Spring MVC、Spring Data JPA、Spring Security。
1. 项目结构
src/main/java/com/example/usermanagement/
├── UserManagementApplication.java
├── config/
│ └── SecurityConfig.java
├── controller/
│ └── UserController.java
├── service/
│ └── UserService.java
├── repository/
│ └── UserRepository.java
├── entity/
│ └── User.java
└── dto/
├── UserDTO.java
└── RegisterRequest.java
2. Entity定义
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false, length = 100)
private String email;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private boolean enabled = true;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
// getter和setter省略
}
3. Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
boolean existsByUsername(String username);
boolean existsByEmail(String email);
}
4. Service
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private RoleRepository roleRepository;
public User register(RegisterRequest request) {
if (userRepository.existsByUsername(request.getUsername())) {
throw new BusinessException("Username already exists");
}
if (userRepository.existsByEmail(request.getEmail())) {
throw new BusinessException("Email already exists");
}
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setRoles(Set.of(roleRepository.findByName("USER").orElseThrow()));
return userRepository.save(user);
}
public User findByUsername(String username) {
return userRepository.findByUsername(username)
.orElseThrow(() -> new BusinessException("User not found"));
}
}
5. Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody RegisterRequest request) {
try {
User user = userService.register(request);
return ResponseEntity.ok(new ApiResponse("Registration successful", user));
} catch (BusinessException e) {
return ResponseEntity.badRequest().body(new ApiResponse(e.getMessage()));
}
}
@GetMapping("/{username}")
public ResponseEntity<?> getUser(@PathVariable String username) {
try {
User user = userService.findByUsername(username);
return ResponseEntity.ok(new ApiResponse("Success", user));
} catch (BusinessException e) {
return ResponseEntity.notFound().build();
}
}
}
八、常见坑点和解决方案
1. Circular Dependency(循环依赖)
两个Bean互相依赖,Spring启动时会报错。解决方案:
- 用构造器注入代替字段注入
- 提取公共逻辑到第三个Bean
- 用
@Lazy注解延迟注入
2. Transaction不生效
@Transactional不加在public方法上,或者在同一个类里调用,事务都不生效。解决方案:
- 确保方法是public的
- 通过另一个Bean调用带事务的方法
- 用
@EnableTransactionManagement启用事务管理
3. Bean Not Found
Spring找不到Bean,通常是:
- 类没有加
@Component或@Service等注解 - 扫描路径不对,检查
@ComponentScan - Bean作用域问题
九、进阶:Spring Cloud微服务
当你的应用规模变大,单体架构就不够了。Spring Cloud提供了微服务架构的完整解决方案:
- Spring Cloud Netflix:Eureka(服务发现)、Ribbon(负载均衡)、Hystrix(熔断)
- Spring Cloud Config:集中化配置管理
- Spring Cloud Gateway:API网关
- Spring Cloud OpenFeign:声明式HTTP客户端
微服务是个大话题,建议先把Spring基础打扎实了再学。
十、学习资源推荐
官方文档
Spring的官方文档写得非常好,建议作为主要参考资料:https://spring.io/projects/spring-framework
书籍推荐
- 《Spring in Action》——最经典的Spring入门书
- 《深入理解Spring技术内幕》——适合想深入了解原理的人
在线课程
- Udemy的Spring Boot课程
- 极客时间的Spring源码解析
实战项目
- 在GitHub上找一些Spring Boot的开源项目,比如Spring PetClinic
- 自己做一个用户管理系统,覆盖CRUD、登录注册、权限控制
结语
Spring的学习曲线确实有点陡,但只要你按部就班,从IoC、Bean、依赖注入这些基础概念开始,然后逐步深入Spring MVC、Spring Data JPA、Spring Security,最后上手实战项目,你一定能掌握Spring。
记住:不要只看书或看视频,一定要动手写代码。Spring是一个实践性很强的框架,只有真正写过项目,你才能理解那些概念为什么这么设计。
祝你学习顺利!Spring的世界很精彩,等你探索。
