购物网站开发简介,网站开发需求报告,seo交互论坛,腾讯云学生机做网站目录
Spring 原理#xff1a;超详细解析与代码示例#xff08;新手友好版#xff09;
第一部分#xff1a;Bean的作用域#xff08;Scope#xff09;
1.1 核心概念#xff1a;什么是作用域#xff1f;
1.2 完整代码示例与逐行注释
基础类#xff1a;Dog.java
配…目录Spring 原理超详细解析与代码示例新手友好版第一部分Bean的作用域Scope1.1 核心概念什么是作用域1.2 完整代码示例与逐行注释基础类Dog.java配置类DogBeanConfig.java启动测试类SpringIocApplication.java1.3 作用域结论与代码的对应关系详解第二部分Bean的生命周期2.1 生命周期5个阶段详解2.2 完整生命周期代码与逐行注释依赖类UserComponent.java生命周期演示类BeanLifeComponent.java测试启动类SpringIocApplication.java2.3 源码级生命周期流程高手理解版第三部分Spring Boot自动配置原理3.1 问题场景为什么扫描不到第三方包3.2 解决方案演进完整代码方案1ComponentScan粗暴直接方案2Import灵活但繁琐方案3EnableXxx注解优雅终极方案3.3 Spring Boot自动配置终极原理逐层拆解第1层SpringBootApplication入口第2层EnableAutoConfiguration心脏第3层AutoConfigurationImportSelector引擎第4层META-INF/spring.factories配置清单3.4 结论与代码的终极对应关系第四部分总结与核心记忆点4.1 Bean作用域记忆口诀4.2 Bean生命周期记忆口诀4.3 Spring Boot自动配置记忆口诀新手必读如何验证你的理解Spring 原理超详细解析与代码示例新手友好版作为新手我会用最浅显易懂的方式配合逐行注释的完整代码帮你彻底搞懂Spring的核心原理。每个知识点都会解释为什么和怎么用并明确说明结论与代码的对应关系。第一部分Bean的作用域Scope1.1 核心概念什么是作用域通俗理解作用域就是Bean的寿命规则。它决定了Spring是为你创建一个新对象还是每次都给你同一个对象。关键结论与代码联系默认情况代码中不写Scope注解时就是singleton单例单例验证通过context.getBean()两次获取的对象打印出的哈希值相同如Dog1a2b3c4d原型验证加上Scope(prototype)后两次获取的对象哈希值不同1.2 完整代码示例与逐行注释基础类Dog.java// package声明告诉Java这个类位于com.example.demo包下 package com.example.demo; // 创建一个普通的Java类用来演示Bean的作用域 public class Dog { // 私有属性狗的名字 private String name; // getter方法获取狗的名字 // 外部可以通过这个方法读取name属性的值 public String getName() { return name; } // setter方法设置狗的名字 // Spring会通过这个方法注入属性值依赖注入 public void setName(String name) { this.name name; } // 重写toString方法方便打印对象信息 // 输出格式Dog{name旺旺} // 这里能看出打印的是哪个对象以及对象的name属性值 Override public String toString() { return Dog{ name name \ }; } }配置类DogBeanConfig.java// 指定当前类所在的包路径 package com.example.demo.config; // ConfigurableBeanFactory: 包含作用域的常量定义如SCOPE_SINGLETON import org.springframework.beans.factory.config.ConfigurableBeanFactory; // Bean注解告诉Spring这个方法会返回一个需要被容器管理的对象 import org.springframework.context.annotation.Bean; // Scope注解用于指定Bean的作用域 import org.springframework.context.annotation.Scope; // ScopedProxyMode枚举定义代理模式用于web作用域 import org.springframework.context.annotation.ScopedProxyMode; // Component注解标记这个类为组件让Spring扫描并注册到容器 import org.springframework.stereotype.Component; // WebApplicationContext: 包含web相关作用域的常量定义 import org.springframework.web.context.WebApplicationContext; // Component把这个配置类本身也注册为Bean // 这样Spring会处理这个类中所有的Bean方法 Component public class DogBeanConfig { // 默认是单例作用域singleton所有不指定Scope就是单例 Bean public Dog dog() { Dog dog new Dog(); // 创建Dog对象实例 dog.setName(旺旺); // 设置名字为旺旺 return dog; // 返回实例给Spring容器管理 } // Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)显式指定为单例 // 等价于Scope(singleton)使用常量更安全避免拼写错误 Bean Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public Dog singleDog() { Dog dog new Dog(); dog.setName(单例狗); // 设置名字方便识别 return dog; } // Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)原型作用域 // 每次从容器获取都会创建新实例 // 重点Spring容器启动时不会立即创建prototype Dog而是每次getBean时才创建 Bean Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public Dog prototypeDog() { Dog dog new Dog(); dog.setName(原型狗); // 注意每次获取都是新对象名字虽然相同但对象不同 return dog; } // Scope(value WebApplicationContext.SCOPE_REQUEST...)请求作用域 // value指定作用域类型为每个HTTP请求创建一次 // proxyMode ScopedProxyMode.TARGET_CLASS使用CGLIB代理 // 为什么需要代理因为request作用域的Bean要在请求期间保持一致但Spring需要提前注入 // 代理模式会在实际使用时才创建真正的Bean实例 Bean Scope(value WebApplicationContext.SCOPE_REQUEST, proxyMode ScopedProxyMode.TARGET_CLASS) public Dog requestDog() { Dog dog new Dog(); dog.setName(请求狗); return dog; } // Scope(value WebApplicationContext.SCOPE_SESSION...)会话作用域 // 每个用户会话Session创建一次 // 同一个浏览器多次访问是同一个对象换个浏览器就是新对象 Bean Scope(value WebApplicationContext.SCOPE_SESSION, proxyMode ScopedProxyMode.TARGET_CLASS) public Dog sessionDog() { Dog dog new Dog(); dog.setName(会话狗); return dog; } // Scope(value WebApplicationContext.SCOPE_APPLICATION...)应用作用域 // 整个ServletContext生命周期内只有一个实例 // 注意singleton是ApplicationContext级别application是ServletContext级别 // Web应用中可能有多个ApplicationContext但只有一个ServletContext Bean Scope(value WebApplicationContext.SCOPE_APPLICATION, proxyMode ScopedProxyMode.TARGET_CLASS) public Dog applicationDog() { Dog dog new Dog(); dog.setName(应用狗); return dog; } }启动测试类SpringIocApplication.javapackage com.example.demo; // SpringBootApplicationSpring Boot核心注解组合了多个功能 import org.springframework.boot.SpringApplication; // SpringApplicationSpring Boot应用启动入口类 import org.springframework.boot.autoconfigure.SpringBootApplication; // ApplicationContextSpring容器接口所有Bean都从这里获取 import org.springframework.context.ApplicationContext; // SpringBootApplication标记这是Spring Boot应用的主类 // 它会扫描当前包及其子包下的所有组件如Component、Bean等 SpringBootApplication public class SpringIocApplication { // main方法Java程序入口 public static void main(String[] args) { // SpringApplication.run()启动Spring Boot应用 // 返回ApplicationContext对象这就是Spring容器 ApplicationContext context SpringApplication.run(SpringIocApplication.class, args); // context.getBean(Dog.class)从容器中获取Dog类型的Bean // 因为是单例两次获取的是同一个对象 Dog dog1 context.getBean(Dog.class); System.out.println(第一次获取: dog1); // 打印对象哈希和名字 Dog dog2 context.getBean(Dog.class); System.out.println(第二次获取: dog2); // 哈希值相同证明是单例 // 验证prototype作用域 // 注意需要指定bean名称获取因为容器中有多个Dog类型的Bean Dog protoDog1 (Dog) context.getBean(prototypeDog); Dog protoDog2 (Dog) context.getBean(prototypeDog); System.out.println(原型1: protoDog1); // 哈希值不同 System.out.println(原型2: protoDog2); // 哈希值不同 // 重要context.close()关闭容器触发单例Bean的销毁 // 但prototype Bean不会销毁因为容器不管理它的完整生命周期 ((ConfigurableApplicationContext) context).close(); } }1.3 作用域结论与代码的对应关系详解作用域代码体现如何验证结论singletonBean或Scope(singleton)两次getBean()打印的哈希值相同如Dog1a2b3c4dprototypeScope(prototype)两次getBean()打印的哈希值不同如Dog1a2b3c4d和Dog5d6e7f8grequestScope(valuerequest, proxyModeTARGET_CLASS)同一个URL多次刷新对象哈希不变换个浏览器访问哈希变化sessionScope(valuesession, proxyModeTARGET_CLASS)同一个浏览器多次访问哈希不变清cookie或换浏览器哈希变化applicationScope(valueapplication, proxyModeTARGET_CLASS)所有用户请求获取的都是同一个对象哈希相同重要细节proxyMode ScopedProxyMode.TARGET_CLASS这是web作用域的关键。Spring会先注入一个代理对象真正使用时才创建实际Bean解决长寿命Bean注入短寿命Bean的问题测试技巧在Dog构造函数中加System.out.println(创建Dog实例)你会发现singleton只打印一次prototype每次getBean都会打印第二部分Bean的生命周期2.1 生命周期5个阶段详解通俗理解Bean的一生就像人的成长过程出生→上学→工作→退休→去世结论与代码的对应关系实例化执行构造函数new BeanLifeComponent()→ 对应代码中的System.out.println(执行构造函数)属性赋值调用Autowired的setter方法 → 对应setUserComponent()方法初始化执行PostConstruct方法 → 对应postConstruct()方法使用调用业务方法 → 对应use()方法销毁执行PreDestroy方法 → 对应preDestroy()方法在context.close()时触发2.2 完整生命周期代码与逐行注释依赖类UserComponent.javapackage com.example.demo.component; import org.springframework.stereotype.Component; // Component把这个类注册为单例Bean // 它将被注入到BeanLifeComponent中演示依赖注入过程 Component public class UserComponent { // 普通业务方法 public void sayHello() { System.out.println(Hello from UserComponent); } }生命周期演示类BeanLifeComponent.javapackage com.example.demo.component; // 导入UserComponent用于演示依赖注入 import com.example.demo.UserComponent; // BeanNameAware接口让Bean知道自己在容器中的名字 import org.springframework.beans.factory.BeanNameAware; // Autowired自动注入依赖 import org.springframework.beans.factory.annotation.Autowired; // Component标记为组件 import org.springframework.stereotype.Component; // PostConstructJSR-250注解在Bean初始化后执行 import javax.annotation.PostConstruct; // PreDestroyJSR-250注解在Bean销毁前执行 import javax.annotation.PreDestroy; // 实现BeanNameAware接口能获取到Bean的名称 // 这是生命周期中的一个感知接口 Component public class BeanLifeComponent implements BeanNameAware { // 声明UserComponent依赖将在属性赋值阶段注入 private UserComponent userComponent; // 1. 实例化阶段构造函数 // Spring通过反射调用new BeanLifeComponent() public BeanLifeComponent() { System.out.println(执行构造函数 → 实例化完成); } // 2. 属性赋值阶段Autowired自动注入 // Spring在实例化后会查找所有Autowired方法并调用 // 这里注入了userComponent依赖 Autowired public void setUserComponent(UserComponent userComponent) { System.out.println(设置属性userComponent → 依赖注入完成); this.userComponent userComponent; } // BeanNameAware接口方法设置Bean名称 // 3. 初始化阶段 - Aware接口回调 // Spring检测到实现了BeanNameAware自动调用此方法 // 参数s就是Bean在容器中的名字默认为beanLifeComponent Override public void setBeanName(String s) { System.out.println(执行setBeanName方法 → Bean名字叫: s); } // 3. 初始化阶段 - 初始化方法 // PostConstruct在属性赋值完成后立即执行 // 常用于资源初始化如连接数据库、加载配置等 PostConstruct public void postConstruct() { System.out.println(执行PostConstruct() → 初始化完成); } // 4. 使用阶段业务方法 // 此时Bean已完全准备好可以执行业务逻辑 public void use() { System.out.println(执行了use方法 → Bean正在使用中); userComponent.sayHello(); // 调用注入的依赖方法 } // 5. 销毁阶段销毁前回调 // PreDestroy在容器关闭前执行 // 常用于资源释放如关闭连接、清理临时文件等 PreDestroy public void preDestroy() { System.out.println(执行PreDestroy() → Bean即将销毁); } }测试启动类SpringIocApplication.javapackage com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; // ConfigurableApplicationContext可关闭的ApplicationContext import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ApplicationContext; // 导入我们的生命周期演示类 import com.example.demo.component.BeanLifeComponent; SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { // 启动应用获取Spring容器 // 此时会触发Bean的实例化、属性赋值、初始化 ConfigurableApplicationContext context SpringApplication.run(SpringIocApplication.class, args); // 从容器中获取BeanLifeComponent实例 // 注意因为之前已经初始化完成这里只是获取已存在的Bean BeanLifeComponent beanLifeComponent context.getBean(BeanLifeComponent.class); // 调用业务方法演示Bean的使用 beanLifeComponent.use(); // 关闭容器触发所有单例Bean的销毁方法 // 这行代码会调用PreDestroy方法 context.close(); // 运行结果顺序 // 1. 执行构造函数 → 实例化完成 // 2. 设置属性userComponent → 依赖注入完成 // 3. 执行setBeanName方法 → Bean名字叫: beanLifeComponent // 4. 执行PostConstruct() → 初始化完成 // 5. 执行了use方法 → Bean正在使用中 // 6. Hello from UserComponent // 7. 执行PreDestroy() → Bean即将销毁 } }2.3 源码级生命周期流程高手理解版虽然新手不需要深入源码但了解Spring内部如何运转能帮你更好地理解// Spring创建Bean的核心流程伪代码 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) { // 1. 实例化调用构造函数创建空对象 Object bean createBeanInstance(beanName, mbd, args); // → 执行构造函数 // 2. 属性赋值注入依赖 populateBean(beanName, mbd, bean); // → 执行Autowired方法 // 3. 初始化完成各种回调 Object exposedObject initializeBean(beanName, bean, mbd); // 3.1 invokeAwareMethods() → 执行BeanNameAware等 // 3.2 applyBeanPostProcessorsBeforeInitialization() → BeanPostProcessor前置处理 // 3.3 invokeInitMethods() → 执行PostConstruct // 3.4 applyBeanPostProcessorsAfterInitialization() → BeanPostProcessor后置处理 return exposedObject; // 返回完全初始化的Bean }结论与源码的对应源码的createBeanInstance()→ 你的构造函数打印源码的populateBean()→ 你的Autowired setter打印源码的invokeAwareMethods()→ 你的setBeanName()打印源码的invokeInitMethods()→ 你的PostConstruct打印源码的destroy()→ 你的PreDestroy打印在context.close()时第三部分Spring Boot自动配置原理3.1 问题场景为什么扫描不到第三方包核心结论SpringBootApplication默认只扫描启动类所在包及其子包代码体现// 启动类在 com.example.demo 包下 package com.example.demo; SpringBootApplication public class SpringAutoconfigApplication { ... } // 第三方类在 com.bite.autoconfig 包下不同层级 package com.bite.autoconfig; Component public class BiteConfig { ... } // 测试获取Bean会失败因为BiteConfig不在扫描路径内 BiteConfig biteConfig context.getBean(BiteConfig.class); // 抛出NoSuchBeanDefinitionException3.2 解决方案演进完整代码方案1ComponentScan粗暴直接package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; // ComponentScan手动指定扫描路径 import org.springframework.context.annotation.ComponentScan; // ComponentScan(com.bite.autoconfig)额外扫描第三方包 // 注意如果第三方包很多要写很多路径非常繁琐 ComponentScan(com.bite.autoconfig) SpringBootApplication public class SpringAutoconfigApplication { public static void main(String[] args) { SpringApplication.run(SpringAutoconfigApplication.class, args); } }方案2Import灵活但繁琐方式2.1直接导入类package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; // Import导入指定类到容器 import org.springframework.context.annotation.Import; import com.bite.autoconfig.BiteConfig; // Import(BiteConfig.class)直接把BiteConfig类注册到容器 // 缺点如果有多个第三方类要列很多 Import(BiteConfig.class) SpringBootApplication public class SpringAutoconfigApplication { public static void main(String[] args) { SpringApplication.run(SpringAutoconfigApplication.class, args); } }方式2.2导入ImportSelector批量导入// MyImportSelector.java package com.example.demo.config; // ImportSelector批量导入接口 import org.springframework.context.annotation.ImportSelector; // AnnotationMetadata获取注解元数据 import org.springframework.core.type.AnnotationMetadata; // 实现ImportSelector接口可以一次性导入多个类 public class MyImportSelector implements ImportSelector { Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 返回要导入的全限定类名数组 // Spring会自动把这些类注册为Bean return new String[]{ com.bite.autoconfig.BiteConfig, com.bite.autoconfig.BiteConfig2 }; } } // 启动类 package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Import; import com.example.demo.config.MyImportSelector; // Import(MyImportSelector.class)导入选择器 // 优点集中管理第三方类启动类更简洁 Import(MyImportSelector.class) SpringBootApplication public class SpringAutoconfigApplication { public static void main(String[] args) { SpringApplication.run(SpringAutoconfigApplication.class, args); } }方案3EnableXxx注解优雅终极方案// EnableBiteConfig.java第三方提供 package com.bite.autoconfig.annotation; // 导入选择器 import org.springframework.context.annotation.Import; // 元注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.example.demo.config.MyImportSelector; // Retention(RetentionPolicy.RUNTIME)注解在运行时保留 // Target(ElementType.TYPE)注解可以用在类上 // Import(MyImportSelector.class)实际导入逻辑在这里 // 这就是EnableXxx的本质封装了Import Retention(RetentionPolicy.RUNTIME) Target(ElementType.TYPE) Import(MyImportSelector.class) public interface EnableBiteConfig { // 可以添加配置属性例如指定包路径等 } // 启动类使用者视角 package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.bite.autoconfig.annotation.EnableBiteConfig; // EnableBiteConfig一行代码开启第三方自动配置 // 优点极简、优雅使用者无需关心底层细节 // 这就是Spring Boot自动配置的核心思想 EnableBiteConfig SpringBootApplication public class SpringAutoconfigApplication { public static void main(String[] args) { SpringApplication.run(SpringAutoconfigApplication.class, args); } }3.3 Spring Boot自动配置终极原理逐层拆解第1层SpringBootApplication入口// 这个注解在启动类上 Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented Inherited // 下面三个是核心组合注解 SpringBootConfiguration // 标记为配置类 EnableAutoConfiguration // 开启自动配置最关键 ComponentScan( // 包扫描默认扫描启动类所在包 excludeFilters { Filter(type FilterType.CUSTOM, classes TypeExcludeFilter.class), Filter(type FilterType.CUSTOM, classes AutoConfigurationExcludeFilter.class) } ) public interface SpringBootApplication { // ... 省略属性 }第2层EnableAutoConfiguration心脏Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented Inherited AutoConfigurationPackage // 注册启动类所在包 Import({AutoConfigurationImportSelector.class}) // 批量导入自动配置类核心 public interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY spring.boot.enableautoconfiguration; Class?[] exclude() default {}; String[] excludeName() default {}; }第3层AutoConfigurationImportSelector引擎public class AutoConfigurationImportSelector implements DeferredImportSelector { // selectImports返回需要导入的配置类全名 public String[] selectImports(AnnotationMetadata annotationMetadata) { // 1. 判断是否启用自动配置 if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 2. 获取自动配置条目 AutoConfigurationEntry autoConfigurationEntry this.getAutoConfigurationEntry(annotationMetadata); // 3. 返回配置类列表 return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } // getAutoConfigurationEntry读取配置文件中的配置类 protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { // 获取所有候选配置类从META-INF/spring.factories读取 ListString configurations this.getCandidateConfigurations(annotationMetadata, attributes); // 去重、排除、过滤通过Conditional条件判断 configurations this.removeDuplicates(configurations); SetString exclusions this.getExclusions(annotationMetadata, attributes); configurations.removeAll(exclusions); configurations this.getConfigurationClassFilter().filter(configurations); return new AutoConfigurationEntry(configurations, exclusions); } }第4层META-INF/spring.factories配置清单# 在spring-boot-autoconfigure.jar的META-INF/spring.factories中 org.springframework.boot.autoconfigure.EnableAutoConfiguration\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration # ... 100多个自动配置类3.4 结论与代码的终极对应关系Spring Boot自动配置流程启动类注解→ 代码SpringBootApplication开启自动配置→ 代码EnableAutoConfiguration在前者内部读取配置清单→ 代码AutoConfigurationImportSelector读取spring.factories条件判断→ 代码ConditionalOnClass({JedisConnection.class})注册Bean→ 代码配置类中的Bean方法注入使用→ 代码Autowired private RedisTemplate redisTemplate;Redis自动配置示例// 当你在pom.xml中加入 // dependency // groupIdorg.springframework.boot/groupId // artifactIdspring-boot-starter-data-redis/artifactId // /dependency // Spring Boot自动加载这个类从spring.factories中找到 Configuration ConditionalOnClass({JedisConnection.class, JedisPool.class}) // 条件类路径下有Jedis public class RedisAutoConfiguration { // 条件用户自己没有定义JedisConnectionFactory Bean时才创建 Bean ConditionalOnMissingBean public JedisConnectionFactory jedisConnectionFactory() { return new JedisConnectionFactory(); // 自动创建连接工厂 } // 条件用户自己没有定义RedisTemplate Bean时才创建 Bean ConditionalOnMissingBean public RedisTemplateString, Object redisTemplate(JedisConnectionFactory connectionFactory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(connectionFactory); // 自动注入连接工厂 return template; } } // 你的代码可以直接使用无需任何配置 RestController public class MyController { Autowired private RedisTemplateString, Object redisTemplate; // 自动注入 GetMapping(/test) public String test() { redisTemplate.opsForValue().set(key, value); return success; } }第四部分总结与核心记忆点4.1 Bean作用域记忆口诀singleton单身一辈子容器内只有一个prototype多胞胎每次都要新的request一次请求一生情HTTP请求内相同session浏览器恋爱史浏览器会话内相同application全公司都知道整个服务器共享4.2 Bean生命周期记忆口诀创配初使毁创建对象构造函数配置属性Autowired初始化PostConstruct使用业务方法毁灭PreDestroy4.3 Spring Boot自动配置记忆口诀总分总结构总SpringBootApplication总开关分EnableAutoConfigurationComponentScanSpringBootConfiguration三兄弟总spring.factories总清单→AutoConfigurationImportSelector总指挥新手必读如何验证你的理解动手改代码把Scope(singleton)改成prototype看哈希值变化打断点在构造函数、Autowired方法、PostConstruct方法打断点看调用顺序删依赖移除Redis的starter看redisTemplate是否还能注入会报错加排除在启动类加SpringBootApplication(exclude RedisAutoConfiguration.class)看Redis是否还自动配置希望这篇超详细教程能帮你真正理解Spring原理记住代码不会骗人打印的哈希值和调用顺序就是结论的最好证明。有疑问随时追问