当前位置: 首页 > news >正文

深入理解Spring IoCDI

1. 引言:为什么需要IoC和DI?

传统开发方式的耦合性问题

在传统开发中,对象通常通过 new 关键字直接创建,例如:

// 直接依赖具体实现类
UserService userService = new UserServiceImpl();
OrderService orderService = new OrderServiceImpl(userService);

这种方式存在以下问题:

  1. 紧耦合:调用方(如OrderService)直接依赖具体实现类(如UserServiceImpl)。若实现类发生变更(例如替换为NewUserServiceImpl),所有使用到的地方都需要修改代码。
  2. 难以扩展:依赖关系硬编码在代码中,无法动态替换实现(例如测试时替换为MockUserService)。
  3. 责任分散:对象的创建、依赖管理逻辑散落在各个类中,导致代码重复且维护困难。

2. 工厂模式与反射的局限性

为解决紧耦合问题,开发者曾尝试以下方案,但仍存在不足:

(1)工厂模式(Factory Pattern)
public class UserServiceFactory {public static UserService getUserService() {return new UserServiceImpl();}
}// 调用方
UserService userService = UserServiceFactory.getUserService();
  • 优点:解耦对象创建逻辑。
  • 局限性
    • 工厂类本身仍需硬编码实现类。
    • 依赖关系仍由调用方主动获取,未彻底解耦。
    • 工厂类可能膨胀为“上帝类”(管理所有对象创建)。
(2)反射(Reflection)
// 通过类名动态创建对象
Class<?> clazz = Class.forName("com.example.UserServiceImpl");
UserService userService = (UserService) clazz.newInstance();
  • 优点:实现类可通过配置动态指定。
  • 局限性
    • 代码复杂度高,类型安全无法保证(需强制转换)。
    • 配置管理繁琐(如类名硬编码在配置文件)。
    • 反射性能较差,且破坏了封装性。
依赖管理复杂性的挑战

随着系统规模扩大,类之间的依赖关系可能变得错综复杂:

// 复杂的依赖链:A依赖B,B依赖C,C依赖D...
A a = new A(new B(new C(new D(...))));
  • 依赖链冗长:手动管理依赖需要逐层实例化,容易出错。
  • 资源浪费:频繁创建重复对象(如未共享的数据库连接)。
  • 难以测试:单元测试时难以隔离依赖(如替换为Mock对象)。

Spring的解决方案:IoC和DI

Spring通过控制反转(IoC)依赖注入(DI),将对象的创建与依赖管理权交给容器,实现解耦:

  1. 控制反转(IoC)

    • 开发者不再手动 new 对象,而是由Spring容器负责实例化、配置和管理对象(Bean)。
    • 从“主动创建”变为“被动接收”,降低代码对具体实现的依赖。
  2. 依赖注入(DI)

    • 容器自动将依赖关系注入到对象中,例如:
      @Service
      public class OrderService {// 容器自动注入UserService的实现@Autowiredprivate UserService userService;
      }
      
    • 解耦:依赖通过接口或抽象类定义,而非具体实现类。
    • 可维护性:修改依赖时只需调整配置,无需改动业务代码。
    • 可测试性:轻松替换依赖(如注入Mock对象进行单元测试)。

IoC/DI的核心优势
维度传统开发模式Spring IoC/DI
对象创建调用方主动 new 对象容器创建并注入对象
耦合度紧耦合(依赖具体实现类)松耦合(依赖接口或抽象)
可维护性修改依赖需改动源码修改配置或注解即可
可测试性难以替换依赖(如 Mock 对象)轻松注入测试依赖
代码复杂度冗余的对象创建代码依赖关系声明式配置

总结
IoC和DI通过将对象的控制权交给容器,解决了传统开发中的紧耦合依赖管理混乱问题,使代码更灵活、可维护、可测试。后续章节将深入探讨其实现原理与实践方法。

二、IoC(控制反转)详解


1. 什么是控制反转?

控制反转(Inversion of Control, IoC) 是一种设计思想,其核心是将对象的创建权与依赖管理权从程序代码转移到外部容器,实现从“主动创建”到“被动接收”的转变。

传统方式 vs. Spring IoC 容器管理
场景传统方式Spring IoC
对象创建开发者通过 new 主动创建对象容器负责实例化对象,开发者通过依赖注入获取
依赖管理手动管理依赖链(如 A a = new A(new B())容器自动解析并注入依赖关系
代码耦合度紧耦合(依赖具体实现类)松耦合(依赖接口或抽象类)

代码对比示例

// 传统方式:主动创建对象(紧耦合)
public class OrderController {private OrderService orderService = new OrderServiceImpl(); 
}// Spring IoC:被动接收对象(松耦合)
public class OrderController {@Autowired  // 容器自动注入OrderService的实现private OrderService orderService;
}

2. IoC 容器的作用

Spring 的 IoC 容器是管理 Bean(对象)的核心组件,主要职责包括:

  • 对象的实例化:根据配置或注解创建 Bean。
  • 依赖注入:自动解析并注入 Bean 之间的依赖关系。
  • 生命周期管理:控制 Bean 的初始化、使用和销毁。
(1)BeanFactory:基础容器
  • 功能:提供最基础的 Bean 管理能力。
    • 加载 Bean 定义(如 XML 配置)。
    • 按需懒加载(Bean 在首次被请求时创建)。
    • 支持简单的依赖注入。
    • 特点
      • 按需懒加载(Bean 在首次请求时创建)。
      • 轻量级,适合资源受限环境。
    • 适用场景
      • 资源受限环境(如移动端)。
      • 不需要高级功能(如事件、AOP)的简单应用。
    • 使用示例
      // 通过XML配置文件初始化BeanFactory
      BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
      OrderService orderService = factory.getBean("orderService", OrderService.class);
      
    (2)ApplicationContext:扩展容器
    • 功能:在 BeanFactory 基础上扩展高级特性,是实际开发中的主流选择。
    • 特性
      • 事件发布:支持应用事件(如 ContextRefreshedEvent)。
      • 国际化:通过 MessageSource 支持多语言。
      • 资源加载:统一管理文件、图片等资源。
      • AOP 集成:无缝支持面向切面编程。
    • 常见实现类
      • AnnotationConfigApplicationContext(基于注解配置)。
      • ClassPathXmlApplicationContext(基于 XML 配置)。
    • 使用示例
      // 通过注解配置初始化ApplicationContext
      ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
      OrderService orderService = context.getBean(OrderService.class);
      

    3. 核心实现原理

    Spring IoC 容器的底层实现基于以下关键技术:

    (1)反射机制
    • 动态创建对象:通过反射(Class.newInstance()Constructor.newInstance())实例化 Bean。
    • 示例
      Class<?> clazz = Class.forName("com.example.UserServiceImpl");
      UserService userService = (UserService) clazz.getDeclaredConstructor().newInstance();
      
    • 优势:无需硬编码类名,支持灵活配置。
    • 局限:反射性能较低(可通过缓存优化)。
    (2)配置定义 Bean

    Spring 支持两种方式定义 Bean:

    XML 配置文件(传统方式)
    <!-- beans.xml -->
    <beans><bean id="userService" class="com.example.UserServiceImpl"/><bean id="orderService" class="com.example.OrderServiceImpl"><property name="userService" ref="userService"/></bean>
    </beans>
    
    • 优点
      • 适用于遗留项目或需要集中管理的场景。
      • 修改配置无需重新编译代码。
    • 缺点
      • 配置冗长,类型安全性差。
      • 维护成本高(需手动管理大量 XML 文件)。
    注解驱动(现代主流)
    • 核心注解
      • @Component:标记类为 Spring 管理的 Bean。
      • @Autowired:自动注入依赖(支持构造器、Setter、字段注入)。
      • @Configuration + @ComponentScan:声明配置类并自动扫描包。
    @Component  // 标记为Spring管理的Bean
    public class UserServiceImpl implements UserService { // ...
    }Configuration
    @ComponentScan("com.example")  // 扫描指定包下的Bean
    public class AppConfig {}@Component
    public class OrderService {@Autowired  // 自动注入UserService的实现private UserService userService;
    }
    • 优点
      • 简洁直观,类型安全。
      • 与代码紧密结合,便于维护。
    • 缺点
      • 配置分散在代码中,需结合包扫描规则。
    (3)Java Config(基于 @Bean 的配置类)
    • 特点:通过 Java 代码显式定义 Bean,替代 XML 配置。
    • 核心注解
      • @Configuration:标记类为配置类。
      • @Bean:在方法上定义 Bean,方法返回值即为 Bean 实例。
    • 示例
      @Configuration
      public class AppConfig {// 显式定义Bean@Beanpublic UserService userService() {return new UserServiceImpl();}// 依赖注入(通过方法参数)@Beanpublic OrderService orderService(UserService userService) {return new OrderServiceImpl(userService);}
      }
      
    • 优点
      • 完全代码化,类型安全,支持复杂初始化逻辑。
      • 适合与注解驱动结合使用。
    • 缺点
      • 需手动编写配置类,适合对灵活性要求高的场景。
    (4)三种配置方式对比
    维度XML 配置注解驱动Java Config
    配置形式集中式 XML 文件分散在代码中(注解)集中式 Java 类
    类型安全低(字符串类名)高(编译时检查)高(编译时检查)
    灵活性中等(适合简单依赖)高(自动扫描)高(可编程配置)
    适用场景遗留项目现代应用(主流)复杂依赖或条件化配置

    4. IoC 容器的核心流程
    1. 加载配置:读取 XML 或扫描注解,解析 Bean 的定义(BeanDefinition)。
    2. 实例化 Bean:通过反射创建 Bean 的实例。
    3. 依赖注入:根据配置自动注入 Bean 的依赖(属性赋值)。
    4. 初始化 Bean:调用 @PostConstructInitializingBean 的初始化方法。
    5. 提供 Bean:将初始化完成的 Bean 存入容器,供其他组件使用。

    5. 总结

    Spring IoC 容器通过控制反转思想,将对象的创建与依赖管理权交给容器,开发者只需关注业务逻辑。其核心实现依赖反射机制灵活的配置方式(XML 或注解),结合 BeanFactoryApplicationContext 的分层设计,XML、注解、Java Config 三种配置方式各有优劣,实际开发中常混合使用(如注解 + Java Config),注解驱动和 Java Config 是现代 Spring 应用的主流选择,兼顾简洁性和灵活性。为应用提供高效、灵活的对象管理能力。

    3. DI(依赖注入)的实现方式


    1. 什么是依赖注入?

    依赖注入(Dependency Injection, DI) 是 Spring 实现控制反转(IoC)的核心机制,其核心思想是:由容器动态地将依赖关系注入到对象中,而非对象自行创建或查找依赖。

    • 目标:解耦对象与依赖的实现,提升代码的灵活性和可维护性。
    • 关键原则:面向接口编程,而非具体实现。

    2. 三种注入方式

    Spring 支持三种依赖注入方式,各有适用场景:

    (1)构造器注入(推荐)
    • 特点:通过构造器参数注入依赖,确保对象在创建时即完成依赖初始化。
    • 优点
      • 不可变性:依赖字段可声明为 final,避免后续被修改。
      • 完整性:对象初始化后即可安全使用,无空指针风险。
    • 代码示例
      @Service
      public class OrderService {private final UserService userService;// 构造器注入(Spring 4.3+ 可省略 @Autowired)@Autowiredpublic OrderService(UserService userService) {this.userService = userService;}
      }
      
    (2)Setter 注入
    • 特点:通过 Setter 方法注入依赖,适合可选或可变的依赖。
    • 优点:灵活性高,允许动态更新依赖。
    • 缺点:依赖可能未被初始化,需处理潜在的空指针问题。
    • 代码示例
      @Service
      public class OrderService {private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}
      }
      
    (3)字段注入(不推荐)
    • 特点:直接通过字段注入依赖,无 setter 或构造函数。
    • 缺点
      • 破坏封装性,直接访问私有字段。
      • 隐藏依赖关系,难以追踪依赖来源。
      • 不利于单元测试(需通过反射注入依赖)。
    • 代码示例
      @Service
      public class OrderService {@Autowiredprivate UserService userService;
      }
      

    3. 自动装配(Autowiring)

    Spring 通过自动装配机制,根据规则自动解析并注入依赖。

    (1)@Autowired vs. @Resource
    注解来源默认行为适用场景
    @AutowiredSpring 框架按类型(byType适合明确类型匹配的依赖注入
    @ResourceJSR-250 标准按名称(byName需按名称指定 Bean 的场景
    • 示例
      // 使用 @Autowired(按类型匹配)
      @Autowired
      private UserRepository userRepository;// 使用 @Resource(按名称匹配)
      @Resource(name = "mysqlUserRepository")
      private UserRepository userRepository;
      
    (2)自动装配模式

    Spring 支持以下自动装配策略:

    1. byType:根据依赖的类型匹配 Bean(默认)。
    2. byName:根据依赖的字段名或 Setter 方法名匹配 Bean 的名称。
    3. constructor:类似于 byType,但应用于构造器参数。
    • 配置示例(XML)
      <bean id="orderService" class="com.example.OrderService" autowire="byType"/>
      

    4. 解决依赖冲突

    当存在多个同类型 Bean 时,需解决歧义性问题:

    1. @Primary
    • 标记某个 Bean 为首选注入项。
      @Bean
      @Primary
      public UserRepository jdbcUserRepository() {return new JdbcUserRepository();
      }
      
    2. @Qualifier
    • 精确指定要注入的 Bean 名称。
      @Autowired
      @Qualifier("mongoUserRepository")
      private UserRepository repository;
      


    5. 依赖注入的两种视角
    (1)基于接口的抽象编程
    • 核心思想:依赖接口而非具体实现类。
    • 优势
      • 实现类可灵活替换(如测试时注入 Mock 对象)。
      • 符合开闭原则,扩展性强。
    • 示例
      public interface PaymentService {void processPayment();
      }@Service
      public class AlipayService implements PaymentService { /*...*/ }@Service
      public class OrderService {@Autowiredprivate PaymentService paymentService; // 依赖接口
      }
      
    (2)面向配置的灵活性
    • 核心思想:通过配置(XML 或注解)管理依赖关系,而非硬编码。
    • 优势
      • 同一接口的不同实现可通过配置切换(如开发环境 vs. 生产环境)。
      • 依赖关系集中管理,便于维护。
    • 示例(Java Config)
      @Configuration
      public class AppConfig {@Bean@Profile("dev")  // 开发环境使用模拟实现public PaymentService mockPaymentService() {return new MockPaymentService();}@Bean@Profile("prod") // 生产环境使用真实实现public PaymentService alipayService() {return new AlipayService();}
      }
      

    6. 最佳实践与常见问题
    (1)推荐实践
    • 优先使用构造器注入:确保依赖不可变且完整初始化。
    • 避免滥用字段注入:仅在简单场景(如原型类)中使用。
    • 明确依赖关系:通过 @Qualifier@Primary 解决多 Bean 冲突。
    (2)循环依赖问题
    • 场景:两个 Bean 互相依赖(如 A → B → A)。
    • 解决方案
      • Setter/字段注入:Spring 通过三级缓存解决循环依赖。
      • 构造器注入无法解决循环依赖:需重构代码或使用 @Lazy 延迟加载。

    7. 总结

    依赖注入是 Spring 实现松耦合设计的核心机制,通过构造器、Setter 或字段注入,结合自动装配策略,使开发者专注于业务逻辑而非依赖管理。合理选择注入方式、理解自动装配规则,并遵循最佳实践,是构建灵活、可维护应用的关键。


    四、Spring 容器的核心机制

    Spring 容器通过 Bean 生命周期管理作用域控制条件化装配 等机制,实现对对象的创建、依赖注入及销毁的全流程管理。以下是核心机制的详细解析:


    1. Bean 的生命周期

    Spring Bean 的生命周期是 Spring 容器的核心机制之一,它定义了 Bean 从创建到销毁的完整流程。理解生命周期及其扩展点,能够帮助开发者更精准地控制 Bean 的行为(如资源管理、AOP 代理生成等)。以下是 生命周期关键阶段扩展点流程图解析


     Bean 生命周期关键阶段

    Bean 的生命周期可分为以下 5 个核心阶段:

    1. 实例化(Instantiation)
    • 触发时机:容器根据 Bean 定义(XML、Java Config 或注解)创建 Bean 的实例。
    • 方式
      • 通过构造函数直接实例化。
      • 通过工厂方法(@Bean 或静态工厂)创建。
    2. 属性赋值(Populate Properties)
    • 触发时机:实例化完成后,容器为 Bean 的属性注入依赖。
    • 方式
      • 构造器注入、Setter 注入、字段注入(通过 @Autowired@Resource)。
    3. 初始化(Initialization)
    • 触发时机:属性赋值完成后,执行初始化逻辑。
    • 核心方法
      • @PostConstruct 注解方法:优先执行,推荐使用。
      • InitializingBean 接口的 afterPropertiesSet():Spring 原生接口,侵入性强。
    • 示例
      @Component
      public class CacheManager {@PostConstructpublic void initCache() {System.out.println("初始化缓存...");}public class DatabasePool implements InitializingBean {@Overridepublic void afterPropertiesSet() {System.out.println("初始化数据库连接池...");}}
      }
      
    4. 使用(In Use)
    • Bean 处于就绪状态,可被其他组件调用。
    5. 销毁(Destruction)
    • 触发时机:容器关闭时(如调用 applicationContext.close())。
    • 核心方法
      • @PreDestroy 注解方法:优先执行,推荐使用。
      • DisposableBean 接口的 destroy():Spring 原生接口,侵入性强。
    • 示例
      @Component
      public class NetworkConnection {@PreDestroypublic void closeConnection() {System.out.println("关闭网络连接...");}
      }
      

     生命周期扩展点

    Spring 提供两类扩展接口,允许开发者干预 Bean 的创建和初始化过程:

    1. BeanPostProcessor(干预初始化过程)
    • 作用:在 Bean 初始化前后插入自定义逻辑(如生成 AOP 代理对象)。
    • 核心方法
      public interface BeanPostProcessor {// 初始化前回调(如修改 Bean 属性)default Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; }// 初始化后回调(如生成代理对象)default Object postProcessAfterInitialization(Object bean, String beanName) { return bean; }
      }
      
    • 示例:生成动态代理
      @Component
      public class CustomBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {if (bean instanceof UserService) {return Proxy.newProxyInstance(...); // 生成代理对象}return bean;}
      }
      
    2. BeanFactoryPostProcessor(修改 Bean 定义)
    • 作用:在容器加载 Bean 定义后、实例化前,动态修改 Bean 的元数据(如修改属性值)。
    • 核心方法
      public interface BeanFactoryPostProcessor {void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
      }
      
    • 示例:修改 Bean 的作用域
      @Component
      public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {BeanDefinition bd = beanFactory.getBeanDefinition("userService");bd.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE); // 修改为多例}
      }
      

     生命周期流程图

    以下是 Spring Bean 生命周期的核心步骤(以单例 Bean 为例):

    1. 实例化 Bean│↓  
    2. 填充属性(依赖注入)│↓  
    3. 执行 BeanPostProcessor 的 postProcessBeforeInitialization()│↓  
    4. 初始化:├─ 执行 @PostConstruct 方法├─ 执行 InitializingBean.afterPropertiesSet()└─ 执行自定义 init-method(如 @Bean(initMethod="..."))│↓  
    5. 执行 BeanPostProcessor 的 postProcessAfterInitialization()│↓  
    6. Bean 就绪,进入使用阶段│↓  
    7. 容器关闭时销毁:├─ 执行 @PreDestroy 方法├─ 执行 DisposableBean.destroy()└─ 执行自定义 destroy-method(如 @Bean(destroyMethod="..."))
    
    关键点
    • BeanPostProcessor 的执行顺序:影响代理对象的生成时机。
    • 初始化方法的优先级@PostConstruct > InitializingBean > init-method
    • 销毁方法的优先级@PreDestroy > DisposableBean > destroy-method

     最佳实践与注意事项
    1. 推荐使用注解方式:优先使用 @PostConstruct@PreDestroy,避免与 Spring 接口耦合。
    2. 谨慎使用 BeanPostProcessor
      • 确保逻辑轻量,避免性能瓶颈。
      • 注意处理所有 Bean 类型,或通过条件判断限制目标 Bean。
    3. 作用域对生命周期的影响
      • Singleton Bean:完整经历生命周期(初始化一次,销毁一次)。
      • Prototype Bean:仅经历实例化、属性填充和初始化阶段,销毁需手动触发。
    4. 避免循环依赖:构造函数注入可能导致 Bean 无法完成实例化阶段。
    • public interface BeanPostProcessor {Object postProcessBeforeInitialization(Object bean, String beanName);Object postProcessAfterInitialization(Object bean, String beanName);
      }
      

    2. Bean 的作用域

    Spring 支持多种作用域,控制 Bean 的创建范围和生命周期:

    作用域描述适用场景
    Singleton默认作用域,容器中仅存在一个 Bean 实例(单例)。无状态服务(如工具类、配置类)
    Prototype每次请求(getBean() 或注入)都创建一个新实例。有状态对象(如用户会话)
    Request每个 HTTP 请求创建一个实例(仅 Web 环境)。HTTP 请求相关的数据(如表单数据)
    Session每个用户会话创建一个实例(仅 Web 环境)。用户登录状态、购物车
    WebSocket每个 WebSocket 会话创建一个实例(仅 WebSocket 环境)。实时通信场景
    示例:定义多例 Bean
    @Component
    @Scope("prototype")  // 或 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class PaymentService {// 每次注入都会生成新实例
    }
    

    3. 条件化装配(@Conditional)

    通过条件判断动态决定是否注册 Bean,常用于环境适配(如开发/生产环境配置)。

    使用步骤
    1. 实现 Condition 接口:定义匹配规则。
    2. 通过 @Conditional 注解标记 Bean:指定条件类。
    示例:根据环境注册数据源
    // 1. 定义条件类(检查是否启用 MySQL)
    public class MySQLCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {String dbType = context.getEnvironment().getProperty("app.datasource.type");return "mysql".equalsIgnoreCase(dbType);}
    }// 2. 条件化注册 Bean
    @Configuration
    public class DataSourceConfig {@Bean@Conditional(MySQLCondition.class)public DataSource mysqlDataSource() {return new MySQLDataSource();}@Bean@Conditional(MongoDBCondition.class)public DataSource mongoDataSource() {return new MongoDBDataSource();}
    }
    

    4. 核心机制总结

    机制核心要点
    Bean 生命周期实例化 → 属性填充 → 初始化(@PostConstruct) → 使用 → 销毁(@PreDestroy)。
    BeanPostProcessor干预 Bean 初始化过程(如生成 AOP 代理对象)。
    作用域根据业务需求选择合适的生命周期范围(单例、多例、请求级等)。
    条件化装配动态控制 Bean 的注册,适配不同环境或配置。

    5. 最佳实践

    1. 理解生命周期:避免在构造函数中依赖未初始化的资源。
    2. 合理选择作用域
      • 单例:无状态服务,减少内存开销。
      • 多例:有状态对象,避免线程安全问题。
    3. 灵活使用条件装配:简化环境配置(如 @Profile 底层基于 @Conditional)。
    4. 慎用 BeanPostProcessor:过度使用会增加复杂度,优先使用标准生命周期回调。

    掌握 Spring 容器的核心机制,能够更高效地设计灵活、可维护的应用程序架构。

    5. 高级特性与配置

    Spring 框架提供了一系列高级特性,帮助开发者实现更灵活、模块化的应用架构。以下是 作用域控制条件化注册环境隔离配置方式 的详细解析:


    一、Bean 的作用域(Scope)

    Spring 通过作用域控制 Bean 的创建范围和生命周期,默认支持以下作用域:

    1. 内置作用域
    作用域描述适用场景
    Singleton容器中仅存在一个 Bean 实例(默认作用域)。无状态服务(如工具类、配置类)
    Prototype每次请求(getBean() 或注入)都创建一个新实例。有状态对象(如用户会话、计数器)
    Request每个 HTTP 请求创建一个实例(仅 Web 环境)。HTTP 请求相关的数据(如表单数据)
    Session每个用户会话创建一个实例(仅 Web 环境)。用户登录状态、购物车
    Application整个 Web 应用共享一个实例(类似 Singleton,但上下文为 ServletContext)。全局配置对象
    WebSocket每个 WebSocket 会话创建一个实例(仅 WebSocket 环境)。实时通信场景
    示例:定义 Prototype 作用域
    @Bean  
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  // 或 @Scope("prototype")  
    public class TaskProcessor {  // 每次注入都会生成新实例  
    }  
    
    2. 自定义作用域

    若内置作用域无法满足需求,可自定义作用域:

    1. 实现 Scope 接口:定义作用域的行为(如线程级作用域)。
    2. 注册到容器:通过 ConfigurableBeanFactory 注册作用域。

    示例:自定义线程级作用域

    public class ThreadScope implements Scope {  private final ThreadLocal<Map<String, Object>> threadLocal = ThreadLocal.withInitial(HashMap::new);  @Override  public Object get(String name, ObjectFactory<?> objectFactory) {  Map<String, Object> scope = threadLocal.get();  return scope.computeIfAbsent(name, k -> objectFactory.getObject());  }  @Override  public void registerDestructionCallback(String name, Runnable callback) {  // 线程结束时清理资源  }  // 其他方法省略...  
    }  // 注册自定义作用域  
    @Configuration  
    public class AppConfig {  @Bean  public static BeanFactoryPostProcessor beanFactoryPostProcessor() {  return factory -> factory.registerScope("thread", new ThreadScope());  }  
    }  // 使用自定义作用域  
    @Bean  
    @Scope("thread")  
    public class UserContext {  // 每个线程独立实例  
    }  
    

    二、条件化 Bean 注册

    通过条件判断动态决定是否注册 Bean,适配不同环境或配置。

    1. @Conditional 注解
    • 核心机制:实现 Condition 接口,定义匹配逻辑。
    • 示例:根据环境变量注册数据源
      public class MySQLCondition implements Condition {  @Override  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {  String dbType = context.getEnvironment().getProperty("app.datasource.type");  return "mysql".equalsIgnoreCase(dbType);  }  
      }  @Configuration  
      public class DataSourceConfig {  @Bean  @Conditional(MySQLCondition.class)  public DataSource mysqlDataSource() {  return new MySQLDataSource();  }  
      }  
      
    2. Spring Boot 的派生注解

    Spring Boot 提供了更简洁的条件注解:

    • @ConditionalOnProperty:根据配置属性判断。
    • @ConditionalOnClass:类路径存在指定类时生效。
    • @ConditionalOnMissingBean:容器中不存在指定 Bean 时生效。

    示例

    @Bean  
    @ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")  
    public CacheManager cacheManager() {  return new RedisCacheManager();  
    }  
    

    三、Profile 环境隔离

    通过 @Profile 注解隔离不同环境的配置(如开发、测试、生产)。

    1. 定义 Profile 专属 Bean
    @Configuration  
    @Profile("dev")  
    public class DevConfig {  @Bean  public DataSource devDataSource() {  return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();  }  
    }  @Configuration  
    @Profile("prod")  
    public class ProdConfig {  @Bean  public DataSource prodDataSource() {  return new MySQLDataSource();  }  
    }  
    
    2. 激活 Profile
    • 配置文件
      spring.profiles.active=dev  
      
    • 启动参数
      java -jar app.jar --spring.profiles.active=dev,debug  
      

    四、基于 Java 的配置

    使用 @Configuration@Bean 替代 XML 配置,提供类型安全的配置方式。

    1. 声明配置类
    @Configuration  
    public class AppConfig {  @Bean  public UserService userService(UserRepository repository) {  return new UserService(repository);  }  @Bean  public UserRepository userRepository() {  return new JdbcUserRepository();  }  
    }  
    
    2. 组合配置类

    通过 @Import 整合多个配置类:

    @Configuration  
    @Import({DataSourceConfig.class, SecurityConfig.class})  
    public class MainConfig {  // 主配置类整合子配置  
    }  
    
    3. 混合 XML 配置

    若需兼容旧项目,可混合使用 Java 和 XML 配置:

    @Configuration  
    @ImportResource("classpath:legacy-config.xml")  
    public class HybridConfig {  // 组合 Java 与 XML 配置  
    }  
    

    五、最佳实践总结

    特性使用建议
    作用域优先使用 Singleton,有状态场景用 Prototype,避免滥用自定义作用域。
    条件化注册使用 @Conditional 或 Spring Boot 派生注解,简化环境适配逻辑。
    Profile严格隔离环境配置,避免生产环境误用开发配置。
    Java 配置优先使用 @Configuration,替代 XML 配置,提升可维护性。
    组合配置通过 @Import 分模块管理配置,避免单个配置类臃肿。

    六、总结

    掌握 Spring 的高级特性与配置技巧,能够显著提升应用的灵活性和可维护性:

    • 作用域控制:精准管理 Bean 的生命周期和状态。
    • 条件化注册:动态适配不同环境需求。
    • Profile 隔离:确保环境配置的安全性。
    • Java 配置:构建类型安全、模块化的配置体系。

    6. 实际应用场景

    Spring 的核心特性(IoC/DI、自动化配置)在实际开发中广泛应用,尤其在分层架构设计、第三方框架整合及快速启动项目中体现其价值。以下是典型场景的详细解析与实现示例:


    一、分层架构中的 IoC/DI

    Spring 的依赖注入天然支持分层架构,典型的三层结构如下:

    1. 分层结构与依赖关系
    ┌───────────────┐       ┌───────────────┐       ┌───────────────┐  
    │   Controller  │──────>│    Service    │──────>│     DAO       │  
    └───────────────┘       └───────────────┘       └───────────────┘  (Web层)               (业务层)              (数据访问层)  
    
    2. 各层注解与依赖注入
    • Controller 层:使用 @Controller@RestController,接收 HTTP 请求并调用 Service。
    • Service 层:使用 @Service,封装业务逻辑并调用 DAO。
    • DAO 层:使用 @Repository,负责数据库操作(Spring 会为 @Repository 注解的类自动启用异常转换,将数据访问异常转换为 Spring 统一异常体系)。

    示例代码

    // DAO 层(数据访问)  
    @Repository  
    public class UserDaoImpl implements UserDao {  @Autowired  private JdbcTemplate jdbcTemplate;  public User findById(Long id) {  return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?", User.class, id);  }  
    }  // Service 层(业务逻辑)  
    @Service  
    public class UserServiceImpl implements UserService {  private final UserDao userDao;  @Autowired  // 构造器注入(推荐)  public UserServiceImpl(UserDao userDao) {  this.userDao = userDao;  }  public User getUserById(Long id) {  return userDao.findById(id);  }  
    }  // Controller 层(HTTP接口)  
    @RestController  
    @RequestMapping("/users")  
    public class UserController {  private final UserService userService;  @Autowired  public UserController(UserService userService) {  this.userService = userService;  }  @GetMapping("/{id}")  public User getUser(@PathVariable Long id) {  return userService.getUserById(id);  }  
    }  
    

    二、与第三方框架整合(以 MyBatis 为例)

    Spring 通过 SqlSessionFactoryBean 等辅助类,无缝整合 MyBatis 实现 ORM 功能。

    1. 核心配置步骤
    1. 定义数据源:配置数据库连接池(如 HikariCP)。
    2. 配置 SqlSessionFactoryBean:指定 MyBatis 的 Mapper 文件位置、别名等。
    3. 启用 Mapper 接口扫描:通过 @MapperScan 自动注册 DAO 接口。
    2. 示例配置类
    @Configuration  
    @MapperScan("com.example.mapper")  // 扫描 MyBatis Mapper 接口  
    public class MyBatisConfig {  @Bean  public DataSource dataSource() {  HikariDataSource ds = new HikariDataSource();  ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");  ds.setUsername("root");  ds.setPassword("123456");  return ds;  }  @Bean  public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception {  SqlSessionFactoryBean factory = new SqlSessionFactoryBean();  factory.setDataSource(dataSource);  factory.setTypeAliasesPackage("com.example.entity");  // 实体类包别名  factory.setMapperLocations(new PathMatchingResourcePatternResolver()  .getResources("classpath:mapper/*.xml"));  // Mapper XML 文件位置  return factory;  }  @Bean  public PlatformTransactionManager transactionManager(DataSource dataSource) {  return new DataSourceTransactionManager(dataSource);  // 事务管理  }  
    }  // Mapper 接口(无需实现类)  
    @Mapper  
    public interface UserMapper {  @Select("SELECT * FROM users WHERE id = #{id}")  User findById(Long id);  
    }  
    

    三、基于 Spring Boot 的自动化配置

    Spring Boot 通过 @EnableAutoConfiguration 实现“约定优于配置”,大幅简化项目搭建流程。

    1. 核心机制
    • @SpringBootApplication 注解:组合了 @Configuration@ComponentScan@EnableAutoConfiguration
    • 自动配置原理
      1. 根据类路径中的依赖(如 spring-boot-starter-data-jpa)自动配置 Bean。
      2. 通过 spring.factories 文件加载 AutoConfiguration 类(条件化注册 Bean)。
    2. 示例:Spring Boot 启动类
    @SpringBootApplication  // 等价于 @Configuration + @ComponentScan + @EnableAutoConfiguration  
    public class Application {  public static void main(String[] args) {  SpringApplication.run(Application.class, args);  }  
    }  
    
    3. 自定义配置覆盖默认行为

    若需覆盖 Spring Boot 的自动配置,只需显式定义同名 Bean。

    示例:自定义数据源

    @Configuration  
    public class DataSourceConfig {  @Bean  @ConfigurationProperties(prefix = "app.datasource")  // 从配置文件中读取参数  public DataSource customDataSource() {  return new HikariDataSource();  }  
    }  
    

    四、最佳实践总结

    场景关键实践
    分层架构严格遵循分层职责,Controller 仅处理 HTTP 交互,Service 封装业务逻辑。
    第三方框架整合优先使用 Spring 官方提供的整合模块(如 mybatis-spring-boot-starter)。
    Spring Boot 配置通过 application.propertiesapplication.yml 管理环境相关配置。
    自动化配置理解 spring-boot-autoconfigure 原理,避免重复造轮子。

    五、总结

    Spring 在实际开发中的应用价值体现在:

    • 分层解耦:通过 IoC/DI 实现各层职责分离,提升代码可维护性。
    • 高效整合:简化第三方框架(如 MyBatis、Hibernate)的接入成本。
    • 快速启动:Spring Boot 的自动化配置大幅减少样板代码,聚焦核心业务逻辑

      7. 常见问题与解决方案

      Spring 开发中常因配置或设计问题引发异常,以下是高频问题的 根因分析解决方案


      一、循环依赖问题

      1. Spring 三级缓存解决循环依赖的原理

      Spring 通过 三级缓存 解决单例 Bean 的循环依赖问题,缓存结构如下:

      缓存级别存储内容作用
      一级缓存(单例池)完全初始化好的 Bean(singletonObjects直接提供已就绪的 Bean
      二级缓存早期暴露的 Bean(earlySingletonObjects存放半成品 Bean(属性未填充)
      三级缓存Bean 的工厂对象(singletonFactories生成 Bean 的早期引用(解决代理对象问题)

      流程示例(A → B → A):

      1. 实例化 A(未填充属性),将 A 的工厂对象存入三级缓存。
      2. 填充 A 的依赖 B,触发 B 的实例化。
      3. 实例化 B(未填充属性),尝试填充依赖 A,从三级缓存获取 A 的早期引用。
      4. B 完成初始化,存入一级缓存。
      5. A 继续填充属性 B,完成初始化后存入一级缓存。
      2. 构造器注入导致的循环依赖
      • 问题:构造器注入需在实例化阶段完成依赖注入,此时 Bean 未放入三级缓存,无法解决循环依赖。
      • 解决方案
        1. 重构代码:检查是否存在不必要的循环依赖,优化设计。
        2. 改用 Setter/字段注入:延迟依赖注入到属性填充阶段。
        3. 使用 @Lazy 延迟加载
          @Service  
          public class ServiceA {  private final ServiceB serviceB;  // 延迟注入 ServiceB  public ServiceA(@Lazy ServiceB serviceB) {  this.serviceB = serviceB;  }  
          }  
          

      二、Bean 配置冲突

      当存在多个同类型 Bean 时,Spring 抛出 NoUniqueBeanDefinitionException

      1. @Primary@Qualifier 使用场景
      • @Primary:标记为首选 Bean,自动注入时优先选择。
        @Bean  
        @Primary  
        public DataSource mysqlDataSource() {  return new MySQLDataSource();  
        }  
        
      • @Qualifier:精确指定 Bean 名称注入。
        @Autowired  
        @Qualifier("oracleDataSource")  
        private DataSource dataSource;  
        
      2. 选择策略
      • @Primary:适用于定义全局默认 Bean。
      • @Qualifier:需明确指定某特定 Bean 时使用。

      三、Bean 未被容器管理的原因

      1. 包路径未扫描
      • 根因@ComponentScan 未覆盖 Bean 所在包。
      • 解决
        @SpringBootApplication  
        @ComponentScan(basePackages = "com.example")  
        public class Application { ... }  
        
      2. 缺少注解
      • 根因:Bean 类未标记 @Component@Service 等注解,或配置类中未定义 @Bean
      • 解决
        @Service  // 添加注解  
        public class UserService { ... }  
        
      3. 作用域配置错误
      • 根因:Prototype Bean 未被正确获取(如直接通过 new 创建实例)。
      • 解决:通过容器获取 Bean:
        @Autowired  
        private ApplicationContext context;  public void process() {  UserService userService = context.getBean(UserService.class);  
        }  
        

      四、其他高频问题

      1. 事务失效问题
      • 根因:非代理对象调用事务方法(如类内部方法调用)、异常未抛出等。
      • 解决
        • 确保事务方法为 public
        • 使用 @Transactional(rollbackFor = Exception.class)
      2. AOP 不生效
      • 根因:切面类未标记 @Aspect@Component,或切入点表达式错误。
      • 解决
        @Aspect  
        @Component  
        public class LogAspect {  @Before("execution(* com.example.service.*.*(..))")  public void logBefore(JoinPoint joinPoint) { ... }  
        }  
        

      五、总结

      问题类型关键解决思路
      循环依赖优先使用 Setter/字段注入,避免构造器注入循环,必要时重构代码。
      Bean 冲突合理使用 @Primary@Qualifier 明确依赖关系。
      Bean 未被管理检查包扫描路径、注解完整性及作用域配置。
      事务与 AOP 失效确保代理机制生效(如避免内部调用)、正确配置切面与事务注解。

      通过理解 Spring 底层机制(如三级缓存、代理模式)和遵循最佳实践,可高效解决大多数常见问题。

      8. 总结与最佳实践

      Spring 的 IoC(控制反转)和 DI(依赖注入)机制是现代 Java 应用开发的基石,其核心价值在于提升代码的模块化、可维护性和灵活性。以下是核心总结与实践指南:


      一、IoC 与 DI 的核心优势

      优势说明
      代码解耦通过依赖注入消除类之间的硬编码依赖,实现模块间松耦合。
      可测试性依赖可替换(如 Mock 对象),便于单元测试和集成测试。
      配置灵活性通过 XML、Java Config 或注解动态管理依赖关系,适配不同环境需求。

      二、推荐实践

      1. 优先使用构造器注入
      • 理由
        • 强制依赖明确:确保必需的依赖在实例化时已就绪,避免 NullPointerException
        • 不可变性:结合 final 关键字,保证 Bean 的线程安全性和状态一致性。
      • 示例
        @Service  
        public class OrderService {  private final PaymentService paymentService;  private final InventoryService inventoryService;  // 构造器注入  public OrderService(PaymentService paymentService, InventoryService inventoryService) {  this.paymentService = paymentService;  this.inventoryService = inventoryService;  }  
        }  
        
      2. 避免过度依赖字段注入
      • 问题
        • 隐藏依赖关系,增加代码维护成本。
        • 破坏封装性,难以通过构造函数追踪依赖来源。
      • 替代方案:仅在原型代码或框架限制时使用字段注入,生产代码优先选择构造器或 Setter 注入。
      3. 合理划分模块与配置类
      • 模块化设计
        • 按功能分包:例如 com.example.usercom.example.order
        • 分模块配置:使用 @Configuration 类管理相关 Bean,通过 @Import 组合配置。
      • 示例
        @Configuration  
        @Import({SecurityConfig.class, DataSourceConfig.class})  
        public class AppConfig {  // 主配置类整合子模块配置  
        }  
        

      三、其他关键实践

      1. 避免循环依赖

        • 优先通过设计重构消除循环依赖,而非依赖 Spring 的三级缓存机制。
        • 若必须存在循环依赖,使用 Setter 注入替代构造器注入。
      2. 合理选择作用域

        • Singleton:无状态服务(如工具类)。
        • Prototype:有状态对象(如用户会话)。
      3. 条件化配置

        • 使用 @Profile 隔离环境配置(开发、测试、生产)。
        • 通过 @Conditional 实现动态 Bean 注册(如根据类路径加载驱动)。
      4. 日志与监控

        • 结合 Spring AOP 实现统一的日志切面、性能监控或事务管理。

      附录

      1. 代码示例仓库
      • GitHub 示例
        Spring官方示例仓库
        Spring Boot实战项目模板
      2. 官方文档推荐
      • Spring Framework
        Spring Framework 6.x 官方文档
      • Spring Boot
        Spring Boot 3.x 官方文档
      3. 开发工具
      工具功能
      Spring Tools Suite基于 Eclipse 的 Spring 专用 IDE,提供 Bean 可视化、实时配置校验等功能。
      IntelliJ IDEA内置 Spring 插件,支持依赖注入分析、Bean 导航、Profile 快速切换。
      Spring Initializr快速生成 Spring Boot 项目骨架(访问地址)。

      四、总结

      Spring 的核心设计哲学是通过 约定优于配置模块化 降低开发复杂度。遵循以下原则可最大化框架价值:

      1. 明确依赖:优先使用构造器注入,确保依赖关系透明。
      2. 环境隔离:通过 Profile 和条件化配置避免环境差异导致的问题。
      3. 持续优化:定期重构代码,消除不必要的耦合和冗余配置。

       

      结语

      Spring 框架以其优雅的设计理念和强大的功能,成为构建企业级 Java 应用的行业标准。通过 控制反转(IoC)依赖注入(DI),Spring 成功解耦了组件间的依赖关系,使代码更具模块化和可维护性;通过灵活的 生命周期管理条件化配置,开发者能够轻松应对复杂多变的业务需求;而 分层架构第三方框架的无缝整合,则进一步拓展了其应用边界。

      Spring 的核心价值
      1. 简化开发:从 XML 配置到注解驱动,再到 Spring Boot 的自动化配置,Spring 始终致力于减少样板代码,让开发者聚焦核心逻辑。
      2. 生态繁荣:Spring 家族(Spring Boot、Spring Cloud、Spring Data 等)覆盖了微服务、数据访问、安全、消息队列等全场景,形成完整的开发生态。
      3. 与时俱进:持续拥抱新特性(如响应式编程、GraalVM 原生镜像支持),保持技术生命力。
      给开发者的建议
      • 深入理解原理:掌握 Bean 生命周期、循环依赖解决机制、AOP 代理等底层逻辑,避免仅停留在“会用”层面。
      • 实践驱动成长:通过实际项目巩固知识,尝试从零搭建 Spring 应用,逐步引入高级特性(如自定义作用域、条件化配置)。
      • 参与社区:关注 Spring 官方博客、GitHub 仓库及技术峰会,了解最新动态与最佳实践。
      最后的思考

      技术框架的本质是工具,而工具的价值在于解决问题。Spring 的成功不仅源于其功能强大,更在于它传递了一种设计哲学——通过松耦合、模块化和约定优于配置,构建高内聚、低耦合的系统。希望本系列内容能为你打开 Spring 世界的大门,助你在实践中不断探索,用代码创造更大价值。

      大道至简,匠心不息。共勉!


      扩展阅读

      • 《Spring 实战(第6版)》:系统学习 Spring 核心特性与实战技巧。
      • Spring 官方博客:获取最新版本特性解读与案例分享。
      • Baeldung 教程:涵盖 Spring 及 Java 生态的深度技术文章。

      愿你在 Spring 的生态中,找到属于你的技术星辰大海! 🌟

       

      相关文章:

      深入理解Spring IoCDI

      1. 引言&#xff1a;为什么需要IoC和DI&#xff1f; 传统开发方式的耦合性问题 在传统开发中&#xff0c;对象通常通过 new 关键字直接创建&#xff0c;例如&#xff1a; // 直接依赖具体实现类 UserService userService new UserServiceImpl(); OrderService orderService…...

      NO.78十六届蓝桥杯备战|数据结构-并查集|双亲表示法|初始化|查询|合并|判断|亲戚|Lake Counting|程序自动分析(C++)

      双亲表⽰法 接下来要学习到的并查集&#xff0c;本质上就是⽤双亲表⽰法实现的森林。因此&#xff0c;我们先认识⼀下双亲表⽰法。 在学习树这个数据结构的时&#xff0c;讲到树的存储⽅式有很多种&#xff1a;孩⼦表⽰法&#xff0c;双亲表⽰法、孩⼦双亲表⽰法以及孩⼦兄弟表…...

      20250407-组件v-model

      基本用法 v-model 可以在组件上使用以实现双向绑定。 首先看下 v-model 在原生元素上的用法&#xff1a; <input v-model"searchText" /> 在代码背后&#xff0c;模板编译器会对 v-model 进行更冗长的等价展开。因此上面的代码其实等价于下面这段&#xff…...

      oracle 存储体系结构

      oracle 存储体系结构 参考&#xff1a; Logical Storage Structures (oracle.com)...

      晋城市电子健康证上传照片尺寸要求及手机拍照制作方法

      晋城市餐饮从业人员健康证电子照片上传有着明确的技术规范。根据"晋城市从业人员电子健康证明服务平台"要求&#xff0c;照片尺寸应为358像素&#xff08;宽&#xff09;441像素&#xff08;高&#xff09;&#xff0c;这一比例符合标准证件照的规格。照片底色可选择…...

      STL c++ list——模拟实现

      结点类的模拟实现 list是一个带头双向循环链表 因需要实现一个节点类&#xff0c;其中包含哨兵位&#xff08;用来标识位置&#xff09;&#xff0c;节点信息&#xff08;val数据&#xff0c;prev后指针&#xff0c;next后指针&#xff09; template<class T> struct …...

      【ES系列】Elasticsearch从入门到精通保姆级教程 | 启篇

      🔥 本系列将带你从零开始学习Elasticsearch,通过保姆级教程,手把手教你掌握这个强大的搜索与分析引擎。无论你是完全的新手,还是想系统学习ES的开发者,这个系列都能满足你的需求。 📚博主匠心之作,强推专栏: JAVA集合专栏 【夜话集】JVM知识专栏数据库sql理论与实战【…...

      图解Java运行机制-JVM、JRE、JDK区别

      以下是Java运行机制及JVM、JRE、JDK区别的图解与说明&#xff1a; --- ### 一、Java程序运行机制 1. **编写与编译** Java源文件&#xff08;.java&#xff09;通过**JDK中的编译器&#xff08;javac&#xff09;**编译为字节码文件&#xff08;.class&#xff09;&#xff…...

      UML类图综合实验三补档

      1.使用简单工厂模式模拟女娲(Nvwa)造人(Person)&#xff0c;如果传入参数“M”&#xff0c;则返回一个Man对象&#xff0c;如果传入参数“W”&#xff0c;则返回一个Woman对象&#xff0c;用Java语言实现该场景。现需要增加一个新的Robot类&#xff0c;如果传入参数“R”&#…...

      OpenHarmony子系统开发 - DFX(八)

      OpenHarmony子系统开发 - DFX&#xff08;八&#xff09; 八、Faultlogger开发指导 概述 功能简介 Faultlogger是OpenHarmony为开发者提供的一个维测日志框架&#xff0c;能够为应用、元能力、系统服务进程崩溃故障提供统一检测、日志采集、日志存储、日志上报功能&#xf…...

      C# virtual 和 abstract 详解

      简介 在 C# 中&#xff0c;virtual 和 abstract 关键字都用于面向对象编程中的继承和多态&#xff0c;它们主要用于方法、属性和事件的定义&#xff0c;但在用法上存在一些重要的区别。 virtual 关键字 virtual 表示可重写的方法&#xff0c;但可以提供默认实现&#xff0c;…...

      红宝书第三十二讲:零基础学会模块打包器:Webpack、Parcel、Rollup

      红宝书第三十二讲&#xff1a;零基础学会模块打包器&#xff1a;Webpack、Parcel、Rollup 资料取自《JavaScript高级程序设计&#xff08;第5版&#xff09;》。 查看总目录&#xff1a;红宝书学习大纲 一、模块打包器是什么&#xff1f; 把分散的HTML/CSS/JS文件 组合成浏览…...

      DeepSeek 在金融领域的应用解决方案

      DeepSeek 在金融领域的应用解决方案 一、背景 随着人工智能技术的快速发展&#xff0c;DeepSeek 作为一款国产大模型&#xff0c;凭借其强大的语义理解、逻辑推理和多模态处理能力&#xff0c;在金融行业迅速崭露头角。金融行业作为经济的核心&#xff0c;面临着激烈的市场竞…...

      linux 处理2个文件的差集

      命令 grep -Fvxf 文件1 文件2 -F 将模式视为固定字符串&#xff0c;而非正则表达式。 -v 反向匹配&#xff0c;输出不匹配的行。 -x 精确匹配整行&#xff0c;避免部分匹配。 -f 文件1 从文件1中读取模式。 示例 执行命令 grep -Fvxf a1.txt a2.txt...

      vue3中pinia基本使用

      一、安装以及引入 安装&#xff1a;npm install piniamain.js文件&#xff1a; import { createApp } from "vue"; import { createPinia } from "pinia"; import App from "./App.vue";const pinia createPinia() const app createApp(App)…...

      “乐企“平台如何重构业财税票全流程生态?

      2025年&#xff0c;国家税务总局持续推进的"便民办税春风行动"再次推进数字化服务升级&#xff0c;其中"乐企"平台作为税务信息化的重要载体&#xff0c;持续优化数电票服务能力&#xff0c;为企业提供更高效、更规范的税务管理支持。在这一背景下&#xf…...

      JVM内存模型

      JVM内存模型 JVM&#xff08;Java Virtual Machine&#xff09;内存模型是 Java 程序在运行时&#xff0c;JVM 为其分配的内存结构&#xff0c;它定义了 Java 程序如何在内存中存储数据和如何进行线程之间的通信。JVM 内存模型是为了支持高效的多线程执行和垃圾回收机制。 一…...

      LeetCode热题100记录-【二分查找】

      二分查找 35.搜索插入位置 思考&#xff1a;二分查找先判定边界条件 记录&#xff1a;不需要二刷 class Solution {public int searchInsert(int[] nums, int target) {int left 0,right nums.length-1;if(nums[right] < target){return right1;}if(nums[left] > tar…...

      科普:原始数据是特征向量么?

      一、输入向量 x \mathbf{x} x是特征向量 机器学习算法公式中的输入向量 x \mathbf{x} x通常要求是特征向量。原因如下&#xff1a; 从算法原理角度&#xff1a;机器学习算法旨在通过对输入数据的学习来建立模型&#xff0c;以实现对未知数据的预测或分类等任务。特征向量是对…...

      echarts地图添加涟漪波纹点位

      1.完整代码 chartsOption: {tooltip: {trigger: "item",formatter: this.initTooltip,triggerOn: "mousemove",borderColor: "#fff",backgroundColor: "rgba(216, 227, 244, 1)",extraCssText: "border-radius: 14px;", //…...

      Linux(十三)fork + exec进程创建

      一、进程创建 在了解进程创建的步骤前&#xff0c;让我们先通过实例观察一下。大家可以跟小编一起&#xff0c;在终端中执行3次ps -f命令&#xff0c;观察一下。 通过上图&#xff0c;我们可以发现&#xff0c;3次ps -f的父进程&#xff08;PPID&#xff09;都是一样的&#xf…...

      集合计算高级函数

      说明 过滤 遍历一个集合并从中获取满足指定条件的元素组成一个新的集合转化/映射&#xff08;map&#xff09;将集合中的每一个元素映射到某一个函数扁平化 扁平化映射 注&#xff1a;flatMap 相当于先进行 map 操作&#xff0c;在进行 flatten 操作集合中的每个元素的子元素映…...

      鼎讯信通 便携式雷达信号干扰模拟器:打造实战化电磁环境的新利器

      在现代战争中&#xff0c;电磁环境的复杂性直接影响着雷达装备的性能和作战效果。面对敌方日益精进的电子战手段&#xff0c;如何提升雷达设备的抗干扰能力&#xff0c;确保其在实战环境中的稳定性和可靠性&#xff0c;已成为各国军队和科研机构的重要课题。 为此&#xff0c;…...

      避开养生误区,拥抱健康生活

      在追求健康的道路上&#xff0c;我们常常会陷入一些养生误区&#xff0c;不仅无法达到预期效果&#xff0c;还可能损害身体健康。只有拨云见日&#xff0c;认清这些误区&#xff0c;采取正确的养生方式&#xff0c;才能真正拥抱健康生活。​ 很多人认为&#xff0c;保健品吃得…...

      解码ChatBI技术形态:独立对话框、插件式与IM集成模式的技术优劣

      ChatBI的形态之争 随着大语言模型&#xff08;LLM&#xff09;技术的成熟&#xff0c;**对话式商业智能&#xff08;ChatBI&#xff09;**正成为企业数据分析的新范式。然而&#xff0c;不同的技术形态直接影响ChatBI的落地效果——独立对话框、插件式助手、IM集成机器人&…...

      rockylinux 8 9 升级到指定版本

      rockylinux 8 update 指定版本 rockylinux 历史版 所有版本rockylinux 最新版 所有版本vault历史版 pub最新版(https://dl.rockylinux.org)地址后面增加不同名称 echo "delete repos" rm -rf /etc/yum.repos.d/*echo "new rockylinux repo" cat <<EO…...

      一文详解OpenCV环境搭建:Ubuntu20.4使用CLion配置OpenCV开发环境

      在计算机视觉和图像处理领域&#xff0c;OpenCV 是一个不可或缺的工具。其为开发者提供了一系列广泛的算法和实用工具&#xff0c;支持多种编程语言&#xff0c;并且可以在多个平台上运行。对于希望在其项目中集成先进视觉功能的开发者来说&#xff0c;掌握如何配置和使用OpenC…...

      Android Coli 3 ImageView load two suit Bitmap thumb and formal,Kotlin(四)

      Android Coli 3 ImageView load two suit Bitmap thumb and formal&#xff0c;Kotlin&#xff08;四&#xff09; 对 Android Coli 3 ImageView load two suit Bitmap thumb and formal&#xff0c;Kotlin&#xff08;三&#xff09;-CSDN博客 进行完善&#xff0c;注意完善 …...

      01-JVM 内存模型与 GC 原理

      JVM 内存模型与 GC 原理解析 本文将从 JVM 内存模型入手&#xff0c;深入剖析各个区域的作用、GC 的运行机制与常见算法&#xff0c;并结合源码与面试思维&#xff0c;带你掌握 JVM 的底层世界。 一、JVM 内存模型&#xff08;Java Memory Model&#xff09; JVM 将内存划分为…...

      Docker--Docker镜像制作的注意事项

      Docker 镜像制作 dockerfiles的指令讲解 链接 CMD和ENTRYPOINT CMD 和 ENTRYPOINT 是 Dockerfile 中用于指定容器启动时运行命令的两个指令。它们在功能上有一些相似之处&#xff0c;但也存在重要区别。 在编辑Dockerfile时&#xff0c;ENTRYPOINT或者CMD命令会自动覆盖之前…...

      AI:支持向量机(SVM)

      支持向量机(SVM)理论基础详解:从零开始的完全指南 一、SVM的核心思想:直观理解 1.1 什么是分类问题? 想象你在玩一个游戏:桌上有红色和蓝色的球,你需要画一条线把它们分开。这条线就是分类边界。SVM的目标是找到一条最优分界线,使得这条线到最近的红色球和蓝色球的距…...

      Vue.js 中 v-if 的使用及其原理

      在 Vue.js 的开发过程中&#xff0c;条件渲染是一项极为常见的需求。v-if指令作为 Vue.js 实现条件渲染的关键手段&#xff0c;能够根据表达式的真假来决定是否渲染某一块 DOM 元素。它在优化页面展示逻辑、提升用户体验等方面发挥着重要作用。接下来&#xff0c;我们就深入探讨…...

      Vue.js 中 v-show 的使用及其原理

      在 Vue.js 的开发过程中&#xff0c;我们常常需要根据不同的条件来控制页面元素的显示与隐藏。v-show指令作为 Vue.js 提供的重要工具之一&#xff0c;为我们实现这一功能提供了便捷的途径。它与v-if指令有些相似&#xff0c;但在使用方法和原理上存在着明显的区别。接下来&…...

      docker安装redisSearch

      1.背景 Redis Search 是 Redis 官方提供的全文搜索引擎,它为Redis 提供全文搜索、索引和复杂查询功能。它基于内存存储&#xff0c;结合了 Redis 的高性能和倒排索引技术&#xff0c;支持实时搜索、聚合分析、模糊匹配等场景。RedisSearch 适用于需要快速检索结构化或非结构化…...

      ADI的BF561双核DSP怎么做开发,我来说一说(六)IDE硬盘设计

      作者的话 ADI的双核DSP&#xff0c;最早的一颗是Blackfin系列的BF561&#xff0c;这颗DSP我用了很久&#xff0c;比较熟悉&#xff0c;且写过一些给新手的教程。 是的你没有看错&#xff0c;就是IDE&#xff0c;那个最老的硬盘&#xff0c;我们当年做过此类设计。 硬件准备 …...

      5.数据结构-图

      5.数据结构-图 5.1 图的定义和基本术语5.1.1 图的定义5.1.2 图的基本术语 5.2图的存储结构5.2.1邻接矩阵采用邻接矩阵表示法创建无向网邻接表 5.1 图的定义和基本术语 5.1.1 图的定义 图 G由两个集合V和E组成&#xff0c;记为 G ( V , E ) G(V,E) G(V,E)&#xff0c;其中V是…...

      uni-app使用web-view传参的坑

      问题描述 uni-app开发的一个页面&#xff0c;需要点击时跳转到PC端后台的一个详情页&#xff0c;所以需要传参如下&#xff1a; ticketIdticketCodetoken&#xff08;用于自动登录&#xff0c;校验身份的&#xff09; 但是吧&#xff0c;如果你明文传token&#xff0c;容易导…...

      Android studio打包uniapp插件

      一.参考资料与环境准备 原生工程配置需要使用到Android studio和HbuilderX 当前测试的as版本-20240301,下载地址&#xff1a;HbuilderX版本&#xff1a;4.36 二.插件创建流程 1.导入下载的UniPlugin-Hello-AS工程&#xff08;下载地址见参考资料&#xff09; 2.生成jks证书…...

      SVT-AV1学习-函数selfguided_restoration_fast_internal

      一 selfguided_restoration_fast_internal 函数作用 selfguided_restoration_fast_internal是SVT-AV1 编码器中用于自引导恢复Guided Resration SGR 的一个内部函数&#xff0c;通过自引导滤波技术对输入的去燥他图像数据进行处理&#xff0c;生成一个去燥版本的图像&#xff0…...

      双引擎驱动:解密音视频体验的QoS技术底座与QoE感官革命

      QoS 定义&#xff1a;QoS&#xff08;Quality of Service&#xff0c;服务质量&#xff09;衡量音视频传输技术层面的性能表现&#xff0c;聚焦网络传输和系统处理能力&#xff0c;通过客观指标量化服务质量。核心指标 码率/带宽&#xff1a;数据传输速率上限&#xff0c;直接…...

      element-plus选择菜单栏不变色

      代码&#xff1a; <template> ... <el-menu-item index"/task/execute"><el-icon><IconMenu /></el-icon><span>验收任务</span> </el-menu-item> <el-menu-item index"/task/change"><el-icon…...

      uniapp加载json动画

      一、添加canvas画布 <canvas id"lottie_demo" type"2d" style"display: inline-block;width: 148rpx; height: 148rpx;" /> 二、引入依赖和JSON文件 安装依赖 npm install lottie-miniprogram --save import lottie from lottie-mini…...

      快递物流展同期举办2025中国智慧物流核心零部件创新论坛

      2025中国智慧物流核心零部件创新论坛 会议主题&#xff1a;“AI ”重构智慧物流核心技术生态 会议介绍 随着人工智能、物联网、5G等技术的快速发展&#xff0c;智慧物流已成为全球物流行业转型升级的核心方向。在AI技术的驱动下&#xff0c;物流行业正从传统的“人、车、货”…...

      ASP.NET图书馆借阅系统(源码+lw+部署文档+讲解),源码可白嫖!

      摘要 近些年来&#xff0c;随着科技的飞速发展&#xff0c;互联网的普及逐渐延伸到各行各业中&#xff0c;给人们生活带来了十分的便利&#xff0c;图书馆借阅系统利用计算机网络实现信息化管理&#xff0c;使图书信息、图书借阅、归还的管理发展和服务水平有显著提升。 本文拟…...

      在 Linux 终端中轻松设置 Chromium 的 User-Agent:模拟手机模式与自定义浏览体验

      在 Linux 系统中&#xff0c;通过终端灵活控制 Chromium 的行为可以大幅提升工作效率。本文将详细介绍如何通过命令行参数和环境变量自定义 Chromium 的 User-Agent&#xff0c;并结合手机模式模拟&#xff0c;实现更灵活的浏览体验。 为什么需要自定义 User-Agent&#xff1f;…...

      实验一 单管共射极放大电路

      这篇文章是即兴写的&#xff0c;也不知道对不对&#xff0c;只有代码哦~~(文章结尾有彩蛋哦~~~&#xff09; 表1-1实验数据MATLAB代码&#xff1a; clear all; clc % 参数设置 VCC 12; % 电源电压 (V) RB1 45e3; % 偏置电阻 RB1 (Ohms) RB2 15e3; % 偏置电阻 RB2 (Ohms) R…...

      10-python面向对象(上)

      10-python面向对象【上】 1.面向对象简介2. 类(class)3. 类的定义4. 参数self4.1 属性和方法4.2 self 1.面向对象简介 Python是一门面向对象的编程语言 所谓面向对象的语言&#xff0c;简单理解就是语言中的所有操作都是通过对象来进行的 面向过程&#xff1a; 面向过程指将我们…...

      Java 大视界 -- 基于 Java 的大数据分布式缓存技术在电商高并发场景下的性能优化(181)

      &#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

      Apache Airflow开源程序是一个以编程方式编写、计划和监控工作流程的平台

      一、软件介绍 文末提供程序和源码下载 Apache Airflow开源程序是一个以编程方式编写、计划和监控工作流程的平台&#xff0c;当工作流被定义为代码时&#xff0c;它们将变得更加可维护、可版本化、可测试和协作性。使用 Airflow 将工作流创作为任务的有向无环图 &#xff08;D…...

      大数据学习(101)-spark的高可用模式

      &#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一…...