MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java Spring框架入门

2024-09-071.4k 阅读

一、Spring 框架概述

1.1 Spring 框架是什么

Spring 是一个开源的轻量级 Java 开发框架,它旨在简化企业级应用程序的开发。它提供了一个全面的编程和配置模型,用于现代 Java 企业级应用的开发,从简单的 Web 应用到复杂的分布式系统。Spring 框架核心的设计理念是依赖注入(Dependency Injection,简称 DI)和面向切面编程(Aspect - Oriented Programming,简称 AOP),这两个特性大大提高了代码的可测试性、可维护性和可扩展性。

1.2 Spring 框架的优势

  1. 轻量级:Spring 框架是非侵入式的,应用程序中的业务对象不需要依赖于特定的 Spring 类。这使得在现有项目中引入 Spring 变得相对容易,而且不会对原有的代码结构造成太大的破坏。

  2. 依赖注入:通过依赖注入,对象之间的依赖关系由容器来管理和注入,而不是对象自己去创建和管理依赖。这样可以降低对象之间的耦合度,使得代码更加易于测试和维护。例如,一个服务类可能依赖于一个数据访问对象(DAO),在传统的开发方式中,服务类需要自己实例化 DAO,而在 Spring 中,容器会将已经创建好的 DAO 注入到服务类中。

  3. 面向切面编程:AOP 允许将一些通用的功能,如日志记录、事务管理等,从业务逻辑中分离出来,以切面的形式进行统一管理。这样可以避免在业务代码中大量重复这些通用功能的代码,提高代码的复用性和可维护性。比如,在多个业务方法中都需要记录日志,使用 AOP 就可以将日志记录的逻辑提取到切面中,而业务方法只关注自身的核心逻辑。

  4. 容器管理:Spring 提供了一个容器,用于管理应用程序中的对象(Bean)。容器负责对象的创建、初始化、生命周期管理等。开发人员只需要关注业务逻辑的实现,而不需要关心对象的创建和管理细节。

  5. 丰富的功能模块:Spring 框架包含了众多功能模块,如 Spring Web、Spring Data、Spring Security 等,涵盖了 Web 开发、数据访问、安全等各个方面,能够满足企业级应用开发的各种需求。

二、Spring 框架的核心概念

2.1 Bean

  1. 定义:Bean 是 Spring 框架中由 Spring 容器管理的对象。这些对象是应用程序的组成部分,它们之间通过依赖关系相互协作。Spring 容器负责创建、配置和管理这些 Bean。
  2. Bean 的配置方式
    • XML 配置:在 Spring 早期版本中,XML 配置是主要的配置方式。例如,以下是一个简单的 XML 配置文件,用于定义一个名为 helloWorld 的 Bean:
<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 id="helloWorld" class="com.example.HelloWorld">
        <property name="message" value="Hello, Spring!"/>
    </bean>
</beans>

在上述配置中,id 属性是 Bean 的唯一标识符,class 属性指定了 Bean 的实现类,<property> 标签用于设置 Bean 的属性。

  • Java 配置:随着 Spring 的发展,Java 配置逐渐成为一种流行的配置方式。它使用 Java 代码来配置 Spring 容器。示例代码如下:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean
    public HelloWorld helloWorld() {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.setMessage("Hello, Spring!");
        return helloWorld;
    }
}

在这个 Java 配置类中,使用 @Configuration 注解标记该类为配置类,@Bean 注解标记的方法返回一个 Bean 实例。

2.2 依赖注入(DI)

  1. 构造函数注入:通过类的构造函数来注入依赖。例如,假设有一个 UserService 类依赖于 UserDao 类:
public class UserService {
    private final UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    // 业务方法
    public void saveUser() {
        userDao.save();
    }
}

在 Spring 配置中,可以这样配置 UserService 的 Bean:

<bean id="userDao" class="com.example.UserDao"/>
<bean id="userService" class="com.example.UserService">
    <constructor - arg ref="userDao"/>
</bean>

在 Java 配置中:

@Configuration
public class AppConfig {
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }

    @Bean
    public UserService userService(UserDao userDao) {
        return new UserService(userDao);
    }
}
  1. Setter 方法注入:通过类的 Setter 方法来注入依赖。修改 UserService 类如下:
public class UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    // 业务方法
    public void saveUser() {
        userDao.save();
    }
}

XML 配置:

<bean id="userDao" class="com.example.UserDao"/>
<bean id="userService" class="com.example.UserService">
    <property name="userDao" ref="userDao"/>
</bean>

Java 配置:

@Configuration
public class AppConfig {
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }

    @Bean
    public UserService userService() {
        UserService userService = new UserService();
        userService.setUserDao(userDao());
        return userService;
    }
}

2.3 面向切面编程(AOP)

  1. 切面(Aspect):切面是一个关注点的模块化,这个关注点可能会横切多个对象。例如,日志记录、事务管理等都可以定义为切面。
  2. 连接点(Joinpoint):连接点是程序执行过程中的一个点,比如方法调用、异常抛出等。在 Spring AOP 中,连接点总是方法调用。
  3. 切点(Pointcut):切点定义了一组连接点,这些连接点是切面要应用的地方。切点表达式用于指定哪些方法调用是切点。例如,使用 AspectJ 切点表达式:execution(* com.example.service.*.*(..)) 表示匹配 com.example.service 包下所有类的所有方法。
  4. 通知(Advice):通知是在切点所匹配的连接点处执行的操作,包括前置通知(Before Advice)、后置通知(After Advice)、环绕通知(Around Advice)、异常通知(After - throwing Advice)和最终通知(After - returning Advice)。

以下是一个简单的 AOP 示例,使用 Spring AOP 进行日志记录:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @Around("execution(* com.example.service.*.*(..))")
    public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("Starting method execution: {}", joinPoint.getSignature().getName());
        try {
            Object result = joinPoint.proceed();
            logger.info("Method execution completed successfully: {}", joinPoint.getSignature().getName());
            return result;
        } catch (Exception e) {
            logger.error("Method execution failed: {}", joinPoint.getSignature().getName(), e);
            throw e;
        }
    }
}

在上述代码中,@Aspect 注解标记该类为切面,@Around 注解定义了一个环绕通知,切点表达式 execution(* com.example.service.*.*(..)) 表示对 com.example.service 包下所有类的所有方法进行日志记录。

三、Spring 框架的常用模块

3.1 Spring Core

  1. 功能:Spring Core 是 Spring 框架的核心部分,它提供了 IoC(控制反转,即依赖注入的一种实现方式)容器,负责管理 Bean 的创建、配置和生命周期。它包含了 BeanFactory 和 ApplicationContext 等重要接口。
  2. BeanFactory:BeanFactory 是 Spring IoC 容器的基础接口,它提供了基本的 Bean 管理功能,如 Bean 的创建、获取和生命周期管理。它是一个延迟加载的容器,只有在请求获取 Bean 时才会创建 Bean。
  3. ApplicationContext:ApplicationContext 是 BeanFactory 的扩展接口,它提供了更多的企业级功能,如国际化支持、事件发布等。ApplicationContext 在启动时就会预先实例化所有的单例 Bean。常见的 ApplicationContext 实现类有 ClassPathXmlApplicationContextAnnotationConfigApplicationContext。例如,使用 ClassPathXmlApplicationContext 加载 XML 配置文件:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloWorld helloWorld = context.getBean("helloWorld", HelloWorld.class);
        System.out.println(helloWorld.getMessage());
    }
}

使用 AnnotationConfigApplicationContext 加载 Java 配置类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        HelloWorld helloWorld = context.getBean("helloWorld", HelloWorld.class);
        System.out.println(helloWorld.getMessage());
    }
}

3.2 Spring Web

  1. Spring Web MVC:Spring Web MVC 是 Spring 框架中用于构建 Web 应用的模块,基于 Model - View - Controller(MVC)设计模式。它提供了一个灵活的架构,用于处理 HTTP 请求、视图渲染等。
    • 控制器(Controller):控制器负责处理用户的请求,并调用业务逻辑,然后返回一个视图模型。例如:
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {
    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("message", "Hello, Spring Web MVC!");
        return "hello";
    }
}

在上述代码中,@Controller 注解标记该类为控制器,@GetMapping("/hello") 注解表示处理 /hello 的 GET 请求,方法返回视图名 hello,并向模型中添加了一个属性 message

  • 视图解析器(View Resolver):视图解析器负责将视图名解析为实际的视图对象,如 JSP、Thymeleaf 等。例如,配置 InternalResourceViewResolver 来解析 JSP 视图:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB - INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>
  1. Spring WebFlux:Spring WebFlux 是 Spring 5.0 引入的响应式 Web 框架,它基于 Reactor 库,支持非阻塞 I/O 和背压(Backpressure),适用于构建高性能、可伸缩的 Web 应用。
    • RouterFunction 和 HandlerFunction:在 Spring WebFlux 中,使用 RouterFunction 定义路由规则,HandlerFunction 处理请求。示例代码如下:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

@Configuration
public class WebFluxConfig {
    @Bean
    public RouterFunction<ServerResponse> route() {
        return RouterFunctions.route(RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
                request -> ServerResponse.ok().body(BodyInserters.fromValue("Hello, Spring WebFlux!")));
    }
}

在上述代码中,RouterFunctions.route 方法定义了一个路由规则,当接收到 /hello 的 GET 请求且请求头 Accepttext/plain 时,返回一个包含 Hello, Spring WebFlux! 的响应。

3.3 Spring Data

  1. 功能:Spring Data 提供了一种统一的方式来访问不同类型的数据库,包括关系型数据库(如 MySQL、Oracle 等)和非关系型数据库(如 MongoDB、Redis 等)。它减少了数据访问层的样板代码,提高了开发效率。
  2. Spring Data JPA:Spring Data JPA 是 Spring Data 针对 JPA(Java Persistence API)的实现。它允许开发人员通过定义简单的接口来实现数据访问操作,而无需编写大量的 SQL 代码。例如,定义一个 UserRepository 接口:
import com.example.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

在上述代码中,UserRepository 接口继承自 JpaRepository,Spring Data JPA 会自动为其实现基本的数据访问方法,如 findByIdsavedelete 等。 3. Spring Data MongoDB:用于访问 MongoDB 数据库。同样,通过定义接口来实现数据访问。示例如下:

import com.example.entity.User;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface UserMongoRepository extends MongoRepository<User, String> {
}

这里 UserMongoRepository 接口继承自 MongoRepository,可以方便地对 MongoDB 中的 User 文档进行操作。

四、Spring 框架的搭建与使用

4.1 使用 Maven 搭建 Spring 项目

  1. 创建 Maven 项目:可以使用 IDE(如 IntelliJ IDEA、Eclipse 等)创建一个 Maven 项目,或者使用命令行:
mvn archetype:generate -DgroupId=com.example -DartifactId=spring - demo -DarchetypeArtifactId=maven - archetype - quickstart -DinteractiveMode=false
  1. 添加 Spring 依赖:在 pom.xml 文件中添加 Spring 相关依赖。例如,添加 Spring Core 和 Spring Context 依赖:
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring - context</artifactId>
        <version>5.3.10</version>
    </dependency>
</dependencies>

如果要使用 Spring Web,还需要添加 Spring Web 依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring - webmvc</artifactId>
    <version>5.3.10</version>
</dependency>
  1. 配置 Spring:可以选择使用 XML 配置或 Java 配置。如果使用 XML 配置,在 src/main/resources 目录下创建 applicationContext.xml 文件,并进行相应的 Bean 配置。如果使用 Java 配置,创建一个配置类并添加 @Configuration 注解,然后在其中定义 Bean。

4.2 创建和使用 Bean

  1. 创建 Bean 类:例如,创建一个简单的 HelloWorld 类:
public class HelloWorld {
    private String message;

    public void setMessage(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}
  1. 配置 Bean
    • XML 配置:在 applicationContext.xml 中:
<bean id="helloWorld" class="com.example.HelloWorld">
    <property name="message" value="Hello, Spring!"/>
</bean>
  • Java 配置:在配置类中:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean
    public HelloWorld helloWorld() {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.setMessage("Hello, Spring!");
        return helloWorld;
    }
}
  1. 获取和使用 Bean
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloWorld helloWorld = context.getBean("helloWorld", HelloWorld.class);
        System.out.println(helloWorld.getMessage());
    }
}

如果使用 Java 配置:

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        HelloWorld helloWorld = context.getBean("helloWorld", HelloWorld.class);
        System.out.println(helloWorld.getMessage());
    }
}

4.3 使用 Spring 进行 Web 开发

  1. 配置 Spring Web MVC
    • 添加依赖:在 pom.xml 中添加 Spring Web MVC 依赖。
    • 配置 DispatcherServlet:在 web.xml 中配置 DispatcherServlet:
<servlet>
    <servlet - name>dispatcher</servlet - name>
    <servlet - class>org.springframework.web.servlet.DispatcherServlet</servlet - class>
    <init - param>
        <param - name>contextConfigLocation</param - name>
        <param - value>/WEB - INF/spring - servlet.xml</param - value>
    </init - param>
    <load - on - startup>1</load - on - startup>
</servlet>
<servlet - mapping>
    <servlet - name>dispatcher</servlet - name>
    <url - pattern>/</url - pattern>
</servlet - mapping>
  • 配置 Spring Web MVC 相关 Bean:在 spring - servlet.xml 中配置视图解析器、控制器等。例如:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB - INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>
<context:component - scan base - package="com.example.controller"/>
  1. 创建控制器:如前面的 HelloController 示例,处理用户请求并返回视图模型。
  2. 创建视图:在 WEB - INF/views 目录下创建 hello.jsp 文件:
<%@ page contentType="text/html;charset=UTF - 8" language="java" %>
<html>
<head>
    <title>Hello Spring Web MVC</title>
</head>
<body>
${message}
</body>
</html>

五、Spring 框架的最佳实践

5.1 分层架构

  1. 表现层(Presentation Layer):负责与用户交互,接收用户请求并返回响应。在 Spring 中,通常使用 Spring Web MVC 或 Spring WebFlux 来实现表现层。表现层应该只负责处理 HTTP 请求、验证输入数据、调用业务层方法并返回合适的视图或响应。
  2. 业务层(Business Layer):包含应用程序的核心业务逻辑。业务层对象(如服务类)应该专注于业务规则的实现,并且通过依赖注入获取所需的其他组件(如数据访问对象)。业务层应该对表现层和数据访问层保持低耦合。
  3. 数据访问层(Data Access Layer):负责与数据库或其他数据存储进行交互。在 Spring 中,可以使用 Spring Data 来简化数据访问操作。数据访问层应该提供统一的接口来执行数据的增删改查操作,并且对业务层隐藏具体的数据存储实现细节。

5.2 代码结构与命名规范

  1. 包结构:按照功能模块进行包的划分,例如,com.example.presentation 包用于表现层相关代码,com.example.business 包用于业务层代码,com.example.data 包用于数据访问层代码。
  2. 类命名:类名应该使用驼峰命名法,并且能够清晰地表达其功能。例如,UserService 表示用户服务类,UserRepository 表示用户数据访问接口。
  3. 方法命名:方法名也使用驼峰命名法,并且命名应该能够反映方法的功能。例如,saveUser 方法用于保存用户数据,getUserById 方法用于根据 ID 获取用户。

5.3 单元测试与集成测试

  1. 单元测试:使用测试框架(如 JUnit、Mockito 等)对单个组件(如服务类、数据访问对象等)进行测试。在测试时,应该模拟组件的依赖,以确保测试的独立性。例如,使用 Mockito 来模拟 UserDao,对 UserService 进行单元测试:
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.*;

public class UserServiceTest {
    @Test
    public void testSaveUser() {
        UserDao userDao = Mockito.mock(UserDao.class);
        UserService userService = new UserService(userDao);
        userService.saveUser();
        Mockito.verify(userDao, Mockito.times(1)).save();
    }
}
  1. 集成测试:集成测试用于测试多个组件之间的协作是否正常。在 Spring 项目中,可以使用 Spring Test 框架来进行集成测试。例如,测试 UserServiceUserRepository 的集成:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class UserServiceIntegrationTest {
    @Autowired
    private UserService userService;

    @Test
    public void testSaveUser() {
        userService.saveUser();
        // 可以添加更多的断言来验证业务逻辑的正确性
    }
}

通过遵循这些最佳实践,可以使 Spring 项目的代码更加清晰、可维护和可测试,提高项目的整体质量和开发效率。在实际开发中,还需要根据项目的具体需求和特点,灵活运用 Spring 框架的各种功能,以实现高效、稳定的企业级应用。同时,随着 Spring 框架的不断发展和更新,开发人员也需要持续学习和关注新的特性和最佳实践,以保持技术的先进性。