Spring-基础(下)

Spring设计

Spring 如何解决循环依赖

什么是循环依赖?

循环依赖(Circular Dependency)是指两个或多个类之间互相依赖,形成一个闭环。例如,类 A 依赖类 B,而类 B 又依赖类 A,从而形成依赖闭环。循环依赖在软件设计中是一个常见的问题,尤其是在使用依赖注入(Dependency Injection, DI)框架时。

循环依赖的三种情况

在 Spring 中,循环依赖问题可以分为以下三种情况:

  1. 使用构造参数传递依赖构成的循环依赖问题:两个类通过构造器注入形成循环依赖。
  2. 使用 setter 方法传递依赖且是原型模式下构成的循环依赖问题:两个类通过 setter 方法注入形成循环依赖,并且 Bean 的作用域是原型(Prototype)。
  3. 使用 setter 方法传递依赖且是单例模式下构成的循环依赖问题:两个类通过 setter 方法注入形成循环依赖,并且 Bean 的作用域是单例(Singleton)。

Spring 如何解决循环依赖

在 Spring 中,只有第三种情况(单例模式下使用 setter 方法构成的循环依赖)被解决了,其他两种情况在遇到循环依赖问题时,Spring 仍然会抛出相应的异常。

Spring 通过三级缓存(Three-Level Cache)来解决单例模式下使用 setter 方法构成的循环依赖问题。具体步骤如下:

  1. 实例化 Bean:Spring 在实例化 Bean 时,会先创建一个空的 Bean 对象,并将其放入一级缓存(singletonObjects)中。

  2. 属性赋值:当 Spring 发现存在循环依赖时,会先将当前 Bean 暴露给后续的依赖 Bean,从而解决循环依赖问题。这一步通过提前暴露 Bean 来解决循环依赖

  3. Bean 初始化:完成属性赋值后,Spring 将 Bean 实例化,并放入二级缓存(earlySingletonObjects)中。

  4. 依赖注入:Spring 继续对 Bean 进行依赖注入,如果发现循环注入,会将二级缓存中的 Bean 对象取出,完成初始化的 Bean 实例。

三级缓存策略详解

在 Spring 中,三级缓存策略是解决单例模式下使用 setter 方法构成的循环依赖问题的核心机制。三级缓存采用的都是 Map 类型的缓存数据结构,用于存储不同阶段的 Bean 实例。以下是对三级缓存策略的详细解释:

一级缓存(singletonObjects

一级缓存 使用 Map 存放完全初始化好的单例 Bean,也就是开箱即用的 Bean 实例。一级缓存存放在 DefaultSingletonBeanRegistry 中的 singletonObjects 属性中。

  • 数据结构Map<String, Object>,其中 key 是 Bean 的名称,value 是完全初始化好的 Bean 实例。
  • 作用:存放已经完全初始化好的单例 Bean,这些 Bean 可以直接使用。

二级缓存(earlySingletonObjects

二级缓存 使用 Map 存放已经实例化但还未完全初始化的 Bean。这些 Bean 可能还没有进行属性注入等操作。二级缓存存放在 DefaultSingletonBeanRegistry 中的 earlySingletonObjects 属性中。

  • 数据结构Map<String, Object>,其中 key 是 Bean 的名称,value 是已经实例化但还未完全初始化的 Bean 实例。
  • 作用:存放提前暴露的 Bean,这些 Bean 已经实例化但还未完全初始化,用于解决循环依赖问题。

三级缓存(singletonFactories

三级缓存 同样是一个 Map 类型的缓存,存储的是 ObjectFactory 对象,这些对象可以生成早期 Bean 的引用。当一个 Bean 正在创建过程中,如果被其他 Bean 依赖,那么这个正在创建的 Bean 就会通过这个 ObjectFactory 来创建一个早期引用,从而解决循环依赖问题。三级缓存存放在 DefaultSingletonBeanRegistry 中的 singletonFactories 属性中。

  • 数据结构Map<String, ObjectFactory<?>>,其中 key 是 Bean 的名称,valueObjectFactory 对象,用于生成早期 Bean 的引用。
  • 作用:存放 Bean 工厂,用于创建提前暴露的 Bean,解决循环依赖问题。

三级缓存策略的工作流程

  1. 实例化 Bean:Spring 在实例化 Bean 时,会先创建一个空的 Bean 对象,并将其放入三级缓存(singletonFactories)中。

  2. 提前暴露 Bean:当 Spring 发现存在循环依赖时,会先将当前 Bean 暴露给后续的依赖 Bean,从而解决循环依赖问题。这一步通过将 Bean 工厂放入三级缓存中来实现。

  3. 属性赋值:完成属性赋值后,Spring 将 Bean 实例化,并放入二级缓存(earlySingletonObjects)中。

  4. 依赖注入:Spring 继续对 Bean 进行依赖注入,如果发现循环注入,会将二级缓存中的 Bean 对象取出,完成初始化的 Bean 实例。

  5. 完全初始化:当 Bean 完全初始化后,Spring 将其放入一级缓存(singletonObjects)中,并从二级缓存和三级缓存中移除。

Spring 设计模式

Spring 框架是一个高度模块化和可扩展的框架,它广泛使用了多种设计模式来实现其核心功能。以下是 Spring 中常用的一些设计模式及其应用场景:

1. 工厂模式(Factory Pattern)

工厂模式 是一种创建型设计模式,用于创建对象而不指定具体的类。Spring 使用工厂模式来创建和管理 Bean 对象。

  • BeanFactory:Spring 的核心接口之一,用于创建和管理 Bean 实例。BeanFactory 提供了基本的 Bean 创建和获取功能。
  • ApplicationContextApplicationContextBeanFactory 的子接口,提供了更高级的功能,如国际化、事件发布等。

2. 代理模式(Proxy Pattern)

代理模式 是一种结构型设计模式,用于为其他对象提供一个代理以控制对这个对象的访问。Spring AOP 通过使用反射机制和动态代理来实现切面编程。

  • JDK Proxy:适用于被代理对象实现了接口的情况。
  • CGLIB:适用于被代理对象没有实现接口的情况。

3. 单例模式(Singleton Pattern)

单例模式 是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。Spring 中默认的 Bean 都是单例的。

  • 单例 Bean:Spring 容器中的 Bean 默认是单例的,即在整个应用中只有一个实例。

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

模板方法模式 是一种行为型设计模式,定义了一个算法的骨架,并允许子类在不改变算法结构的情况下重新定义算法的某些步骤。Spring 中的 JdbcTemplate 和 HibernateTemplate 等就是模板方法模式的典型应用。

  • JdbcTemplate:提供了数据库操作的模板方法,子类可以通过实现回调接口来定义具体的操作。

5. 装饰器模式(Decorator Pattern)

装饰器模式 是一种结构型设计模式,允许动态地向对象添加功能。Spring 中的 DataSource 实现类(如 DataSourceTransactionManager)就是装饰器模式的典型应用。

6. 观察者模式(Observer Pattern)

观察者模式 是一种行为型设计模式,定义了对象之间的一对多依赖关系,当一个对象状态发生改变时,所有依赖它的对象都会收到通知并自动更新。Spring 事件驱动模型就是一个典型的观察者模式。

  • ApplicationEvent:Spring 中的事件类,用于定义事件。
  • ApplicationListener:Spring 中的监听器接口,用于监听事件。

7. 适配器模式(Adapter Pattern)

适配器模式 是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。Spring AOP 的增强和通知使用了适配器模式,Spring MVC 中的控制器层也使用了适配器模式。

  • AdvisorAdapter:Spring AOP 中的适配器接口,用于将通知适配为增强。
  • HandlerAdapter:Spring MVC 中的适配器接口,用于将控制器适配为处理器。

Spring 常用注解

Spring 框架提供了丰富的注解(Annotation)来简化配置和开发过程。以下是一些常用的 Spring 注解及其作用:

1. @Autowired 注解

@Autowired 注解主要用于自动装配(Dependency Injection)Bean。当 Spring 容器中存在与注入属性类型匹配的 Bean 时,它会自动将 Bean 注入到属性中,类似于 new 对象一样。

1
2
3
4
5
6
7
8
9
@Component
public class MyService {
@Autowired
private MyRepository myRepository;

public void doSomething() {
myRepository.save();
}
}

2. @Component 注解

@Component 注解用于将一个类标记为 Spring 中的 Bean。当一个类被 @Component 标记后,Spring 会将该类实例化为一个 Bean,并将其存放到 Bean 容器中。

1
2
3
4
5
6
@Component
public class MyComponent {
public void doSomething() {
System.out.println("Doing something in MyComponent");
}
}

3. @Configuration 注解

@Configuration 注解用于将一个类标记为 Spring 的配置类。配置类可以包含 @Bean 注解的方法,用于定义和配置 Bean,作为全局配置。

1
2
3
4
5
6
7
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyService();
}
}

4. @Bean 注解

@Bean 注解用于标记一个方法,将该方法作为 Spring 的 Bean 工厂方法。当一个方法被 @Bean 注解标记时,Spring 会将该方法的返回值作为一个 Bean,并将其添加到 Spring 容器中。如果自定义配置会经常用到该注解。

1
2
3
4
5
6
7
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyService();
}
}

5. @Service 注解

@Service 注解用于将一个类标记为服务层的组件。它是 @Component 注解的特例,用于标记服务层的 Bean,一般用于标记服务层的实现类。

1
2
3
4
5
6
@Service
public class MyService {
public void doSomething() {
System.out.println("Doing something in MyService");
}
}

6. @Repository 注解

@Repository 注解通常用于标记一个类作为数据访问层的组件。它也是 @Component 注解的特例,用于标记数据访问层的组件,该注解开发过程中很容易忘记而导致无法访问数据库。

1
2
3
4
5
6
@Repository
public class MyRepository {
public void save() {
System.out.println("Saving data in MyRepository");
}
}

7. @Controller 注解

@Controller 注解通常用于标记一个类作为控制层的组件。它也是 @Component 注解的特例,用于标记控制层的 Bean。

1
2
3
4
5
6
7
8
@Controller
public class MyController {
@RequestMapping("/hello")
@ResponseBody
public String hello() {
return "Hello, World!";
}
}

总结

Spring 框架提供了丰富的注解来简化配置和开发过程。通过合理使用这些注解,可以提高代码的可读性、可维护性和可扩展性。以下是这些注解的简要总结:

注解 作用
@Autowired 自动装配 Bean
@Component 将类标记为 Spring Bean
@Configuration 将类标记为 Spring 配置类
@Bean 将方法标记为 Spring Bean 工厂方法
@Service 将类标记为服务层组件
@Repository 将类标记为数据访问层组件
@Controller 将类标记为控制层组件

Spring 扩展机制

Spring框架提供了丰富的扩展机制,使得开发者可以根据自己的需求定制和扩展Spring的功能。以下是一些常用的扩展点及其详细介绍:

1. BeanFactoryPostProcessor

BeanFactoryPostProcessor允许在Spring容器实例化Bean之前修改Bean的定义。开发者可以通过实现该接口,在Bean实例化之前对Bean定义进行自定义处理。

1
2
3
4
5
6
7
8
9
10
11
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 修改Bean定义
}
}

2. BeanPostProcessor

BeanPostProcessor允许在Bean实例化、配置以及初始化的前后对其进行额外的处理。开发者可以通过实现该接口,在Bean生命周期的关键点插入自定义逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class CustomBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// Bean初始化前的处理逻辑
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// Bean初始化后的处理逻辑
return bean;
}
}

3. PropertySource

PropertySource用于定义不同的属性源,如文件、数据库等,以便在Spring应用中使用。开发者可以通过自定义PropertySource来加载和使用外部属性。

1
2
3
4
5
6
7
8
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:custom.properties")
public class CustomPropertyConfig {
// 配置类
}

4. Spring MVC中的HandlerInterceptor

HandlerInterceptor用于拦截处理请求,可以在请求处理前、处理中和处理后执行特定逻辑。开发者可以通过实现该接口,在请求处理的不同阶段插入自定义逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CustomHandlerInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 请求处理前的逻辑
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 请求处理中的逻辑
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求处理后的逻辑
}
}

5. Spring MVC中的ControllerAdvice

ControllerAdvice用于全局处理控制器的异常、数据绑定和校验。开发者可以通过实现该接口,在控制器处理请求时插入全局逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
@ResponseBody
public String handleException(Exception e) {
// 异常处理逻辑
return "Error: " + e.getMessage();
}
}

6. Spring Boot的自动配置

Spring Boot提供了自动配置机制,开发者可以通过创建配置类,实现对框架和第三方库的自动配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CustomAutoConfiguration {

@Bean
@ConditionalOnProperty(name = "custom.enabled", havingValue = "true")
public CustomService customService() {
return new CustomService();
}
}

7. 自定义注解

Spring支持自定义注解,开发者可以通过创建自定义注解来实现特定的功能。

1
2
3
4
5
6
7
8
9
10
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
String value() default "";
}

事务管理

Spring事务失效场景分析

在Spring Boot应用中,事务管理是确保数据一致性和完整性的关键机制。Spring通过其事务管理模块,特别是@Transactional注解,来实现事务操作。然而,事务失效的情况时有发生,这通常是由于配置不当或代码实现中的细微问题所致。以下是一些可能导致Spring事务失效的典型场景及其深入分析:

1. 未捕获的异常

场景描述: 当一个异常未被捕获,并且该异常未被处理或传播至事务边界之外时,事务会失效。

深入分析: 在Spring中,默认情况下,事务会在遇到未捕获的异常时自动回滚。然而,如果异常被捕获但未重新抛出,或者异常被捕获后通过某种方式被“吞噬”,事务管理器将无法感知到异常的发生,从而导致事务无法回滚。

解决方案: 确保所有可能引发异常的代码路径都被正确处理,并在必要时重新抛出异常,以便事务管理器能够检测到异常并触发回滚操作。

2. 未受检异常

场景描述: 默认情况下,Spring会对未受检异常(如RuntimeException及其子类)进行回滚,此时事务失效。

深入分析: 未受检异常通常表示程序逻辑中的错误或不可恢复的情况。Spring默认将这些异常视为事务失败的信号,并触发回滚操作。然而,如果开发者未意识到这一点,可能会在代码中无意间抛出未受检异常,导致事务失效。

解决方案: 在编写代码时,应尽量避免抛出未受检异常,或者在必要时通过@Transactional注解的rollbackFor属性显式指定哪些异常应触发回滚。

3. 事务属性传递不当

场景描述: 当事务内存在嵌套事务且传播了一定的属性时,若传播的属性配置不当,可能导致事务失效。

深入分析: Spring提供了多种事务传播行为(如REQUIREDREQUIRES_NEWNESTED等),这些行为决定了事务如何在方法调用链中传播。如果传播行为配置不当,可能会导致事务边界不明确,从而引发事务失效。

解决方案: 仔细选择和配置事务传播行为,确保每个方法调用链中的事务边界清晰且符合业务逻辑需求。

4. 多数据源配置不当

场景描述: 当事务操作涉及多个数据源时,若配置文件不当可能导致事务失效。

深入分析: 在多数据源场景下,Spring需要明确指定每个事务操作所涉及的数据源。如果配置不当,可能会导致事务管理器无法正确识别和处理事务,从而引发事务失效。

解决方案: 确保多数据源配置正确,并在必要时使用@Transactional注解的value属性显式指定事务管理器。

5. 事务调用外部方法

场景描述: 当事务内调用其他方法,且其他方法未使用@Transactional标记,可能导致事务失效。

深入分析: 在Spring中,事务是通过代理机制实现的。如果一个事务方法调用了另一个未标记为事务的方法,Spring的事务代理将无法拦截该调用,从而导致事务失效。

解决方案: 确保所有需要事务支持的方法都使用@Transactional注解进行标记,或者通过AOP(面向切面编程)机制显式配置事务拦截。

6. 非公有方法

场景描述: 如果@Transactional标记在私有方法或其他非公有方法上,事务也会失效。

深入分析: Spring的事务管理依赖于代理机制,而代理机制通常只能拦截公有方法的调用。因此,如果@Transactional注解被应用于非公有方法(如私有方法、受保护方法或包级私有方法),Spring将无法创建事务代理,从而导致事务失效。

解决方案:@Transactional注解应用于公有方法,或者通过AOP机制显式配置事务拦截。

Spring事务中调用this是否生效?

在Spring事务管理中,调用this是否生效是一个常见且重要的技术问题。为了深入理解这一问题,我们需要从Spring事务的实现机制入手。

Spring事务的实现机制

Spring事务管理的核心机制是通过AOP(面向切面编程)实现的。具体来说,Spring使用代理模式来拦截被@Transactional注解标记的方法,并在方法执行前后插入事务管理逻辑。

调用this是否生效?

结论: 在Spring事务中,调用this是不生效的。

原因分析

  1. 代理对象与目标对象分离

    • 在Spring中,事务管理是通过代理对象实现的。代理对象与目标对象(即实际的业务逻辑类)是分离的。
    • 当一个方法被@Transactional注解标记时,Spring会创建一个代理对象来拦截该方法的调用。
  2. this引用指向目标对象

    • 在目标对象的方法内部,this引用指向的是目标对象本身,而不是代理对象。
    • 因此,当在目标对象的方法内部调用this引用的其他方法时,实际上是直接调用了目标对象的方法,绕过了代理对象的事务管理逻辑。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class MyService {

@Transactional
public void methodA() {
// 事务管理生效
methodB(); // 事务管理不生效,因为是通过this调用的
}

public void methodB() {
// 业务逻辑
}
}

在上面的示例中,methodA@Transactional注解标记,因此Spring会为其创建一个代理对象。然而,当methodA内部调用this.methodB()时,实际上是直接调用了目标对象的methodB,绕过了代理对象的事务管理逻辑,导致methodB的事务管理不生效。

解决方案

为了避免上述问题,可以采用以下几种解决方案:

  1. 注入代理对象:通过依赖注入的方式获取代理对象,而不是直接使用this引用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class MyService {

@Autowired
private MyService self; // 注入代理对象

@Transactional
public void methodA() {
// 事务管理生效
self.methodB(); // 通过代理对象调用,事务管理生效
}

public void methodB() {
// 业务逻辑
}
}
  1. 使用AOP切面:通过AOP切面显式配置事务拦截逻辑。
1
2
3
4
5
6
7
8
9
10
@Aspect
@Component
public class TransactionAspect {

@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
public Object aroundTransactionalMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// 事务管理逻辑
return joinPoint.proceed();
}
}

在Spring事务管理中,调用this是不生效的,因为this引用指向的是目标对象,而不是代理对象。为了避免事务失效,可以通过注入代理对象或使用AOP切面来确保事务管理逻辑的正确执行。

Bean

Spring Bean的生命周期

Spring框架的核心之一是其强大的依赖注入(DI)和控制反转(IoC)机制,而Bean的生命周期管理则是这一机制的重要组成部分。理解Spring Bean的生命周期对于掌握Spring框架的工作原理和优化应用性能至关重要。以下是Spring Bean生命周期的详细步骤:

  1. 实例化Bean

Spring容器启动时,首先会查找并加载需要被管理的Bean定义。然后,Spring容器会根据Bean定义创建Bean实例。实例化过程通常涉及调用Bean的构造函数。

  1. 属性注入

Bean实例化之后,Spring容器会根据Bean定义中的配置,将依赖的Bean引用和属性值注入到Bean实例中。这一过程通常通过调用Bean的setter方法或直接设置字段值来完成。

  1. 实现BeanNameAware接口

如果Bean实现了BeanNameAware接口,Spring容器会在属性注入完成后,调用setBeanName(String name)方法,将Bean的名称(通常是Bean的id)传递给Bean。

  1. 实现BeanFactoryAware接口

如果Bean实现了BeanFactoryAware接口,Spring容器会在BeanNameAware阶段之后,调用setBeanFactory(BeanFactory beanFactory)方法,将BeanFactory实例传递给Bean。

  1. 实现ApplicationContextAware接口

如果Bean实现了ApplicationContextAware接口,Spring容器会在BeanFactoryAware阶段之后,调用setApplicationContext(ApplicationContext applicationContext)方法,将ApplicationContext实例传递给Bean。

  1. 前置处理器(BeanPostProcessor)

如果Bean实现了BeanPostProcessor接口,Spring容器会在初始化之前调用postProcessBeforeInitialization(Object bean, String beanName)方法。这一阶段允许开发者在Bean初始化之前对其进行自定义处理。

  1. 初始化Bean

在BeanPostProcessor前置处理之后,Spring容器会进行Bean的初始化。

  • 实现InitializingBean接口:如果Bean实现了InitializingBean接口,Spring容器会调用afterPropertiesSet()方法。
  • 自定义初始化方法:如果Bean定义中通过init-method属性指定了初始化方法,Spring容器会调用该方法。
  1. 后置处理器(BeanPostProcessor)

在Bean初始化之后,Spring容器会调用BeanPostProcessorpostProcessAfterInitialization(Object bean, String beanName)方法。这一阶段允许开发者在Bean初始化之后对其进行自定义处理。

  1. Bean的使用

此时,Bean已经完全初始化并准备好被使用。Bean将在应用上下文中驻留,直至应用上下文被销毁。

  1. Bean的销毁

当应用上下文被销毁时,Spring容器会进行Bean的销毁处理。

  • 实现DisposableBean接口:如果Bean实现了DisposableBean接口,Spring容器会调用destroy()方法。
  • 自定义销毁方法:如果Bean定义中通过destroy-method属性指定了销毁方法,Spring容器会调用该方法。

Spring Bean生命周期(图片来源小林coding)

Spring Bean的单例与多例模式

在Spring框架中,Bean的实例化模式是一个重要的配置选项,它决定了Bean在应用中的创建和使用方式。Spring默认采用单例(Singleton)模式,但也可以根据需要配置为多例(Prototype)模式。以下是对这两种模式的详细解释及其生命周期的差异。

单例模式(Singleton)

默认行为

在Spring中,Bean默认是单例的。这意味着在整个应用上下文中,每个Bean定义只会创建一个实例,并将其缓存起来。当需要使用该Bean时,Spring容器会从缓存中取出该实例,而不是重新创建。

优点

  • 提高性能:由于Bean实例只创建一次,减少了频繁创建和销毁对象的开销。
  • 节省内存:单例Bean在整个应用中共享,减少了内存占用。
  • 提高复用率:单例Bean可以在多个地方重复使用,减少了对象的创建和销毁次数。

多例模式(Prototype)

配置方式

要将Bean配置为多例模式,可以在Bean定义中设置scope属性为prototype。例如:

1
<bean id="myBean" class="com.example.MyBean" scope="prototype"/>

或者在Java配置中:

1
2
3
4
5
@Bean
@Scope("prototype")
public MyBean myBean() {
return new MyBean();
}

行为

在多例模式下,每次请求该Bean时,Spring容器都会创建一个新的实例。这意味着每个请求都会得到一个独立的Bean实例。对于多例Bean,Spring容器只负责创建实例,并将实例交给使用者。Spring不会管理多例Bean的完整生命周期。

优点

  • 隔离性:每个请求都得到一个独立的Bean实例,避免了状态共享的问题。
  • 灵活性:适用于需要频繁创建和销毁对象的场景。

Spring Bean的作用域

在Spring框架中,Bean的作用域(Scope)定义了其生命周期与可见性。不同的作用域决定了Spring容器如何管理这些Bean实例,包括它们的创建、销毁以及是否被多个用户共享。Spring提供了多种作用域,以满足不同的应用需求。

  • Singleton(单例)是Spring的默认作用域,在整个应用中只会创建一次Bean实例。Spring容器在整个生命周期中管理该Bean实例,并将其缓存起来,供多个用户共享。这种作用域适用于无状态的Bean,如服务层、DAO层等,以及需要全局共享的Bean。

  • Prototype(多例)作用域下,每次请求都会新建一个Bean实例。新的实例交由使用者后,容器将不再管理该实例的后续生命周期。这种作用域适用于有状态的Bean,如表单对象、对话框等,以及需要频繁创建和销毁的Bean。

  • Request(请求)作用域在每个HTTP请求时生成一个新的Bean实例,仅在Spring Web应用程序中有效,适用于需要在单个HTTP请求中保持状态的Bean。

  • Session(会话)作用域在Session范围内只会创建一个Bean实例,该Bean实例仅在会话范围内共享。这种作用域适用于需要在用户会话中保持状态的Bean,如用户购物车。

  • Application(应用)作用域在当前ServletContext中仅存在一个Bean实例,适用于需要在整个应用中共享的Bean,如全局配置对象。

  • WebSocket(WebSocket)作用域在WebSocket范围内仅存在一个Bean实例,适用于需要在WebSocket会话中保持状态的Bean。

  • Spring还允许开发者自定义作用域,通过实现Scope接口来自定义Bean的作用域。这种自定义作用域适用于需要特殊生命周期管理的Bean。

在Bean加载/销毁前后增加自定义逻辑

在Spring框架中,开发者可以通过多种方式在Bean加载和销毁前后增加自定义逻辑。这些方式包括使用生命周期回调接口、注解以及XML配置。以下是详细的方法介绍:

1. 使用init-methoddestroy-method

在XML配置中,可以通过init-methoddestroy-method属性指定Bean的初始化和销毁方法。

XML配置示例

1
2
<bean id="myBean" class="com.example.MyBeanClass"
init-method="init" destroy-method="destroy" />

Bean类实现

1
2
3
4
5
6
7
8
9
public class MyBeanClass {
public void init() {
// 初始化逻辑
}

public void destroy() {
// 销毁逻辑
}
}

2. 实现InitializingBean接口和DisposableBean接口

Spring提供了两个生命周期回调接口:InitializingBeanDisposableBean。通过实现这两个接口,可以在Bean初始化和销毁时执行自定义逻辑。

Bean类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.DisposableBean;

public class MyBeanClass implements InitializingBean, DisposableBean {

@Override
public void afterPropertiesSet() throws Exception {
// 初始化逻辑
}

@Override
public void destroy() throws Exception {
// 销毁逻辑
}
}

3. 使用@PostConstruct注解和@PreDestroy注解

Spring支持使用JSR-250注解@PostConstruct@PreDestroy来指定Bean的初始化和销毁方法。

Bean类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class MyBeanClass {

@PostConstruct
public void init() {
// 初始化逻辑
}

@PreDestroy
public void destroy() {
// 销毁逻辑
}
}

4. 使用@Bean注解的initMethod属性和destroyMethod属性

在Java配置中,可以使用@Bean注解的initMethoddestroyMethod属性来指定Bean的初始化和销毁方法。

Java配置示例

1
2
3
4
5
6
7
8
9
10
11
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

@Bean(initMethod = "init", destroyMethod = "destroy")
public MyBeanClass myBean() {
return new MyBeanClass();
}
}

Bean类实现

1
2
3
4
5
6
7
8
9
public class MyBeanClass {
public void init() {
// 初始化逻辑
}

public void destroy() {
// 销毁逻辑
}
}

Bean注入与XML注入

在Spring框架中,Bean的注入方式主要有两种:注解注入和XML注入。这两种方式各有优缺点,适用于不同的场景。以下是对这两种注入方式的详细解释及其底层执行步骤。

Bean注解注入

注解注入是Spring中常用的一种依赖注入方式,通过在类和属性上使用特定的注解,Spring容器可以自动完成Bean的注册和依赖注入。执行步骤如下:

  1. 类路径扫描:当Spring容器启动时,它会进行类路径扫描,查找带有特定注解的类,如@Component@Service@Repository@Controller等。这些注解标记的类会被Spring容器识别为候选Bean。

  2. 注册Bean定义:找到的类会被注册到BeanDefinitionRegistry中,Spring容器会为其生成Bean定义信息,包括类的全限定名、作用域、依赖关系等。

  3. 依赖注入:在对Bean进行实例化时,Spring容器会检查属性中是否存在@Autowired@Inject@Resource注解。如果有,Spring会根据注解的信息进行依赖注入。注入方式可以是构造函数注入、setter方法注入或字段注入。

XML注入

XML注入是Spring早期常用的依赖注入方式,通过在XML配置文件中定义Bean及其依赖关系,Spring容器可以完成Bean的注册和依赖注入。执行步骤如下:

  1. Bean定义解析:Spring容器通过XmlBeanDefinitionReader类解析XML配置文件,读取其中的<bean>标签以获取Bean的定义信息。解析后的Bean信息包括类的全限定名、作用域、依赖关系、初始化和销毁方法等。

  2. 注册Bean定义:解析后的Bean信息将被注册到BeanDefinitionRegistry中,Spring容器会根据这些信息生成Bean定义。

  3. 实例化和依赖注入:当应用程序请求某个Bean时,Spring容器会根据已有的Bean定义,使用反射机制创建该Bean实例。然后根据Bean定义中的配置,通过构造函数、setter方法或字段声明注入等方式注入所需要的依赖Bean。

Spring MVC

了解MVC

MVC(Model-View-Controller)是一种经典的软件设计模式,广泛应用于Web开发和其他类型的应用程序中。MVC模式通过将应用程序的不同职责分离到三个独立的组件中,提高了代码的可维护性、可扩展性和可重用性。

Spring MVC(图片来源网络)

  1. Model(模型)

模型是应用程序的核心部分,负责处理业务逻辑和数据操作。模型可以分为两类:

  • 数据承载Bean:这些是实体对象(如UserProduct等),专门用于承载业务数据。它们通常对应数据库中的表结构,包含数据的属性和方法。
  • 业务处理Bean:这些是服务层或数据访问层对象(如ServiceDao等),专门用于处理用户发起的请求。它们负责执行业务逻辑、数据验证、数据持久化等操作。
  1. View(视图)

视图是用户界面,负责向用户展示数据和接收用户的输入。视图通常是HTML、JSP、Thymeleaf、React等前端技术实现的页面或组件。

  1. Controller(控制器)

控制器是模型和视图之间的桥梁,负责处理用户请求并将其转发给相应的模型进行处理。控制器根据模型的计算结果,决定如何更新视图。

Spring MVC的处理流程

Spring MVC是Spring框架中的一个重要模块,用于构建基于MVC模式的Web应用程序。Spring MVC的处理流程涵盖了从用户请求到最终响应的整个过程。以下是对Spring MVC处理流程的详细解释:

Spring MVC处理流程(图片来源网络)

  1. 用户发送请求到前端控制器(DispatcherServlet):用户通过浏览器或其他客户端发送HTTP请求到Spring MVC应用程序。请求首先被前端控制器DispatcherServlet接收。

  2. DispatcherServlet调用处理器映射器(HandlerMapping)DispatcherServlet接收到请求后,会调用处理器映射器HandlerMapping,以确定哪个处理器(Controller)应该处理该请求。

  3. 处理器映射器生成处理器执行链(HandlerExecutionChain)HandlerMapping根据请求的URL找到具体的处理器(Controller),并生成一个处理器执行链HandlerExecutionChain。该执行链包括处理器对象和处理器拦截器(如果有配置)。

  4. DispatcherServlet获取处理器适配器(HandlerAdapter)DispatcherServlet根据处理器(Controller)获取相应的处理器适配器HandlerAdapter。处理器适配器负责执行处理器(Controller)的方法。

  5. 处理器适配器执行处理器(Controller)HandlerAdapter执行处理器(Controller)的方法,进行一系列处理操作,如数据封装、参数解析等。

  6. 处理器执行完成返回ModelAndView:处理器(Controller)执行完成后,返回一个ModelAndView对象。ModelAndView包含视图名称和模型数据。

  7. HandlerAdapter将ModelAndView返回给DispatcherServletHandlerAdapter将处理器(Controller)返回的ModelAndView对象传递给DispatcherServlet

  8. DispatcherServlet调用视图解析器(ViewResolver)DispatcherServlet接收到ModelAndView后,调用视图解析器ViewResolver,根据视图名称解析出具体的视图对象。

  9. ViewResolver解析视图并返回给DispatcherServletViewResolver解析视图名称,找到对应的视图对象(如JSP、Thymeleaf等),并将其返回给DispatcherServlet

  10. DispatcherServlet对视图进行渲染DispatcherServlet将模型数据传递给视图对象,并调用视图对象的渲染方法,生成最终的HTML或其他格式的响应内容。

  11. DispatcherServlet响应用户DispatcherServlet将渲染后的响应内容返回给用户,完成整个请求处理流程。

HandlerMapping与HandlerAdapter

在Spring MVC框架中,HandlerMappingHandlerAdapter是两个关键组件,它们协同工作以处理用户请求并调用相应的控制器方法。以下是对这两个组件的详细解释及其工作流程。

HandlerMapping

HandlerMapping的主要作用是将HTTP请求映射到相应的控制器(Controller)方法。它根据请求的URL、HTTP方法、请求参数等信息,确定哪个控制器方法应该处理该请求

工作流程:

  1. 接收请求DispatcherServlet接收到HTTP请求。
  2. 调用HandlerMappingDispatcherServlet调用HandlerMapping,请求其查找处理该请求的控制器方法。
  3. 查找处理器HandlerMapping根据请求的URL、HTTP方法等信息,查找并返回一个HandlerExecutionChain对象。该对象包含处理器(Controller)和拦截器(如果有配置)。
  4. 返回HandlerExecutionChainHandlerMappingHandlerExecutionChain返回给DispatcherServlet

HandlerAdapter

HandlerAdapter的主要作用是调用处理器(Controller)方法来处理请求。它负责解析请求参数、调用控制器方法、处理返回值等操作。

工作流程:

  1. 接收HandlerExecutionChainDispatcherServlet接收到HandlerMapping返回的HandlerExecutionChain
  2. 获取HandlerAdapterDispatcherServlet根据处理器(Controller)的类型,获取相应的HandlerAdapter
  3. 调用处理器方法HandlerAdapter调用处理器(Controller)的方法,进行请求处理。
  4. 处理返回值HandlerAdapter处理处理器方法的返回值,生成ModelAndView对象。
  5. 返回ModelAndViewHandlerAdapterModelAndView返回给DispatcherServlet

HandlerMappingHandlerAdapter是Spring MVC框架中的两个关键组件,它们协同工作以处理用户请求并调用相应的控制器方法。HandlerMapping负责将请求映射到控制器方法,而HandlerAdapter负责调用控制器方法并处理返回值。