Skip to content

Spring篇

本章内容都由网上整合,以及个人思考,仅用于学习分享,如有侵权部分或者思考错误地方,欢迎大家及时提出,本人一定及时纠正。

Spring、SpringMVC、SpringBoot有什么区别?

  1. Spring

    Spring 是一个开源的 Java 应用程序框架,旨在简化企业级应用程序的开发。它的核心特性包括:

    • 控制反转(IoC):通过依赖注入(DI),Spring 管理对象的创建和生命周期,降低组件之间的耦合度。

    • 面向切面编程(AOP):允许将跨越多个模块的通用功能(如日志记录、事务管理)分离出来,提高代码的模块化和可维护性。

  2. SpringMVC

    SpringMVC 是 Spring 框架中的一个模块,专门用于构建基于模型-视图-控制器(MVC)模式的 Web 应用程序。它的主要功能包括:

    • 请求处理:将 HTTP 请求映射到相应的控制器方法。
    • 视图解析:将控制器返回的数据模型渲染为视图(如 JSP、Thymeleaf)。
    • 表单处理和验证:简化用户输入的处理和验证。
  3. Spring Boot

    Spring Boot 是基于 Spring 框架的一个子项目,旨在简化 Spring 应用程序的创建和部署。它的特点包括:

    • 自动配置:根据项目依赖和配置,自动设置合理的默认值,减少手动配置的工作量。
    • 内嵌服务器:内置如 Tomcat、Jetty 等服务器,使应用程序可以独立运行,无需外部容器。
    • 简化依赖管理:提供一系列开箱即用的启动器(Starter),方便引入常用功能模块。
  4. 总结

    • Spring:一个全面的企业级应用程序框架,提供 IoC、AOP 等核心功能。
    • SpringMVC:Spring 框架中的一个模块,专注于构建基于 MVC 模式的 Web 应用程序。
    • Spring Boot:基于 Spring 的子项目,提供自动配置和内嵌服务器,简化 Spring 应用程序的开发和部署。

Spring中有哪些常用注解

1. 组件扫描和自动装配相关注解:

  • @Component:用于将一个类标识为 Spring 管理的组件(Bean)。
  • @Service:用于标识服务层组件,实际上是 @Component 的一种特殊化形式,表示业务逻辑层的组件。
  • @Repository:用于标识持久层组件,同样是 @Component 的特殊化,表示数据访问层的组件,并支持持久层的异常转换。
  • @Controller:用于标识控制层组件,表示该类是一个控制器,用于处理用户请求。
  • @Autowired:用于自动装配 Bean,Spring 会自动满足被注解的字段、构造函数或方法的依赖。
  • @Qualifier:与 @Autowired 一起使用,用于在有多个同类型 Bean 时,指定注入的具体 Bean。
  • @Primary:当有多个同类型的 Bean 时,标识首选的 Bean。

2. 配置相关注解:

  • @Configuration:用于定义配置类,该类可以包含一个或多个 @Bean 方法,这些方法会被 Spring 容器处理以生成 Bean 定义和服务请求。
  • @Bean:用于告诉 Spring,一个方法将返回一个需要注册为 Bean 的对象。
  • @PropertySource:用于指定外部属性文件的位置,并将其加载到 Spring 的环境中。

3. AOP(面向切面编程)相关注解:

  • @Aspect:用于定义一个切面(Aspect),即关注于横切关注点的模块化。
  • @Before:在目标方法执行之前执行通知。
  • @After:在目标方法执行之后执行通知。
  • @AfterReturning:在目标方法成功执行之后执行通知。
  • @AfterThrowing:在目标方法抛出异常后执行通知。
  • @Around:环绕通知,可以在目标方法执行前后自定义行为。

4. 事务管理相关注解:

  • @Transactional:用于声明事务性,在方法或类上使用该注解,表示其中的操作需要在事务中执行。

5. Spring MVC 相关注解:

  • @RequestMapping:用于映射请求到处理方法或类上,指定 URL 路径和请求方法等。
  • @GetMapping@PostMapping@PutMapping@DeleteMapping:分别用于映射 GET、POST、PUT、DELETE 请求到处理方法上,是 @RequestMapping 的快捷方式。
  • @RequestParam:用于将请求参数绑定到方法的参数上。
  • @PathVariable:用于将 URL 路径中的变量绑定到方法的参数上。
  • @RequestBody:用于将请求体绑定到方法的参数上。
  • @ResponseBody:用于将方法的返回值作为响应体返回。
  • @RestController:组合注解,相当于 @Controller@ResponseBody 的结合,表示该类中的所有方法的返回值都直接作为响应体返回。

6. 条件装配相关注解:

  • @Conditional:根据特定条件,决定某个 Bean 是否被注册到 Spring 容器中。
  • @Profile:用于根据环境配置,选择性地加载某些 Bean 或配置类。

7. 异常处理相关注解:

  • @ExceptionHandler:用于定义全局或局部的异常处理方法。
  • @ControllerAdvice:用于定义全局的异常处理类,集中处理控制器层的异常。

SpringAOP的理解

Spring AOP(Aspect-Oriented Programming,面向切面编程)。旨在通过将横切关注点(cross-cutting concerns)从业务逻辑中分离出来,提高代码的模块化和可维护性。

  1. 核心概念
  • 切面(Aspect):封装横切逻辑的模块,包含通知和切入点定义。通过@Aspect注解标记。
  • 通知(Advice):定义切面在何时执行,包括:
    • 前置通知(@Before):方法执行前执行。
    • 后置通知(@After):方法执行后执行(无论成功或异常)。
    • 返回通知(@AfterReturning):方法成功返回后执行。
    • 异常通知(@AfterThrowing):方法抛出异常后执行。
    • 环绕通知(@Around):包裹目标方法,控制其执行。
  • 切入点(Pointcut):通过表达式(如execution(...))定义哪些方法需要被拦截。
  • 连接点(Join Point):程序执行中的可插入点(如方法调用),Spring AOP仅支持方法级别的连接点。
  • 织入(Weaving):将切面应用到目标对象的过程,Spring AOP通过动态代理在运行时实现。
  1. 实现机制
  • 动态代理
    • JDK动态代理:针对实现了接口的类。
    • CGLIB代理:针对无接口的类,生成子类(继承的方式)作为代理。
  • 代理对象:客户端调用的是代理对象,代理负责将方法调用委托给目标对象,并在适当时机执行通知逻辑。
  1. 局限性
  • 作用范围:仅拦截Spring容器管理的Bean。
  • 方法限制:默认仅支持public方法。
  • 自调用问题:同一类内部方法调用不会触发代理(需通过代理对象调用或使用AspectJ)。
  1. 应用场景
  • 日志记录:统一记录方法调用信息。
  • 事务管理:通过@Transactional实现声明式事务。
  • 安全控制:检查权限或角色。
  • 性能监控:统计方法执行时间。

对Spring中的IOC的理解?

Ioc——Inversion of Control,即控制反转,是一种设计思想。在Java开发中,IOC 意味着将你设计好的对象交给容器管理。下面举一些例子说明

1. 传统方式:手动管理对象依赖

开发者需要 自己创建对象手动注入依赖,代码耦合度高。

java
// 1. 定义UserRepository
public class UserRepository {
    public void saveUser(String username) {
        System.out.println("保存用户: " + username);
    }
}

// 2. 定义UserService,直接依赖UserRepository
public class UserService {
    private UserRepository userRepository;

    // 开发者需要手动创建依赖对象
    public UserService() {
        this.userRepository = new UserRepository(); // 直接实例化
    }

    public void createUser(String username) {
        userRepository.saveUser(username);
    }
}

// 3. 使用UserService
public class Main {
    public static void main(String[] args) {
        UserService userService = new UserService(); // 手动创建对象
        userService.createUser("张三");
    }
}

问题

  • UserService 直接依赖具体的 UserRepository紧耦合
  • 若想替换 UserRepository 的实现(比如换为 MockUserRepository),必须修改 UserService 的代码。

2. IOC方式:容器管理对象依赖

将对象的创建和依赖注入交给 IOC容器(如Spring),代码解耦。

步骤1:定义Bean

java
// 1. 定义UserRepository接口,面向接口编程
public interface UserRepository {
    void saveUser(String username);
}

// 2. 具体实现类(由容器管理)
@Component // 标记为Bean,由Spring容器管理
public class DatabaseUserRepository implements UserRepository {
    @Override
    public void saveUser(String username) {
        System.out.println("数据库保存用户: " + username);
    }
}

// 3. 定义UserService,依赖注入UserRepository
@Component // 标记为Bean
public class UserService {
    private final UserRepository userRepository;

    // 通过构造函数注入依赖(容器自动完成)
    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void createUser(String username) {
        userRepository.saveUser(username);
    }
}

步骤2:配置容器(使用Java Config)

java
@Configuration
@ComponentScan("com.example") // 扫描包下的@Component注解
public class AppConfig {
    // 容器会自动创建UserService和DatabaseUserRepository的Bean
}

步骤3:从容器获取对象

java
public class Main {
    public static void main(String[] args) {
        // 启动IOC容器
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 从容器获取UserService(容器已自动注入依赖)
        UserService userService = context.getBean(UserService.class);
        userService.createUser("张三");
    }
}

关键点

  • 控制反转:对象的创建和依赖注入由容器完成,而非开发者手动控制。
  • 解耦UserService 不再依赖具体的 DatabaseUserRepository,而是依赖接口。
  • 灵活性:若想替换实现(例如用 MockUserRepository),只需修改容器配置,无需改动 UserService

对比总结

传统方式IOC方式
手动创建对象 (new UserRepository())容器自动创建对象 (@Component)
类之间直接依赖具体实现依赖接口,容器注入具体实现 (@Autowired)
代码紧耦合,难以扩展代码解耦,易于替换实现

生动类比

想象你去餐馆点菜:

  • 传统方式:你不仅要下单,还要亲自去厨房做菜(自己管理对象)。
  • IOC方式:你只需下单(定义需求),厨师(容器)负责做菜并端给你(依赖注入)。

补充:注入方式有三种

  1. 构造函数注入

    java
    import org.springframework.stereotype.Component;
    import org.springframework.beans.factory.annotation.Autowired;
    
    @Component
    public class UserService {
        private final UserRepository userRepository;
    
        @Autowired
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    
        // 业务方法
    }
  2. Setter方法注入

    java
    import org.springframework.stereotype.Component;
    import org.springframework.beans.factory.annotation.Autowired;
    
    @Component
    public class UserService {
        private UserRepository userRepository;
    
        @Autowired
        public void setUserRepository(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    
        // 业务方法
    }
  3. 注解注入

    java
    import org.springframework.stereotype.Component;
    import org.springframework.beans.factory.annotation.Autowired;
    
    @Component
    public class UserService {
        @Autowired
        private UserRepository userRepository;
    
        // 业务方法
    }

Bean的作用域

在 Spring 框架中,Bean 的作用域(Scope) 决定了 Bean 实例的生命周期和可见范围。Spring 提供了多种作用域,开发者可以根据业务需求选择合适的范围来管理 Bean 的创建和销毁行为。


Spring 支持的 Bean 作用域

作用域描述适用场景
singleton默认作用域,容器中仅存在一个 Bean 实例,所有请求共享同一对象。无状态的 Bean(如 Service、DAO),线程安全且无需维护状态。
prototype每次请求 Bean 时,容器都会创建一个新实例。需要维护状态的 Bean(如用户会话),或有线程安全问题的对象。
request每个 HTTP 请求创建一个新实例(仅限 Web 应用)。存储与单个 HTTP 请求相关的数据(如表单提交)。
session每个用户会话(Session)创建一个新实例(仅限 Web 应用)。存储用户会话数据(如购物车、登录信息)。
global session全局会话作用域仅在基于 portlet 的 Web 应用中有效。在基于 Portlet 的应用程序中,多个 Portlet 可能需要访问共享的数据。使用全局会话作用域,可以确保在整个全局会话期间,所有 Portlet 都访问同一个 Bean 实例,从而共享数据。
application每个 ServletContext 生命周期内创建一个实例(类似单例,但属于 ServletContext 范围)。全局共享但需与 ServletContext 生命周期绑定的对象(如全局配置)。
websocket每个 WebSocket 会话创建一个实例(WebSocket 长连接)。实时通信场景(如聊天室),存储与单个 WebSocket 连接相关的状态。

代码示例

  1. 单例(Singleton)
java
@Component
@Scope("singleton") // 可省略(默认)
public class SingletonBean {
    // 无论多少次注入,都是同一个实例
}
  1. 原型(Prototype)
java
@Component
@Scope("prototype")
public class PrototypeBean {
    // 每次注入或 getBean() 时生成新实例
}
  1. 请求(Request)和会话(Session)
java
// Web 应用中需添加依赖(如 spring-web)
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {
    // 每个 HTTP 请求生成一个新实例
}

@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionScopedBean {
    // 每个用户会话生成一个新实例
}

Bean的生命周期

流程图解

mermaid
graph TD
    A[容器启动] --> B[加载Bean定义]
    B --> C[实例化Bean: 构造函数]
    C --> D[属性注入: 依赖赋值]
    D --> E[Aware接口回调]
    E --> F1[BeanPostProcessor.postProcessBeforeInitialization]
    F1 --> G1[初始化方法1: @PostConstruct]
    G1 --> G2[初始化方法2: InitializingBean.afterPropertiesSet]
    G2 --> G3[初始化方法3: 自定义init-method]
    G3 --> F2[BeanPostProcessor.postProcessAfterInitialization]
    F2 --> H[Bean就绪]
    H --> I[容器关闭]
    I --> J1[销毁方法1: @PreDestroy]
    J1 --> J2[销毁方法2: DisposableBean.destroy]
    J2 --> J3[销毁方法3: 自定义destroy-method]

分步详解与代码验证

1. 加载 Bean 定义

  • 触发时机:容器启动时解析配置(XML/注解/JavaConfig)。

  • 动作:读取 Bean 的元数据(类名、作用域、初始化方法等)。

  • 扩展点BeanFactoryPostProcessor(可修改 Bean 定义)。

    java
    @Component
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            System.out.println("BeanFactoryPostProcessor 修改Bean定义...");
        }
    }

2. 实例化 Bean

  • 动作:调用 构造函数 创建对象。

  • 注意:若使用构造器注入,依赖会在此阶段通过构造函数参数注入。

    java
    @Component
    public class UserService {
        // 构造函数注入示例
        private final UserRepository userRepository;
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
            System.out.println("UserService 实例化");
        }
    }

3. 属性注入

  • 动作:通过反射为字段或 Setter 方法注入依赖。

  • 示例

    java
    @Component
    public class OrderService {
        @Autowired  // 字段注入
        private UserService userService;
        
        @Autowired  // Setter注入
        public void setPaymentService(PaymentService paymentService) {
            System.out.println("OrderService 依赖注入完成");
        }
    }

4. Aware 接口回调

  • 作用:让 Bean 感知容器信息。

  • 常见接口

    接口回调方法获取的信息
    BeanNameAwaresetBeanName(String)Bean 的 ID
    BeanFactoryAwaresetBeanFactory(BeanFactory)BeanFactory 实例
    ApplicationContextAwaresetApplicationContext(ApplicationContext)Spring 上下文
    java
    @Component
    public class AwareDemo implements BeanNameAware, ApplicationContextAware {
        private String beanName;
        private ApplicationContext context;
    
        @Override
        public void setBeanName(String name) {
            this.beanName = name;
            System.out.println("BeanNameAware: " + beanName);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext context) {
            this.context = context;
            System.out.println("ApplicationContextAware 完成");
        }
    }

5. BeanPostProcessor 前置处理

  • 接口BeanPostProcessor

  • 方法postProcessBeforeInitialization()

  • 用途:在初始化前修改 Bean(如生成代理)。

    java
    @Component
    public class MyBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) {
            System.out.println("Before初始化: " + beanName);
            return bean; // 可返回代理对象
        }
    }

6. 初始化方法

  • 执行顺序

    1. @PostConstruct 注解方法(JSR-250 标准)
    2. InitializingBean.afterPropertiesSet()(Spring 接口)
    3. 自定义初始化方法(XML 中 init-method@Bean(initMethod="...")
    java
    @Component
    public class InitDemo implements InitializingBean {
        @PostConstruct
        public void postConstruct() {
            System.out.println("@PostConstruct 调用");
        }
    
        @Override
        public void afterPropertiesSet() {
            System.out.println("InitializingBean.afterPropertiesSet 调用");
        }
    
        public void customInit() {
            System.out.println("自定义初始化方法");
        }
    }

7. BeanPostProcessor 后置处理

  • 方法postProcessAfterInitialization()

  • 示例

    java
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("After初始化: " + beanName);
        return bean;
    }

8. Bean 就绪

  • 状态:Bean 已完全初始化,可被其他对象使用。

  • 验证

    java
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        userService.createUser("张三"); // Bean正常使用
    }

9. 销毁阶段

  • 触发条件:容器关闭(仅 Singleton Bean)。

  • 执行顺序

    1. @PreDestroy 注解方法
    2. DisposableBean.destroy()
    3. 自定义销毁方法(XML 中 destroy-method@Bean(destroyMethod="...")
    java
    @Component
    public class DestroyDemo implements DisposableBean {
        @PreDestroy
        public void preDestroy() {
            System.out.println("@PreDestroy 调用");
        }
    
        @Override
        public void destroy() {
            System.out.println("DisposableBean.destroy 调用");
        }
    
        public void customDestroy() {
            System.out.println("自定义销毁方法");
        }
    }

Spring循环依赖是怎么解决的

Spring 通过 三级缓存机制提前暴露半成品 Bean 来解决单例作用域下的循环依赖问题。以下是详细的分步说明:

循环依赖场景

假设存在两个 Bean:

java
@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

A 依赖 B,B 也依赖 A,形成循环依赖。

Spring 解决循环依赖的流程

1. 三级缓存的作用

Spring 通过三级缓存管理 Bean 的创建过程:

  • 一级缓存(singletonObjects):存储完全初始化好的 Bean。
  • 二级缓存(earlySingletonObjects):存储提前暴露的 Bean(已实例化但未完成初始化的半成品)。
  • 三级缓存(singletonFactories):存储 Bean 的工厂对象,用于生成半成品 Bean。

2. 解决流程详解

mermaid
sequenceDiagram
    participant Spring容器
    participant A的创建流程
    participant B的创建流程

    Spring容器->>A的创建流程: 1. 开始创建Bean A
    A的创建流程->>A的创建流程: 2. 实例化A(调用构造函数)
    A的创建流程->>Spring容器: 3. 将A的半成品对象工厂放入三级缓存
    A的创建流程->>A的创建流程: 4. 属性注入:发现依赖B
    A的创建流程->>Spring容器: 5. 请求获取Bean B
    Spring容器->>B的创建流程: 6. 开始创建Bean B
    B的创建流程->>B的创建流程: 7. 实例化B(调用构造函数)
    B的创建流程->>Spring容器: 8. 将B的半成品对象工厂放入三级缓存
    B的创建流程->>B的创建流程: 9. 属性注入:发现依赖A
    B的创建流程->>Spring容器: 10. 请求获取Bean A
    Spring容器->>A的创建流程: 11. 从三级缓存获取A的半成品对象
    Spring容器-->>B的创建流程: 12. 返回A的半成品对象
    B的创建流程->>B的创建流程: 13. 完成B的属性注入和初始化
    B的创建流程->>Spring容器: 14. 将B放入一级缓存(完成态)
    Spring容器-->>A的创建流程: 15. 返回B的完成态对象
    A的创建流程->>A的创建流程: 16. 完成A的属性注入和初始化
    A的创建流程->>Spring容器: 17. 将A放入一级缓存(完成态)

3. 关键步骤解析

  1. 创建 Bean A
    • 实例化 A(调用构造函数),此时 A 是一个半成品(未注入依赖)。
    • 将 A 的工厂对象(ObjectFactory)存入 三级缓存singletonFactories)。
  2. 注入 A 的依赖 B
    • Spring 发现 A 需要注入 B,于是尝试从容器中获取 B。
    • 由于 B 尚未创建,触发 B 的创建流程。
  3. 创建 Bean B
    • 实例化 B(调用构造函数),此时 B 是一个半成品。
    • 将 B 的工厂对象存入三级缓存。
  4. 注入 B 的依赖 A
    • Spring 发现 B 需要注入 A,尝试从容器中获取 A。
    • 关键点:此时 A 的半成品工厂对象仍在三级缓存中:
      • Spring 从三级缓存中取出 A 的工厂对象,生成 A 的早期引用(半成品)。
      • 将 A 的半成品对象存入 二级缓存earlySingletonObjects),并从三级缓存中删除 A 的工厂对象。
  5. 完成 B 的初始化
    • 将 A 的半成品注入到 B 中。
    • 继续完成 B 的属性注入和初始化(如 @PostConstruct)。
    • 将 B 的完成态对象存入 一级缓存singletonObjects)。
  6. 完成 A 的初始化
    • 从一级缓存中获取 B 的完成态对象,注入到 A 中。
    • 继续完成 A 的属性注入和初始化。
    • 将 A 的完成态对象存入一级缓存,并从二级缓存中删除 A 的半成品。

为何需要三级缓存?

  • 三级缓存(singletonFactories):存储 Bean 的工厂对象,用于处理存在 AOP 代理的情况。 当 Bean 需要被代理时,工厂对象可以确保返回的是代理后的对象(而不是原始对象)。 若直接缓存半成品对象,则无法处理代理对象的生成。
  • 二级缓存(earlySingletonObjects): 避免重复执行工厂逻辑。一旦通过三级缓存生成半成品对象,后续直接从二级缓存获取。

无法解决的循环依赖场景

  1. 构造函数注入的循环依赖

    java
    @Component
    public class A {
        private B b;
        @Autowired
        public A(B b) {  // 构造函数注入
            this.b = b;
        }
    }
    
    @Component
    public class B {
        private A a;
        @Autowired
        public B(A a) {  // 构造函数注入
            this.a = a;
        }
    }
    • 原因:实例化 A 时需要先实例化 B,而实例化 B 又需要实例化 A,导致死锁。Spring 会抛出 BeanCurrentlyInCreationException
  2. 原型(Prototype)作用域的循环依赖

    • 原型 Bean 每次请求都生成新实例,无法通过缓存解决循环依赖。Spring 直接抛出异常。

总结

  • 解决范围:单例 Bean + Setter/字段注入。
  • 核心机制:三级缓存 + 提前暴露半成品 Bean。
  • 设计意义: 通过空间换时间,允许在 Bean 未完全初始化时提前暴露引用,打破循环依赖的死锁。 三级缓存确保代理对象的一致性,避免重复创建。

Spring中用到了哪些设计模式

1. 工厂模式(Factory Pattern)

作用:将对象的创建与使用分离,通过工厂统一管理对象的生命周期。 Spring 应用

  • BeanFactory:Spring 容器的核心接口,负责创建和管理 Bean。
  • ApplicationContextBeanFactory 的扩展,提供更高级的工厂功能(如事件发布、资源加载)。
java
// 通过工厂获取 Bean
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean(UserService.class);

2. 单例模式(Singleton Pattern)

作用:确保一个类只有一个实例,并提供全局访问点。 Spring 应用

  • 默认 Bean 作用域:Spring 管理的 Bean 默认是单例的,容器内共享同一实例。
java
@Component
public class SingletonBean {
    // 容器中仅存在一个实例
}

3. 代理模式(Proxy Pattern)

作用:通过代理对象控制对目标对象的访问,用于实现 AOP 等功能。 Spring 应用

  • JDK 动态代理:基于接口的代理(实现相同接口的类)。
  • CGLIB 代理:生成目标类的子类作为代理(用于无接口的类)。
java
// Spring AOP 通过代理增强目标方法
@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("方法执行前日志");
    }
}

4. 模板方法模式(Template Method Pattern)

作用:定义算法的骨架,将某些步骤延迟到子类实现。 Spring 应用

  • JdbcTemplate:封装 JDBC 操作的通用流程(如连接管理、异常处理),用户只需实现 SQL 逻辑。
  • RestTemplate:HTTP 请求的模板化处理。
java
jdbcTemplate.execute("INSERT INTO users(name) VALUES ('张三')");

5. 观察者模式(Observer Pattern)

作用:定义对象间的一对多依赖关系,当一个对象状态改变时,自动通知依赖它的对象。 Spring 应用

  • 事件驱动模型:通过 ApplicationEventApplicationListener 实现。
java
// 自定义事件
public class OrderEvent extends ApplicationEvent {
    public OrderEvent(Object source) {
        super(source);
    }
}

// 监听事件
@Component
public class OrderListener implements ApplicationListener<OrderEvent> {
    @Override
    public void onApplicationEvent(OrderEvent event) {
        System.out.println("收到订单事件: " + event.getSource());
    }
}

// 发布事件
applicationContext.publishEvent(new OrderEvent("订单创建"));

6. 适配器模式(Adapter Pattern)

作用:将一个接口转换成客户端期望的另一个接口。 Spring 应用

  • HandlerAdapter:在 Spring MVC 中适配不同类型的处理器(如 @Controller、Servlet)。
  • AOP 中的 AdvisorAdapter:将不同类型的通知(Advice)适配到 AOP 框架。
java
// Spring MVC 中通过 HandlerAdapter 处理不同 Controller
public class RequestMappingHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return handler instanceof HandlerMethod;
    }
}

7. 装饰者模式(Decorator Pattern)

作用:动态地为对象添加额外职责,比继承更灵活。 Spring 应用

  • BeanDefinitionDecorator:装饰 Bean 的定义(如 XML 配置中的属性增强)。
  • HttpServletRequestWrapper:对请求对象进行包装(如 Spring Security 中的请求加密处理)。
java
// 自定义装饰器示例
public class CachingDataSourceDecorator implements DataSource {
    private final DataSource targetDataSource;
    
    public CachingDataSourceDecorator(DataSource dataSource) {
        this.targetDataSource = dataSource;
    }
    
    @Override
    public Connection getConnection() throws SQLException {
        // 添加缓存逻辑
        return targetDataSource.getConnection();
    }
}

8. 策略模式(Strategy Pattern)

作用:定义一系列算法,将每个算法封装起来,并使它们可以互换。 Spring 应用

  • 资源加载策略ResourceLoader 根据前缀(如 classpath:file:)选择不同的资源加载策略。
  • 事务管理策略PlatformTransactionManager 的不同实现(如 JDBC、JTA)。
java
// 根据环境选择事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource); // JDBC 事务策略
}

9. 责任链模式(Chain of Responsibility Pattern)

作用:将请求的发送者和接收者解耦,使多个对象都有机会处理请求。 Spring 应用

  • Spring Security 过滤器链:多个 Filter 按顺序处理 HTTP 请求。
  • AOP 的拦截器链:多个通知(Advice)按顺序织入目标方法。
java
// Spring Security 过滤器链配置
http
    .addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
    .addFilterAfter(new LoggingFilter(), SecurityContextPersistenceFilter.class);

10. 建造者模式(Builder Pattern)

作用:分步骤构建复杂对象,分离对象的构造与表示。 Spring 应用

  • BeanDefinitionBuilder:构建 Bean 的定义元数据。
  • UriComponentsBuilder:构建 URI 路径。
java
UriComponents uri = UriComponentsBuilder
    .fromUriString("https://example.com/{path}")
    .queryParam("name", "张三")
    .buildAndExpand("user");

11. 原型模式(Prototype Pattern)

作用:通过复制现有对象来创建新对象,避免重复初始化开销。 Spring 应用

  • Bean 的 prototype 作用域:每次请求时生成新实例。
java
@Scope("prototype")
@Component
public class PrototypeBean {
    // 每次注入时生成新实例
}

SpringMVC的执行流程是

mermaid
sequenceDiagram
    participant 用户
    participant DS as DispatcherServlet
    participant HM as HandlerMapping
    participant HA as HandlerAdapter
    participant H as Controller
    participant VR as ViewResolver
    participant V as View

    用户 ->> DS: 发起请求 (/user/list)
    DS ->> HM: 查找 Handler
    HM -->> DS: 返回 Handler
    DS ->> HA: 选择 HandlerAdapter
    HA ->> H: 执行 Controller 方法
    H -->> HA: 返回 ModelAndView
    HA -->> DS: 返回 ModelAndView
    DS ->> VR: 解析视图名称
    VR -->> DS: 返回 View 对象
    DS ->> V: 渲染视图
    V -->> DS: 返回响应内容
    DS ->> 用户: 返回响应结果
  1. 客户端发送请求
  • 描述: 用户在浏览器中输入 URL 或点击链接,浏览器生成一个 HTTP 请求,包含请求头、参数等信息。
  • 关键点: 请求发送到部署了 SpringMVC 应用的服务器。

  1. 前端控制器(DispatcherServlet)接收请求
  • 描述: 所有请求首先由配置在 web.xml(或通过 Spring Boot 自动配置)中的 DispatcherServlet 捕获。 它充当整个请求处理的入口和中央调度者。
  • 关键点: DispatcherServlet 被设计为 Front Controller,统一管理请求分发和响应处理。

  1. 查找 Handler(处理器)
  • 描述: DispatcherServlet 根据请求 URL 调用 HandlerMapping。 HandlerMapping 负责扫描已注册的 Controller 以及其映射(如 @RequestMapping 注解)来确定哪个 Handler(通常是 Controller 的方法)能够处理当前请求。
  • 关键点
    • 可以使用多种 HandlerMapping 实现,如 RequestMappingHandlerMapping(基于注解)或传统的 XML 配置。
    • 如果多个映射匹配,Spring 会根据优先级选择最合适的 Handler。

  1. 选择 HandlerAdapter
  • 描述: DispatcherServlet 不直接调用 Handler,而是委托给一个合适的 HandlerAdapter。 HandlerAdapter 的作用是封装对 Handler 调用的细节,使得 DispatcherServlet 与具体的 Handler 类型(例如基于注解的 Controller 或实现了特定接口的 Controller)解耦。
  • 关键点
    • 常用的 HandlerAdapter 有 RequestMappingHandlerAdapter 等。
    • HandlerAdapter 会负责参数解析、类型转换、数据绑定等工作。

  1. 执行 Handler(Controller)方法
  • 描述: 在 HandlerAdapter 的帮助下,调用具体的 Controller 方法。 在此过程中,SpringMVC 使用 HandlerMethodArgumentResolver 来解析方法参数(如请求参数、路径变量、请求体等),并将它们传入 Controller 方法。
  • 关键点
    • 请求参数经过数据绑定和类型转换后注入到方法参数中。
    • 如果配置了拦截器(Interceptor),它们的 preHandle 方法会在此之前被调用。

  1. 返回 ModelAndView
  • 描述: Controller 方法执行完毕后,返回一个 ModelAndView 对象。 该对象封装了:
    • Model:业务数据和视图渲染所需的属性集合。
    • View:视图名称(例如 JSP、Thymeleaf 模板)或视图对象。
  • 关键点
    • ModelAndView 可以为空,此时表示 Controller 已直接写入响应(例如返回 JSON 数据)。

  1. 选择视图解析器(ViewResolver)
  • 描述: DispatcherServlet 将 ModelAndView 中的视图名称交给 ViewResolver 进行解析,找到具体的视图模板。 ViewResolver 根据配置(如前缀、后缀、模板引擎等)将逻辑视图名称解析为实际视图资源。
  • 关键点
    • 常用的视图解析器包括 InternalResourceViewResolverThymeleafViewResolver 等。

  1. 渲染视图
  • 描述: 得到视图对象后,SpringMVC 将 Model 数据传递给视图进行渲染。 渲染过程通常包括将数据填充到模板中,生成最终的 HTML 页面。
  • 关键点
    • 在视图渲染过程中,还可以调用一些拦截器的 postHandle 方法处理后置逻辑。
    • 如果发生异常,可能会交由异常处理机制(如 @ControllerAdvice)处理。

  1. 返回响应给客户端
  • 描述: 渲染完成的 HTML 响应由 DispatcherServlet 写入到 HTTP 响应中,发送给客户端浏览器。 浏览器接收到响应后展示最终页面。
  • 关键点
    • 请求处理结束后,拦截器的 afterCompletion 方法会被调用,用于执行资源清理等后续工作。

补充说明

  • 拦截器(Interceptors): 在整个流程中,如果配置了拦截器,SpringMVC 会在调用 Handler 前后分别执行 preHandlepostHandleafterCompletion 方法,实现日志记录、安全校验、请求统计等功能。
  • 异常处理: 如果某个环节出现异常,DispatcherServlet 会调用异常解析器(HandlerExceptionResolver)进行处理,返回相应的错误视图或 JSON 数据。
  • 异步请求处理: SpringMVC 还支持异步请求(通过 CallableDeferredResult),这部分流程会稍有不同,但总体思路与同步处理类似,只是在执行 Controller 方法后将结果异步返回。

对SpringBoot的理解是

  • Spring Boot 是基于 Spring 框架的 快速开发脚手架,旨在简化 Spring 应用的初始搭建、配置和开发流程。其核心理念是 约定优于配置,通过自动化机制减少开发者的手动配置工作,使开发者能专注于业务逻辑。以下是 Spring Boot 的核心理解和关键特性:


    1. 核心目标

    • 简化配置:消除传统 Spring 应用中繁琐的 XML 配置和样板代码。
    • 快速启动:通过内嵌服务器(Tomcat/Jetty)和自动化依赖管理,实现“一键启动”应用。
    • 生产就绪:集成健康检查、指标监控(Actuator)、外部化配置等企业级功能。

    2. 核心特性

    (1) 自动配置(Auto-Configuration)

    • 原理:根据类路径中的依赖(如 spring-boot-starter-data-jpa)和现有 Bean,自动配置 Spring 组件。
    • 示例
      • 若引入 spring-boot-starter-data-jpa,Spring Boot 会自动配置数据源、JPA 实体管理器等。
      • 若未配置数据源 URL,默认使用内存数据库(如 H2)。

    (2) 起步依赖(Starters)

    • 作用:预定义一组依赖集合,简化 Maven/Gradle 配置。

    • 示例

      xml
      <!-- 引入Web开发所需的所有依赖(Tomcat、Spring MVC、JSON支持等) -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

    (3) 内嵌服务器(Embedded Server)

    • 实现:将 Tomcat、Jetty 或 Undertow 服务器嵌入应用,无需外部部署。

    • 代码验证

      java
      @SpringBootApplication
      public class MyApp {
          public static void main(String[] args) {
              SpringApplication.run(MyApp.class, args); // 启动内嵌服务器
          }
      }

    (4) Actuator 监控

    • 功能:提供 HTTP 端点监控应用状态(健康检查、指标、环境变量等)。

    • 配置

      yaml
      management:
        endpoints:
          web:
            exposure:
              include: "*"  # 暴露所有监控端点

    (5) 外部化配置

    • 支持格式:YAML、Properties 文件、环境变量、命令行参数等。

    • 优先级:命令行参数 > 外部配置文件 > 默认配置。

    • 示例

      yaml
      # application.yml
      server:
        port: 8081
      spring:
        datasource:
          url: jdbc:mysql://localhost:3306/mydb

    3. 与传统 Spring 应用的区别

    对比项SpringSpring Boot
    配置方式需手动配置 XML 或 JavaConfig自动配置,按需覆盖
    依赖管理手动管理依赖版本通过 Starters 统一管理版本
    内嵌服务器需外部部署(如 WAR 到 Tomcat)内置服务器,JAR 直接运行
    开发效率适合高度定制化场景适合快速开发、微服务和原型验证
    监控支持需集成第三方工具(如 Prometheus)内置 Actuator 提供开箱即用的监控功能

    4. Spring Boot 核心组件

    组件作用
    @SpringBootApplication组合注解(@Configuration + @ComponentScan + @EnableAutoConfiguration
    SpringApplication启动类入口,负责加载配置、启动内嵌服务器和应用上下文。
    Starters预定义依赖包(如 spring-boot-starter-web 包含 Web 开发所需依赖)。
    Actuator提供生产级监控端点(如 /health/metrics)。
    Spring Boot CLI命令行工具,支持 Groovy 脚本快速开发。

    5. 适用场景

    • 微服务架构:快速构建独立部署的小型服务。
    • RESTful API 开发:结合 spring-boot-starter-web 快速创建 API。
    • 原型验证:通过内嵌数据库和自动化配置快速验证想法。
    • 企业级应用:集成安全(Spring Security)、事务管理、批处理(Spring Batch)等模块。

SpringBoot Starter有什么用

  1. 简化依赖管理

    Starter 是一组预定义的依赖集合,它将构建某类功能所需的所有依赖打包在一起。例如,spring-boot-starter-web 包含了 Spring MVC、Jackson(用于 JSON 解析)、内嵌 Tomcat 等组件。这样,开发者只需在项目中添加一个 Starter 依赖,就能获得一整套相关功能,而不用单独配置每个库的版本。

  2. 自动配置支持

    Starter 与 Spring Boot 的自动配置机制紧密结合。通过引入 Starter,Spring Boot 能够根据项目的依赖自动推断出所需的默认配置,并为这些组件进行自动化配置。例如,当添加了 spring-boot-starter-data-jpa 后,Spring Boot 会自动配置数据源、EntityManagerFactory 等 JPA 相关组件。

  3. 降低开发复杂性

    Starter 使项目配置更加简单、直观。开发者可以专注于业务逻辑,而不用关心底层框架和依赖的细节,进一步提高开发效率。

  4. 模块化和可维护性

    不同功能通过不同的 Starter 模块进行划分,项目可以根据需要灵活引入或者剔除某个功能模块,避免不必要的依赖冗余,提高应用的可维护性。

示例:

假设你正在开发一个 Web 应用,只需要在 pom.xml 中添加以下依赖即可获得 Web 开发所需的全部功能:

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

通过这个 Starter,Spring Boot 会自动配置:

  • Spring MVC 用于处理 HTTP 请求
  • 内嵌的 Tomcat 服务器,使应用可以独立运行
  • JSON 序列化/反序列化相关的库(例如 Jackson)

SpringBoot常用注解有哪些

核心启动与配置相关

  • @SpringBootApplication 组合了 @Configuration@EnableAutoConfiguration@ComponentScan。用于标识主启动类,让 Spring Boot 自动扫描和配置应用。
  • @Configuration 表示该类可以使用 Spring IoC 容器作为配置类,替代传统 XML 配置文件。
  • @EnableAutoConfiguration 让 Spring Boot 根据项目依赖自动配置 Spring 应用,通常由 @SpringBootApplication 包含。
  • @ComponentScan 指定 Spring 应扫描的包路径,自动注册 Bean。也通常由 @SpringBootApplication 包含。

控制层相关

  • @RestController 组合了 @Controller@ResponseBody,用于创建 RESTful 控制器,方法返回值会自动转换为 JSON 或其他格式直接响应给客户端。
  • @Controller 用于标识一个控制器组件,处理 Web 请求(通常结合视图解析)。
  • @RequestMapping@GetMapping@PostMapping@PutMapping@DeleteMapping 用于映射 HTTP 请求到控制器方法。@GetMapping@PostMapping 等是 @RequestMapping 的快捷方式。
  • @RequestParam@PathVariable@RequestBody@ResponseBody 用于处理请求参数、路径变量、请求体与响应体的数据绑定和转换。

依赖注入相关

  • @Autowired 让 Spring 自动装配 Bean,可用于构造器、Setter 方法或字段上。
  • @Qualifier 在多个同类型 Bean 存在时,用来指定注入具体的 Bean。
  • @Value 用于将配置文件中的值(如 application.properties 或 application.yml)注入到字段或方法参数中。

Bean 定义与作用域

  • @Bean 标识一个方法创建一个 Bean,该 Bean 将由 Spring 容器管理。
  • @Scope 指定 Bean 的作用域,如单例(默认)、原型、请求、会话等。
  • @Profile 根据不同的环境(开发、测试、生产)加载不同的配置或 Bean。

异步、定时与其他

  • @EnableScheduling 启用定时任务支持,配合 @Scheduled 使用。
  • @EnableAsync 启用异步方法执行,配合 @Async 注解使用。

测试相关

  • @SpringBootTest 用于测试时启动完整的 Spring Boot 应用上下文,适合集成测试。
  • @WebMvcTest 专注于 Spring MVC 控制器层的测试,不加载全部应用上下文。
  • @DataJpaTest 用于测试 JPA 组件,配置嵌入式数据库并只加载与数据访问相关的 Bean。

**问题13:**SpringBoot的启动流程(后续还需修改,本人目前也是一知半解)

Spring Boot的启动流程可以分为以下几个关键步骤,每个步骤通过Spring Boot的自动化配置和扩展机制简化了传统Spring应用的初始化过程:


1. 初始化SpringApplication实例

  • 触发入口:调用SpringApplication.run(主类.class, args)
  • 操作
    • 推断应用类型(Web或非Web),决定创建哪种应用上下文(如AnnotationConfigServletWebServerApplicationContext)。
    • 加载META-INF/spring.factories中的ApplicationContextInitializerApplicationListener
    • 设置主配置类(即标注@SpringBootApplication的类)。

2. 准备运行环境(Environment)

  • 操作
    • 加载配置文件(application.propertiesapplication.yml)。
    • 处理命令行参数(--server.port=8081)。
    • 设置激活的Profiles(如--spring.profiles.active=dev)。
  • 触发事件ApplicationEnvironmentPreparedEvent,允许监听器修改环境配置。

3. 创建应用上下文(ApplicationContext)

  • 根据应用类型创建上下文
    • Servlet Web应用AnnotationConfigServletWebServerApplicationContext
    • Reactive Web应用AnnotationConfigReactiveWebServerApplicationContext
    • 非Web应用AnnotationConfigApplicationContext
  • 初始化上下文
    • 注册主配置类(通过@SpringBootApplication引入的配置)。
    • 加载条件化配置(如@ConditionalOnClass)。

4. 刷新应用上下文(Refresh Context)

  • 核心操作:调用AbstractApplicationContext.refresh()方法,执行以下子步骤:
    1. 准备BeanFactory:配置类解析器、属性编辑器等。
    2. 调用BeanFactoryPostProcessor
      • 关键点ConfigurationClassPostProcessor处理@Configuration类,触发自动配置(@EnableAutoConfiguration)。
      • 自动配置原理:加载META-INF/spring.factories中的AutoConfiguration类,按条件筛选有效配置。
    3. 注册BeanPostProcessor:如处理@Autowired@PostConstruct的处理器。
    4. 初始化消息源、事件广播器
    5. 初始化单例Bean:包括内嵌Web服务器(如Tomcat)。
      • 内嵌服务器启动:在onRefresh()阶段创建WebServer并启动。
    6. 发布ContextRefreshedEvent:表示上下文刷新完成。

5. 执行Runners

  • 触发点:上下文刷新完成后,调用CommandLineRunnerApplicationRunner的实现。
  • 用途:执行启动后逻辑(如数据初始化、连接测试)。
java
@Component
public class MyRunner implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("应用启动完成,执行自定义逻辑");
    }
}

6. 完成启动

  • 触发事件ApplicationStartedEvent
  • 日志输出:显示应用启动耗时和访问地址(如Tomcat的端口)。
  • 进入运行状态:等待处理请求或执行其他任务。

关键机制解析

1. 自动配置(Auto-Configuration)

  • 触发条件@EnableAutoConfiguration注解(包含在@SpringBootApplication中)。
  • 流程
    • 扫描META-INF/spring.factories中的org.springframework.boot.autoconfigure.EnableAutoConfiguration配置类。
    • 根据条件注解(如@ConditionalOnClass@ConditionalOnMissingBean)过滤有效配置。
    • 将符合条件的配置类注册为Bean,完成组件自动装配。

2. 内嵌服务器启动

  • 实现类TomcatServletWebServerFactory(默认)、JettyServletWebServerFactory等。
  • 启动时机:在应用上下文刷新的onRefresh()阶段创建并启动服务器。
  • 端口绑定:通过ServerProperties配置(如server.port=8080)。

3. 事件驱动模型

  • 主要事件
    • ApplicationStartingEvent:应用启动开始。
    • ApplicationEnvironmentPreparedEvent:环境准备完成。
    • ApplicationPreparedEvent:Bean定义加载完成,上下文刷新前。
    • ApplicationStartedEvent:上下文刷新完成,Runners执行前。
    • ApplicationReadyEvent:Runners执行完成,应用就绪。
  • 监听器注册:通过META-INF/spring.factories@EventListener注解。

启动流程示意图

mermaid
sequenceDiagram
    participant User
    participant SpringApplication
    participant Environment
    participant ApplicationContext
    participant AutoConfiguration
    participant WebServer

    User->>SpringApplication: 执行run()
    SpringApplication->>Environment: 准备环境(加载配置)
    SpringApplication->>ApplicationContext: 创建应用上下文
    ApplicationContext->>AutoConfiguration: 加载自动配置类
    ApplicationContext->>WebServer: 启动内嵌服务器
    SpringApplication->>ApplicationContext: 刷新上下文(Bean初始化)
    SpringApplication->>Runners: 执行CommandLineRunner/ApplicationRunner
    SpringApplication-->>User: 启动完成,输出日志

总结

Spring Boot通过自动化配置和内嵌服务器机制,将传统Spring应用的复杂启动流程简化为几个关键步骤:

  1. 环境准备:加载配置和参数。
  2. 上下文创建与刷新:完成Bean注册、自动配置和服务器启动。
  3. 后置处理:执行Runners和监听器逻辑。

问题14:SpringBoot的Jar包和普通Jar包的区别

可执行性

  • 普通JAR包:通常包含项目的编译类文件和资源,但无法独立运行。要运行这些类,通常需要将JAR包添加到应用程序的类路径中,由外部容器或应用程序加载。
  • Spring Boot可执行JAR包:通过内嵌服务器(如Tomcat、Jetty等),使得应用程序可以独立运行。使用java -jar your-application.jar命令即可启动,无需依赖外部的应用服务器。

内部结构

  • 普通JAR包:其结构较为简单,主要包含项目的类文件和资源,通常没有特定的目录结构。
  • Spring Boot可执行JAR包:具有特定的目录结构,包括BOOT-INF/classes/(存放应用的类文件)、BOOT-INF/lib/(存放依赖的库)和META-INF/MANIFEST.MF(包含启动类等配置信息)。这种结构支持Spring Boot应用的自包含运行。

依赖管理

  • 普通JAR包:不包含其依赖的其他JAR包。使用时,需要确保所有依赖项都在类路径中,否则可能导致ClassNotFoundException等错误。
  • Spring Boot可执行JAR包:将所有依赖的JAR包打包在BOOT-INF/lib/目录下,确保应用在任何环境中都能找到所需的依赖,增强了部署的便利性和可靠性。

注意

  • Spring Boot默认生成的可执行JAR包不适合作为其他项目的依赖。