Spring 是一个强大的 Java 开发框架,广泛用于企业级应用程序开发,其主要作用是简化开发过程,提高代码的可维护性和可扩展性。以下是 Spring 的核心功能和作用的详细说明:

Spring 3种配置方式

  1. 基于XML的配置
  2. 基于注解的配置

    • 常见注解:

      • @Component:标记类为 Spring 的组件
      • @Autowired:自动注入依赖
      • @Qualifier:指定注入的具体实现
      • @Value:注入简单值
      • @Configuration 和 @Bean:定义配置类和 Bean。
  3. 基于Java的配置

    @Configuration
    public class AppConfig {
    
     @Bean
     public MyBean myBean() {
         MyBean myBean = new MyBean();
         myBean.setName("Spring Java Config");
         return myBean;
     }
    }
    
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    MyBean myBean = context.getBean(MyBean.class);

    自动组装

    Spring 自动装配(Autowiring)是指 Spring 容器根据配置或注解,自动满足 Bean 之间的依赖关系,而不需要显式配置依赖关系。这可以简化开发并减少 XML 配置文件或手动编码的冗余。

自动组装的方式

  1. No(默认值)

    • 不需要自动组装,需要显示配置依赖
  2. ByType

    • 根据属性的类型在容器中查找匹配的Bean,并自动组装
  3. ByName

    • 根据属性名称在容器中查找与属性名称同名的 Bean,并自动装配
  4. Constructor

    • 根据构造方法的参数类型自动装配依赖
  5. @Autowired(最常用)

    • 根据注解自动装配
  6. @Qualifier(多个子类组装父类, 用子类实现父类)

    • 用于在多候选 Bean 中选择特定的 Bean。

基于注解的自动装配(推荐方式)

  1. 使用 @Autowired 自动装配
@Component
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public void performAction() {
        userRepository.save();
    }
}

@Repository
public class UserRepository {
    public void save() {
        System.out.println("User saved!");
    }
}
  1. 使用 @Qualifier 解决冲突
@Component
public class UserService {
    @Autowired
    @Qualifier("myUserRepository")
    private UserRepository userRepository;

    public void performAction() {
        userRepository.save();
    }
}

@Repository("myUserRepository")
public class UserRepository {
    public void save() {
        System.out.println("User saved!");
    }
}

当容器中有多个同类型 Bean 时,可使用 @Qualifier 指定具体的 Bean。

依赖注入(DI)==> 对象的引用注入

依赖注入是一种设计模式,用于将对象的依赖通过外部传递方式注入,而不是在对象内部自己创建依赖。这种方式能够解耦对象与其依赖,提高代码的灵活性、可测试性和可维护性。

依赖注入的三种方式

  1. 构造器注入

在对象创建时通过构造器传递依赖。

public class UserService {

    public final UserRepository userRepository;

    public UserService userService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  1. Setter注入

使用公共的 setter 方法在对象创建后设置依赖

public class UserService {
    public final UserRepository userReposititory;

    public void setUserRepository(UserRepository userReposititory) {
        this.userReposititory = userReposititory;
    }
}
  1. 字段注入(基于注解)

直接在字段上添加注解

public class userService() {
    @Auotwired
    private UserRepository userReposititory;
}

Spring 中依赖注入的实现

Spring IoC 容器是依赖注入的核心,通过 Bean 定义和生命周期管理 完成依赖的注入。

主要原理和流程

  1. Bean的定义

    • Spring 使用配置文件(如 XML 或 Java 配置类)或注解(如 @Component)定义 Bean
  2. Bean的加载

    • Spring 容器会扫描或解析配置,找到所有需要管理的 Bean,并将其存储在 BeanDefinition 对象中
  3. Bean 的实例化:

    • 根据 BeanDefinition,通过反射机制创建Bean的实例。
  4. 依赖解析:

    • Spring 根据 Bean 的依赖关系,在容器中查找需要注入的依赖(例如,通过构造器、setter 方法或字段注入)。
  5. 注入依赖

    • 使用反射或代理技术,将找到的依赖赋值到目标Bean中
  6. 初始化回调

    • 如果Bean实现了InitializingBean接口,Spring会调用afterPropertiesSet方法
    • 或者,检查是否有自定义的初始化方法(通过 @PostConstruct 标注)。
  7. Bean的生命周期管理

    • Spring 会根据 Bean 的作用域(如单例或原型)对其生命周期进行管理

底层原理

使用反射机制

Spring 在依赖注入时,主要通过反射来完成注入:

  • 构造器注入:

    • 使用Constructor类的newInstance方法
Constructor<?> constructor = clazz.getConstructor(paramTypes);
Object bean = constructor.newInstance(args);
  • Setter注入

    • 使用Metod类的invoke方法
Method method = clazz.getMethod("setDependency", Dependency.class);
method.invoke(bean, dependency);
  • 字段注入

    • 使用Field类的set方法直接修改字段
Field field = clazz.getDeclaredField("dependency");
field.setAccessible(true);
field.set(bean, dependency);
BeanDefinition 和 BeanFactory

Spring 通过 BeanDefinition 和 BeanFactory 实现 Bean 的加载、实例化和依赖注入

BeanDefinition:包含Bean的元信息(类名、依赖、作用域)
BeanFactory:负责解析 BeanDefinition 并创建 Bean

注入依赖的主要逻辑:

  1. 解析Bean的依赖信息
  2. 在容器中找到依赖对象
  3. 使用反射注入
InitializingBean
  • InitializingBean 是一个接口,它提供了一个回调方法,用于在 Spring 完成依赖注入后进行初始化操作
  • 其主要作用是允许 Bean 在其属性设置完成后执行自定义的初始化逻辑

作用:

  1. 依赖注入后的初始化

    • 在 Bean 的属性依赖注入完成后,afterPropertiesSet() 方法允许开发者执行额外的逻辑,例如验证属性值或初始化复杂逻辑
  2. 初始化逻辑集中管理

    • 如果初始化逻辑较复杂,放在构造器中可能显得混乱,afterPropertiesSet() 提供了一个清晰的分离点
  3. 补充配置

    • 用于检查依赖项是否正确配置,或者设置默认值

场景:

  1. 属性的校验和初始化
  2. 资源连接初始化

    • 在依赖注入完成后,连接外部资源(如数据库连接、消息队列连接)。
  3. 预处理操作

    • 加载配置文件,初始化缓存,或者进行一些预处理任务。
  4. 业务逻辑初始化

    • 构建复杂对象,或者启动某些特定服务。
InitializingBean与其他初始化方式比较(@PostConstruct)
  1. 构造方法

    • 构造器适合简单的初始化,但当依赖项注入完成后需要初始化时,afterPropertiesSet 更合适
  2. 自定义初始化方法

    • 配置文件中可以指定初始化方法,功能与 afterPropertiesSet 类似,但更解耦,不依赖接口。
  3. @PostConstruct 注解

    • 在 Spring 3 之后推荐使用,功能等同于 InitializingBean,但不与 Spring 特定接口耦合,更加通用。
import jakarta.annotation.PostConstruct;

public class MyBean {
    private String property;

    public void setProperty(String property) {
        this.property = property;
    }

    @PostConstruct
    public void init() {
        if (this.property == null || this.property.isEmpty()) {
            throw new IllegalArgumentException("Property must be set");
        }
        System.out.println("PostConstruct: Initialization logic executed.");
    }
}
注解扫描和代理

对于注解驱动的依赖注入(如 @Autowired):

  1. 注解扫描:

    • Spring 的 AnnotationConfigApplicationContext 会扫描注解并生成 BeanDefinition
  2. 依赖注入:

    • 使用 依赖注入后处理器 完成注解字段的依赖解析和注入
    • 底层同样使用的是反射修改字段或调用 setter 方法。
循环依赖处理

Spring通过三级缓存解决Bean的循环依赖

  • 一级缓存:完整的单例 Bean。
  • 二级缓存:半成品 Bean(尚未完成依赖注入)。
  • 三级缓存:用于存储 Bean 的对象工厂(ObjectFactory)。

流程:

  1. 创建半成品Bean放入二级缓存
  2. 如果依赖其他Bean,会优先从一级或者二级缓存中获取,避免重复创建
  3. 依赖注入完成后,将 Bean 移入一级缓存。

Bean有几种作用域

  1. singleton:单例,每一个bean只能创建一个实例
  2. protoType:原型,每次对该bean的请求调用都会创建一个实例
  3. request:请求,针对每次HTTP请求,都会穿件一个实例
  4. session:会话,在一个Http Session中,一个bean定义对应一个bean实例
  5. global session,在一个全局Http Session中,一个bean定义对应一个bean实例

单例Bean是线程安全的吗

  1. 单例 Bean 的特点:

    • Spring 中的单例 Bean 是基于容器的实例共享模式,一个容器中只有一个 Bean 实例。
    • 该实例可以被多个线程同时访问。
  2. 并发访问的问题:

    • 如果 Bean 的方法是无状态的(只操作局部变量),则是线程安全的
    • 如果 Bean 的方法依赖类级变量(成员变量),可能在多线程访问时导致数据不一致的问题。
  3. 如何让单例 Bean 线程安全?

    • 避免使用成员变量
    • 使用sychronized或者其他同步机制
    • 使用 ThreadLocal
    • 使用单例 Bean 注入原型 Bean,从而每次请求获取新实例。

注入依赖的优势

  • 解耦:对象与依赖解耦,便于替换和扩展。
  • 可测试性:便于注入 mock 对象,提高测试效率。
  • 简化开发:开发者专注于业务逻辑,减少手动创建对象的繁琐。

面向切面编程(AOP)

AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式,旨在通过分离关注点(Separation of Concerns)来增强代码的可维护性和复用性。

在 Spring 中,AOP 是一种动态代理技术,用于在不修改源代码的情况下为程序添加横切逻辑(Cross-Cutting Concerns)。常见的横切逻辑包括日志记录、安全验证、事务管理等。

AOP 作用:

  1. 增强代码复用性

    • 把通用功能抽取成切面(Aspect),减少重复代码。
  2. 提高代码可维护性

    • 横切逻辑集中管理,便于维护和调整。
  3. 动态增强类功能

    • 无需修改现有业务逻辑,通过配置即可扩展功能。
  4. 业务解耦

    • 使业务逻辑与横切逻辑分离,提升系统的模块化。

AOP的核心概念

  1. 切面(Aspect):增强的代码块,如日志记录,事务管理
  2. 通知(Advice):

    • 前置通知:方法执行前
    • 后置通知:方法执行后
    • 返回通知:方法正确返回结果后执行
    • 异常通知:方法抛出异常时执行
    • 环绕通知:在方法执行前后都可以插入逻辑,最强大的通知类型
  3. 切点(PointCut):定义拦截规则,用于确定哪些方法/类需要应用切面逻辑。
  4. 目标对象(Target Object):被 AOP 增强的对象。
  5. 织入(Waving):

    • 将切面应用到目标对象的过程,分为以下几个阶段

      • 编译时(AspectJ)
      • 类加载时(通过ClassLoader动态增强)
      • 运行时(Spring AOP,基于动态代理)
  6. 代理(Proxy):

    • AOP 动态生成的对象,用来执行目标对象的方法,并附加增强逻辑。

AOP的用法

  1. 基于xml的配置
  2. 基于注解的配置(推荐)
  • 定义切面
@Aspect
@Component
public class LoggingAspect {
    
    @PointCut("execution(*.com.example.service.*.*(..))")
    public void logPointut(){}

    @Before("logPointcut()")
    public void beforeAdvice() {
        System.out.println("Before executing method...");
    }
    
    // 后置通知
    @After("logPointcut()")
    public void afterAdvice() {
        System.out.println("After executing method...");
    }

    // 环绕通知
    @Around("logPointcut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before around...");
        Object result = joinPoint.proceed(); // 调用目标方法
        System.out.println("After around...");
        return result;
    }
}
  • 目标对象
@Service
public class UserService {
    public void getUser() {
        System.out.println("Executing UserService.getUser()");
    }
}
  • 配置类
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = "com.example")
@EnableAspectJAutoProxy // 启用 AOP
public class AppConfig {}

AOP底层实现原理

  1. 基于动态代理

    • JDK动态代理:如果目标类实现了接口,Spring AOP 使用 java.lang.reflect.Proxy 动态生成代理类。
    • CGLIB 动态代理:如果目标类没有实现接口,Spring AOP 使用 CGLIB(Code Generation Library)动态生成子类代理。
  2. AOP流程

    • 配置解析:Spring 解析 AOP 配置并创建代理类。
    • 代理对象生成

      • 如果目标类实现了接口,则创建 JDK 动态代理
      • 否则,生成目标类的 CGLIB 子类代理
    • 方法调用拦截

      • 在代理类中,拦截对目标对象方法的调用
      • 根据通知类型,选择执行目标方法前或后进行增强逻辑。
  3. 动态代理的核心接口:

    • InvocationHandler(JDK 代理)
    • MethodInterceptor(Spring 的 AOP 底层使用)
  4. CGLIB 的工作方式:

    • 创建目标类的子类,覆盖目标类方法。
    • 在覆盖的方法中加入增强逻辑,调用 super 实现目标逻辑。

AOP适用的场景

  1. 日志
  2. 权限验证
  3. 监控
  4. 事务处理
  5. 缓存设计

声明式事务的管理

Spring声明式事务管理是通过 AOP 和 事务管理器 相结合实现的,它允许开发者通过注解或XML配置,透明地管理事务逻辑,从而简化编程流程,提高代码的清晰度和维护性。

实现方式

  1. 基于注解的声明式事务

    • 使用 @Transactional 注解标注需要事务支持的方法或类
@Transactional
public void performTransaction() {
    // 业务逻辑
}
  1. 事务配置细节

    • 事务传播行为:决定当一个事务方法调用另一个事务方法时如何传播现有事务(如 REQUIRED, REQUIRES_NEW 等)。
    • 隔离级别:控制事务对其他事务的可见性。
    • 超时时间:定义事务的最长执行时间
    • 回滚规则:指定哪些异常会触发事务回滚。

底层原理

1.AOP拦截与代理

Spring声明式事务的核心依赖于 AOP。通过 Spring AOP 拦截对事务方法的调用,并动态处理事务。

  • 代理对象生成:
    • Spring为带有 @Transactional 的类或方法创建代理对象。
    • 代理对象拦截方法调用,在调用方法前后执行事务相关操作。
  • 切面处理:
    • 在事务方法执行前,开启事务。
    • 在方法执行成功后提交事务。
    • 在方法抛出异常时回滚事务。
2.TransactionInterceptor

TransactionInterceptor 是事务逻辑的核心实现类。它通过解析 @Transactional 配置,调用事务管理器实现事务操作

3.PlatformTransactionManager

PlatformTransactionManager 是 Spring 事务管理的核心接口,具体实现由事务管理器负责(如 DataSourceTransactionManager、JpaTransactionManager)。

关键方法:

  • getTransaction(TransactionDefinition):开启事务。
  • commit(TransactionStatus):提交事务。
  • rollback(TransactionStatus):回滚事务。
4.事务上下文管理

Spring通过 TransactionSynchronizationManager 管理事务的上下文信息:

  • 当前事务的状态。
  • 绑定的数据库连接。
  • 资源清理工作。
5.@Transactional 注解解析

Spring 使用 TransactionAttributeSource 来解析 @Transactional 的元数据,并将这些信息传递给 AOP 切面。

6. 方法调用流程

以 @Transactional 方法为例:

  1. 调用方法时,代理对象拦截请求。
  2. 解析 @Transactional 注解,获取事务配置信息。
  3. 通过事务管理器开启事务。
  4. 调用目标方法:

    • 方法成功执行:提交事务。
    • 方法抛出异常:回滚事务。
  5. 释放资源,重置事务上下文。

关键点

事务传播行为

事务传播行为定义了事务方法之间如何相互影响:

  • REQUIRED(默认):如果存在事务,则加入当前事务,否则创建新事务。
  • REQUIRES_NEW:始终创建一个新事务。
  • NESTED:嵌套在当前事务中。
  • 其他:SUPPORTS、NOT_SUPPORTED、MANDATORY 等。

事务回滚规则

  • Spring 默认仅对 运行时异常(RuntimeException) 和 Error 回滚。
  • 对于受检查异常(Checked Exception),需要显式配置:

集成持久化框架(ORM)

简化的Web开发(SpringMVC)

MVC是什么?

MVC(Model-View-Controller) 是一种软件设计模式,广泛用于构建用户界面应用程序。它将应用程序分为三个核心部分:模型(Model)、视图(View)和控制器(Controller),实现业务逻辑和用户界面的分离。

MVC 模式的三个核心部分
  1. Model(模型)

    • 表示应用程序的数据和业务逻辑。
    • 负责处理与数据库或其他外部数据源的交互。
    • 在 Web 应用中,通常与实体类或数据访问对象(DAO)相关联
  2. View(视图)

    • 表示用户界面,负责展示数据。
    • 通常是 HTML 页面、模板文件或前端框架组件。
    • View 从 Model 中获取数据,通过 Controller 的控制进行显示。
  3. Controller(控制器)

    • 负责处理用户请求,调用 Model 和 View。
    • 接受用户输入,执行相应的业务逻辑,决定调用哪个 Model 和 View。
    • 在 Web 应用中,Controller 通常对应处理 HTTP 请求的方法或类。

缓存支持

异步事务处理

任务调度支持(定时任务)

微服务支持

安全框架

集成消息中间件

提供测试支持


爱跑步的猕猴桃
1 声望0 粉丝