Autowired和Resource的关系?

典型回答 相同点 对于下面的代码来说,如果是Spring容器的话,两个注解的功能基本是等价的,他们都可以将bean注入到对应的field中。 1 2 3 4 5 @Autowired private Bean beanA; @Resource private Bean beanB; 不同点 支持方不同 Autowired是Spring提供的自动注入注解,只有Spring容器会支持,如果做容器迁移,是需要修改代码的。 以下是Autowired注解的定义,可以看到他是属于spring的包下面的。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package org.springframework.beans.factory.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { boolean required() default true; } � ...

March 22, 2026 · 2 min · santu

Spring 7 和Spring Boot 4 都有哪些新特性?

典型回答 2025 年 11 月,Spring Framework 7.0 和 Spring Boot 4.0 已正式发布。新特性挑一些比较重要的介绍下。 最低版本要求 这个版本同样对一些基础依赖的版本提出了要求,JDK要求最低要17,推荐用JDK 21 (支持虚拟线程),也可以直接用当前的最新的LTS版本——JDK 25(截止2025年11月)。 Jakarta EE全面替代java ee,javax包废弃,并且Jakarta EE升级到11了。另外构建工具要求Maven 3.6.3+ / Gradle 8.5+(Spring Boot 4 还支持 Gradle 9) **<font style="color:rgb(17, 17, 51);background-color:rgba(175, 184, 193, 0.2);">RestTemplate</font>** 弃用 RestTemplate 正式弃用(7.1 标记为 @Deprecated,8.0 移除)推荐使用RestClient(非响应式)和WebClient(响应式) 另外,Feign也有替代产品了——声明式 HTTP 客户端(@HttpExchange),这个其实在Spring Boot 3中就有了,在这个版本做了更新,更是官方强烈推荐使用了 1 2 3 4 5 @HttpExchange("/users") interface UserClient { @Get("/{id}") User findById(String id); } Jackson 3 全面集成 默认 JSON 处理库升级为 Jackson 3.x包名从 com.fasterxml.jackson改为tools.jackson,目前仍可并行使用 Jackson 2 和 3,但 Jackson 2 将在 Spring 7.2 中移除。 ...

March 22, 2026 · 2 min · santu

Spring Bean的初始化过程是怎么样的?

典型回答 ✅Spring Bean的生命周期是怎么样的? 先看过上面这篇,我们就能知道,Spring的可以分为5个小的阶段:实例化、初始化、注册Destruction回调、Bean的正常使用以及Bean的销毁。 我们再把初始化的这个过程单独拿出来展开介绍一下。(本文代码基于 Spring 6.0版本) 首先先看一下初始化和实例化的区别是什么? 在Spring框架中,初始化和实例化是两个不同的概念: 实例化(Instantiation): 实例化是创建对象的过程。在Spring中,这通常指的是通过调用类的构造器来创建Bean的实例。这是对象生命周期的开始阶段。对应doCreateBean中的createBeanInstance方法。 初始化(Initialization): 初始化是在Bean实例创建后,进行一些设置或准备工作的过程。在Spring中,包括设置Bean的属性,调用各种前置&后置处理器。对应doCreateBean中的populateBean和initializeBean方法。 下面是SpringBean的实例化+初始化的完整过程: 实例化Bean Spring容器在这一步创建Bean实例。其主要代码在AbstractAutowireCapableBeanFactory类中的createBeanInstance方法中实现: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { // 解析Bean的类,确保Bean的类在这个点已经被确定 Class<?> beanClass = resolveBeanClass(mbd, beanName); // 检查Bean的访问权限,确保非public类允许访问 if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } // 如果Bean定义中指定了工厂方法,则通过工厂方法创建Bean实例 if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // 当重新创建相同的Bean时的快捷路径 boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { // 如果构造方法或工厂方法已经被解析,直接使用解析结果 if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { // 如果需要自动装配构造函数参数,则调用相应方法进行处理 if (autowireNecessary) { return autowireConstructor(beanName, mbd, null, null); } else { // 否则使用无参构造函数或默认构造方法创建实例 return instantiateBean(beanName, mbd); } } // 通过BeanPostProcessors确定构造函数候选 Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); // 如果有合适的构造函数或需要通过构造函数自动装配,则使用相应的构造函数创建实例 if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // 没有特殊处理,使用默认的无参构造函数创建Bean实例 return instantiateBean(beanName, mbd); } 其实就是先确保这个Bean对应的类已经被加载,然后确保它是public的,然后如果有工厂方法,则直接调用工厂方法创建这个Bean,如果没有的话就调用它的构造方法来创建这个Bean。 ...

March 22, 2026 · 5 min · santu

Spring Bean的生命周期是怎么样的?

典型回答 一个Spring的Bean从出生到销毁的全过程就是他的整个生命周期,那么经历以下几个阶段: 整个生命周期可以大致分为3个大的阶段,分别是:创建、使用、销毁。还可以进一步分为5个小的阶段:实例化、初始化、注册Destruction回调、Bean的正常使用以及Bean的销毁。 有人把设置属性值这一步单独拿出来了,主要是因为在源码中doCreateBean是先调了populateBean进行属性值的设置,然后再调initializeBean进行各种前置&后置处理。但是其实属性的设置其实就是初始化的一部分。要不然初始化啥呢? 有人也把注册Destruction回调放到销毁这一步了,其实是不对的,其实他不算初始化的一步,也不应该算作销毁的一个过程,他虽然和销毁有关,但是他是在创建的这个生命周期中做的。 具体到代码方面,可以参考以下这个更加详细的过程介绍,我把具体实现的代码位置列出来了。 实例化Bean: Spring容器首先创建Bean实例。 在**AbstractAutowireCapableBeanFactory类中的createBeanInstance**方法中实现 设置属性值: Spring容器注入必要的属性到Bean中。 在**AbstractAutowireCapableBeanFactory的populateBean**方法中处理 检查Aware: 如果Bean实现了BeanNameAware、BeanClassLoaderAware等这些Aware接口,Spring容器会调用它们。 在**AbstractAutowireCapableBeanFactory的initializeBean**方法中调用 调用BeanPostProcessor的前置处理方法: 在Bean初始化之前,允许自定义的BeanPostProcessor对Bean实例进行处理,如修改Bean的状态。BeanPostProcessor的postProcessBeforeInitialization方法会在此时被调用。 由**AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsBeforeInitialization**方法执行。 调用InitializingBean的afterPropertiesSet方法: 提供一个机会,在所有Bean属性设置完成后进行初始化操作。如果Bean实现了InitializingBean接口,afterPropertiesSet方法会被调用。 在**AbstractAutowireCapableBeanFactory的invokeInitMethods**方法中调用。 调用自定义init-method方法: 提供一种配置方式,在XML配置中指定Bean的初始化方法。如果Bean在配置文件中定义了初始化方法,那么该方法会被调用。 在**AbstractAutowireCapableBeanFactory的invokeInitMethods**方法中调用。 调用BeanPostProcessor的后置处理方法: 在Bean初始化之后,再次允许BeanPostProcessor对Bean进行处理。BeanPostProcessor的postProcessAfterInitialization方法会在此时被调用。 由**AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsAfterInitialization**方法执行 注册**Destruction回调:** 如果Bean实现了DisposableBean接口或在Bean定义中指定了自定义的销毁方法,Spring容器会为这些Bean注册一个销毁回调,确保在容器关闭时能够正确地清理资源。 在**AbstractAutowireCapableBeanFactory类中的registerDisposableBeanIfNecessary**方法中实现 Bean准备就绪: 此时,Bean已完全初始化,可以开始处理应用程序的请求了。 调用DisposableBean的destroy方法: 当容器关闭时,如果Bean实现了DisposableBean接口,destroy方法会被调用。 在**DisposableBeanAdapter的destroy**方法中实现 调用自定义的destory-method 如果Bean在配置文件中定义了销毁方法,那么该方法会被调用。 在**DisposableBeanAdapter的destroy**方法中实现 可以看到,整个Bean的创建的过程都依赖于**AbstractAutowireCapableBeanFactory这个类,而销毁主要依赖DisposableBeanAdapter**这个类。 AbstractAutowireCapableBeanFactory 的入口处,doCreateBean的核心代码如下,其中包含了实例化、设置属性值、初始化Bean以及注册销毁回调的几个核心方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { // 实例化bean BeanWrapper instanceWrapper = null; if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } // ... Object exposedObject = bean; try { //设置属性值 populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { //初始化Bean exposedObject = initializeBean(beanName, exposedObject, mbd); } } // ... // 注册Bean的销毁回调 try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } return exposedObject; } 而**DisposableBeanAdapter**的destroy方法中核心内容如下: ...

March 22, 2026 · 1 min · santu

Spring 中的 Bean 作用域有哪些?

典型回答 所谓作用域,其实就是说这个东西哪个范围内可以被使用。如我们定义类的成员变量的时候使用的public、private等这些也是作用域的概念。 Spring的Bean的作用域,描述的就是这个Bean在哪个范围内可以被使用。不同的作用域决定了了 Bean 的创建、管理和销毁的方式。 常见的作用域有Singleton、Prototype、Request、Session、Application这五种。我们在代码中,可以在定义一个Bean的时候,通过@Scope 注解来指定他的作用域: 1 2 3 4 5 @Service @Scope("prototype") public class HollisTestService{ } 这五种作用域的解释如下: 单例(Singleton): 默认作用域。 对于每个 Spring IoC 容器,只创建一个 Bean 实例。 适用于全局共享的状态。 原型(Prototype): 每次请求都会创建一个新的 Bean 实例。 适用于所有状态都是非共享的情况。 请求(Request): 仅在 Web 应用程序中有效。 每个 HTTP 请求都会创建一个新的 Bean 实例。 用于请求级别的数据存储和处理。 会话(Session): 仅在 Web 应用程序中有效。 每个 HTTP 会话都会创建一个新的 Bean 实例。 适用于会话级别的数据存储和处理。 应用(Application): 仅在 Web 应用程序中有效。 在 ServletContext 的生命周期内,只创建一个 Bean 实例。 适用于全应用程序级别的共享数据。 Websocket: 仅在 Web 应用程序中有效。 在 Websocket 的生命周期内,只创建一个 Bean 实例。 适用于websocket级别的共享数据。 一般来说我们都是使用Singleton的作用域,有的时候也会用Prototype,其他几个用得不多。 ...

March 22, 2026 · 2 min · santu

SpringBoot和传统的双亲委派有什么不一样吗?

典型回答 传统的采用双亲委派的类加载机制大家都知道,要加载一个类的额时候,先委托父类加载器加载该类;如果父类加载器无法找到(类不存在),才由当前 ClassLoader 加载;这样保证了核心类库(rt.jar)不会被重复加载,避免了类冲突问题。 在JDK 1.8之前,传统的双亲委派如下: ✅什么是双亲委派?如何破坏? 在JDK 1.9中,传统的双亲委派如下: ✅JDK1.8和1.9中类加载器有哪些不同 Spring Boot 打破了传统的双亲委派模型,采用 自定义的 **LaunchedURLClassLoader** 进行类加载,主要为了支持 “Fat JAR”(可执行 JAR) 的运行。 ✅什么是fat jar? 在 Spring Boot 中,使用 Maven 或 Gradle 构建项目时,lib/ 目录中的第三方依赖是以 JAR 形式打包进主 JAR 内部,默认会生成一个包含所有依赖项的 fat jar。 传统的 <font style="background-color:#FBDE28;">Application ClassLoader</font> 只能从 外部 **<font style="background-color:#FBDE28;">classpath</font>** 加载类,无法直接加载 JAR 包内嵌的其他 JAR(fat jar)。 因此 Spring Boot 需要自定义类加载器。 为了支持 Fat JAR 运行模式,Spring Boot **使用 **LaunchedURLClassLoader** 替代 ****AppClassLoader**,打破双亲委派机制,核心做法是: 先加载 **BOOT-INF/classes** 目录下的应用类(优先于 JDK 类)。 再加载 **BOOT-INF/lib/** 目录下的依赖 JAR(传统 AppClassLoader 无法加载嵌套 JAR)。 最后才交给父类加载器(即 JDK 提供的 AppClassLoader)。 也就是说Spring Boot 先尝试加载自身的类和依赖 JAR,找不到才交给父类加载器,从而打破双亲委派。 ...

March 22, 2026 · 2 min · santu

SpringBoot是如何实现main方法启动Web项目的?

典型回答 在Spring Boot中,通过SpringApplication类的静态方法run来启动Web项目。当我们在main方法中调用run方法时,Spring Boot使用一个内嵌的Tomcat服务器,并将其配置为处理Web请求。 当应用程序启动时,Spring Boot会自动扫描应用程序中所有的Spring组件,并使用默认的配置来启动内嵌的Tomcat服务器。在默认情况下,Spring Boot会自动配置大部分的Web开发所需的配置,包括请求处理、视图解析、静态资源处理等。 这样,在应用程序启动后,我们就可以通过Web浏览器访问应用程序了。例如,在默认情况下,可以通过访问http://localhost:8080 来访问应用程序的首页。 但是,很多人都会忽略一个关键的步骤(网上很多介绍SpringBoot启动流程的都没提到),那就是Web容器的启动,及Tomcat的启动其实也是在这个步骤。 实现原理 在SpringBoot的启动流程中,会调用SpringApplication.run方法,这个方法会有一个步骤进行上下文刷新(refreshContext),然后这个过程中,会调用一个关键的方法onRefresh。 ✅SpringBoot的启动流程是怎么样的? 调用链:SpringApplication.run -> refreshContext -> refresh -> onRefresh 在refresh-> onRefresh中,这里会调用到ServletWebServerApplicationContext的onRefresh中: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { StartupStep createWebServer = getApplicationStartup().start("spring.boot.webserver.create"); ServletWebServerFactory factory = getWebServerFactory(); createWebServer.tag("factory", factory.getClass().toString()); this.webServer = factory.getWebServer(getSelfInitializer()); createWebServer.end(); getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer)); getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer)); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); } 这里面的�createWebServer方法中,调用到factory.getWebServer(getSelfInitializer());的时候,factory有三种实现,分别是JettyServletWebServerFactory、TomcatServletWebServerFactory、UndertowServletWebServerFactory这三个,默认使用TomcatServletWebServerFactory。 ...

March 22, 2026 · 2 min · santu

Springboot是如何实现自动配置的?

典型回答 Spring Boot会根据类路径中的jar包、类,为jar包里的类自动配置,这样可以极大的减少配置的数量。简单点说就是它会根据定义在classpath下的类,自动的给你生成一些Bean,并加载到Spring的Context中。 SpringBoot通过Spring 的条件配置决定哪些bean可以被配置,将这些条件定义成具体的Configuration,然后将这些Configuration配置到spring.factories文件中(这种方式Springboot 2.7.0版本已不建议使用,最新的方式是使用 /META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ) 作为key: org.springframework.boot.autoconfigure.EnableAutoConfiguration的值 这时候,容器在启动的时候,由于使用了EnableAutoConfiguration注解,该注解Import的EnableAutoConfigurationImportSelector会去扫描classpath下的所有spring.factories文件,然后进行bean的自动化配置: 扩展知识 条件化配置 假设你希望一个或多个bean只有在某种特殊的情况下才需要被创建,比如,一个应用同时服务于中美用户,要在中美部署,有的服务在美国集群中需要提供,在中国集群中就不需要提供。在Spring 4之前,要实现这种级别的条件化配置是比较复杂的,但是,Spring 4引入了一个新的@Conditional注解可以有效的解决这类问题。 1 2 3 4 5 @Bean @Conditional(ChinaEnvironmentCondition.class) public ServiceBean serviceBean(){ return new ServiceBean(); } 当@Conditional(ChinaEnvironmentCondition.class)条件的值为true的时候,该ServiceBean才会被创建,否则该bean就会被忽略。 @Conditional指定了一个条件。他的条件的实现是一个Java类——ChinaEnvironmentCondition,要实现以上功能就要定义ChinaEnvironmentCondition类,并继承Condition接口并重写其中的matches方法。 1 2 3 4 5 6 7 class ChinaEnvironmentCondition implements Condition{ public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment env = context.getEnvironment(); return env.containProperty("ENV_CN"); } } 在上面的代码中,matches方法的内容比较简单,他通过给定的ConditionContext对象进而获取Environment对象,然后使用该对象检查环境中是否存在ENV_CN属性。如果存在该方法则直接返回true,反之返回false。当该方法返回true的时候,就符合了@Conditional指定的条件,那么ServiceBean就会被创建。反之,如果环境中没有这个属性,那么这个ServiceBean就不会被创建。 除了可以自定义一些条件之外,Spring 4本身提供了很多已有的条件供直接使用,如: ...

March 22, 2026 · 2 min · santu

SpringBoot的启动流程是怎么样的?

典型回答 以下就是一个SpringBoot启动的入口,想要了解SpringBoot(本文代码基于SpringBoot 3)的启动过程,就从这里开始。 1 2 3 4 5 6 @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); 也可简化调用静态方法 } } 这里我们直接看重重点的SpringApplication.run(Application.class, args);方法。他的实现细节如下: 1 2 3 4 5 6 7 public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[] { source }, args); } public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); } 最终就是new SpringApplication(sources).run(args)这部分代码了。那么接下来就需要分两方面介绍SpringBoot的启动过程。一个是new SpringApplication的初始化过程,一个是SpringApplication.run的启动过程。 ...

March 22, 2026 · 6 min · santu

SpringMVC是如何将不同的Request路由到不同Controller中的?

典型回答 在计算机程序处理中,但凡涉及到路由,那包含到的数据结构一定是和map相关的。所以对于url和controller之间的映射,如果交给我们来设计的话,可能会用一个大的map将url和controller中对应的方法作为键值对存储起来,以此来达到路由的目的。 对于Spring MVC的流程中来说,当http请求进入tomcat并在HttpServlet中处理的时候,首先会解析http request中的数据,以此来拿到对应的HandlerMethod(HandlerMethod封装了对应的Method和持有它的Bean)。明白了这一层,本问题就会从不同的request如何路由到不同的Controller变为不同的request如何拿到对应的**HandlerMethod** Spring MVC在启动的时候,会把带有@RequestMapping注解的方法和类封装成一个RequestMappingInfo�和HandlerMethod,然后注册到MappingRegistry。当HttpServletRequest�访问时,会通过AbstractHandlerMethodMapping#lookupHandlerMethod方法获取对应的HandlerMethod�,核心代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); // 先通过url获取到对应的RequestMappingInfo集合 List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath); if (directPathMatches != null) { // 把RequestMappingInfo和HandlerMethod放到match里面 addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request); } if (!matches.isEmpty()) { Match bestMatch = matches.get(0); // 如果匹配到多个Match(譬如url相同但是方法不同),则通过RequestMappingInfo中的各种condition匹配出对应的bestMatch if (matches.size() > 1) { } // 获取match中的HandlerMethod return bestMatch.getHandlerMethod(); } else { return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request); } } 要知道,一个http请求中,携带有不同的信息,如url,method,header等等,SpringMVC通过Match类统一封装所有的RequestMappingInfo中的各种condition,同时利用compare方法,直接比较出最优的那个handlerMethod。同时,不管是RequestMappingInfo和其组合的各个condition都实现了RequestCondition接口,所以,这也符合组合模式的基本思想 ...

March 22, 2026 · 1 min · santu

留言给博主