介绍下@Scheduled的实现原理以及用法

典型回答 Spring 的 @Scheduled 注解用于在 Spring 应用中配置和执行定时任务。 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.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class ScheduledTasks { // 每隔5秒执行一次 @Scheduled(fixedRate = 5000) public void performTask() { System.out.println("fixedRate task " + System.currentTimeMillis()); } // 上一个任务完成后等待5秒再执行 @Scheduled(fixedDelay = 5000) public void performDelayedTask() { System.out.println("fixedDelay task " + System.currentTimeMillis()); } // 每天晚上12点执行 @Scheduled(cron = "0 0 0 * * ?") public void performTaskUsingCron() { System.out.println("corn task " + System.currentTimeMillis()); } } Spring 的定时任务调度框架在 spring-context 包中,所有的类都在 scheduling 包中: ...

March 22, 2026 · 3 min · santu

如何自定义一个starter?

典型回答 在Spring Boot中,创建一个自定义starter可以简化特定功能或组件的配置过程,让其他项目能够轻松地重用这些功能。 这里我们以自定义一个xxl-job的starter为例,介绍下如何简化配置。 添加依赖 添加Spring Boot的依赖: 1 2 3 4 5 6 7 8 9 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- 其他需要的依赖 --> </dependencies> 实现自动配置 在starter项目中,创建自动配置类。这个类要使用@Configuration注解,并根据条件使用@ConditionalOn...注解来条件化地配置beans。(例如,可以使用@ConditionalOnMissingBean来只在用户没有定义特定bean时才提供一个默认实现。) 如果你的starter需要配置属性,可以通过定义一个配置属性类来实现,使用@ConfigurationProperties注解。 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 import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author Hollis */ @ConfigurationProperties(prefix = XxlJobProperties.PREFIX) public class XxlJobProperties { public static final String PREFIX = "spring.xxl.job"; private boolean enabled; private String adminAddresses; private String accessToken; private String appName; private String ip; private int port; private String logPath; private int logRetentionDays = 30; //getter setter } 接下来定义Configuration,并且在其中创建需要的bean: ...

March 22, 2026 · 2 min · santu

为什么Spring不建议使用基于字段的依赖注入?

典型回答 在我们通过IDEA编写Spring的代码的时候,假如我们编写了如下代码: 1 2 @Autowired private Bean bean; IDEA会给我们一个warning警告: Field injection is not recommended 翻阅官方文档我们会发现: Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. 大意就是**强制依赖使用构造器注入,可选依赖使用setter注入。**那么这是为什么呢?使用字段注入又会导致什么问题呢? 单一职责问题 我们都知道,根据SOLID设计原则来讲,一个类的设计应该符合单一职责原则,就是一个类只能做一件功能,当我们使用基于字段注入的时候,随着业务的暴增,字段越来越多,我们是很难发现我们已经默默中违背了单一职责原则的。 但是如果我们使用基于构造器注入的方式,因为构造器注入的写法比较臃肿,所以它就在间接提醒我们,违背了单一职责原则,该做重构了 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Component class Test { @Autowired private Bean bean1; @Autowired private Bean bean2; @Autowired private Bean bean3; @Autowired private Bean bean4; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Component class Test { private final Bean bean1; private final Bean bean2; private final Bean bean3; private final Bean bean4; @Autowired public Test(Bean bean1, Bean bean2, Bean bean3, Bean bean4) { this.bean1 = bean1; this.bean2 = bean1; this.bean3 = bean1; this.bean4 = bean1; } } 可能产生NPE 对于一个bean来说,它的初始化顺序为: ...

March 22, 2026 · 2 min · santu

@PostConstruct、init-method和afterPropertiesSet执行顺序

典型回答 在Spring框架中,使用@PostConstruct、自定义的init-method方法,和InitializingBean接口和afterPropertiesSet方法都是用于在Bean初始化阶段执行特定方法的方式。他们的执行顺序是:构造函数>@PostConstruct > afterPropertiesSet > init-method @PostConstruct 是javax.annotation 包中的注解(Spring Boot 3.0之后jakarta.annotation中,用于在构造函数执行完毕并且依赖注入完成后执行特定的初始化方法。标注在方法上,表示这个方法将在Bean初始化阶段被调用。 init-method 是在Spring配置文件(如XML文件)中配置的一种方式。通过在Bean的配置中指定 init-method 属性,可以告诉Spring在Bean初始化完成后调用指定的初始化方法。如果不使用xml文件,也可以使用 @Bean 注解的 initMethod 属性来指定初始化方法。(下面的例子就是用的这种方式) afterPropertiesSet 是 Spring 的 InitializingBean 接口中的方法。如果一个 Bean 实现了 InitializingBean 接口,Spring 在初始化阶段会调用该接口的 afterPropertiesSet 方法。 Talk is Cheap,Show me the Code ,如下是一个示例: 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 import jakarta.annotation.PostConstruct; //因为我用的是Spring Boot 3.0.如果是2.0需要改成javax.annotation.PostConstruct import org.springframework.beans.factory.InitializingBean; public class ExampleBean implements InitializingBean { private String message; public ExampleBean() { System.out.println("构造函数执行"); } @PostConstruct public void postConstructMethod() { System.out.println("@PostConstruct执行"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet执行"); } public void customInitMethod() { System.out.println("init-method执行"); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 package cn.hollis; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public class ExampleConfig { @Bean(initMethod = "customInitMethod") public ExampleBean exampleBean(){ return new ExampleBean(); } } 启动Spring: ...

March 22, 2026 · 5 min · santu

BeanFactory和FactroyBean的关系?

典型回答 FactoryBean和BeanFactory是Spring中的两个重要的概念。先看一下他们的类定义: FactoryBean: 1 2 3 4 5 6 7 8 9 10 package org.springframework.beans.factory; public interface FactoryBean<T> { T getObject() throws Exception; Class<?> getObjectType(); boolean isSingleton(); } BeanFactory: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package org.springframework.beans.factory; public interface BeanFactory { Object getBean(String name) throws BeansException; <T> T getBean(String name, Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; boolean containsBean(String name); boolean isSingleton(String name) throws NoSuchBeanDefinitionException; boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // ... } 至少从代码上来看,这两个东西都是接口(interface),然后都是在org.springframework.beans.factory包下面的。 ...

March 22, 2026 · 2 min · santu

Spring 6.0和SpringBoot 3.0有什么新特性?

Spring在2022年相继推出了Spring Framework 6.0和SpringBoot 3.0,Spring把这次升级称之为新一代框架的开始,下一个10年的新开端 主要更新内容是以下几个: A Java 17 baseline Support for Jakarta EE 10 with an EE 9 baseline Support for generating native images with GraalVM, superseding the experimental Spring Native project Ahead-Of-Time transformations and the corresponding AOT processing support for Spring application contexts 首先,前两个比较容易理解,主要说的是依赖的服务的版本升级的信息,那就是Spring Framework 6.0和SpringBoot 3.0都要求JDK的版本最低也得是JDK 17;并且底层依赖的J2EE也迁移到了Jakarta EE 9。 虽然JDK 17有很多新的特性,并且也是目前最新的一个LTS版本。 但是其实真正的使用比较多的版本还是JDK 1.8,而Spring彻底抛弃17之前的所有版本!!! AOT编译 Ahead-Of-Time,即预先编译,这是相对于我们熟知的Just-In-Time(JIT,即时编译)来说的。 相比于JIT编译,AOT指的是在程序运行前编译,这样就可以避免在运行时的编译性能消耗和内存消耗,可以在程序运行初期就达到最高性能、也可以显著的加快程序的启动。 AOT的引入,意味着Spring生态正式引入了提前编译技术,相比于JIT编译,AOT有助于优化Spring框架启动慢、占用内存多、以及垃圾无法被回收等问题。 Spring Native 在Spring的新版本中引入了Spring Native。 有了Spring Native ,Spring可以不再依赖Java虚拟机,而是基于 GraalVM 将 Spring 应用程序编译成原生镜像(native image),提供了一种新的方式来部署 Spring 应用。这种部署Spring的方式是云原生友好的。 ...

March 22, 2026 · 1 min · santu

Spring的事务传播机制有哪些?

典型回答 Spring的事务传播机制用于控制在多个事务方法相互调用时事务的行为。 在复杂的业务场景中,多个事务方法之间的调用可能会导致事务的不一致,如出现数据丢失、重复提交等问题,使用事务传播机制可以避免这些问题的发生,保证事务的一致性和完整性。 Spring的事务规定了7种事务的传播级别,默认的传播机制是**REQUIRED** REQUIRED,如果不存在事务则开启一个事务,如果存在事务则加入之前的事务,总是只有一个事务在执行 REQUIRES_NEW,每次执行新开一个事务,如果当前存在事务,则把当前事务挂起 SUPPORTS,有事务则加入事务,没有事务则普通执行 NOT_SUPPORTED,有事务则暂停该事务,没有则普通执行 MANDATORY,强制有事务,没有事务则报异常 NEVER,有事务则报异常 NESTED,如果之前有事务,则创建嵌套事务,嵌套事务回滚不影响父事务,反之父事务影响嵌套事务 扩展知识 用法 假设有两个业务方法A和B,方法A在方法B中被调用,需要在事务中保证它们的一致性,如果方法A或方法B中的任何一个方法发生异常,则需要回滚事务。 使用Spring的事务传播机制,可以在方法A和方法B上使用相同的事务管理器,并通过设置相同的传播行为来保证事务的一致性和完整性。具体实现如下: 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 @Service public class TransactionFooService { @Autowired private FooDao fooDao; @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void methodA() throws Exception { // do something fooDao.updateFoo(); } } @Service public class TransactionBarService { @Autowired private BarDao barDao; @Autowired private TransactionFooService transactionFooService; @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void methodB() throws Exception { // do something barDao.updateBar(); transactionFooService.methodA(); } } 在上述示例中,方法A和方法B都使用了REQUIRED的传播行为,表示如果当前存在事务,则在当前事务中执行;如果当前没有事务,则创建一个新的事务。如果在方法A或方法B中出现异常,则整个事务会自动回滚。 ...

March 22, 2026 · 1 min · santu

Spring在业务中常见的使用方式

典型回答 通过IOC实现策略模式 很多时候,我们需要对不同的场景进行不同的业务逻辑处理,举个例子,譬如不同的场景需要不同支付方式,普通的逻辑是使用if-else,如下所示: 1 2 3 4 5 6 7 8 9 public void use(Scene scene) { if(scene == TENCENT) { doWeiXinPay(); } else if (scene == ALIBABA) { doAlipay(); } else { doDothing(); } } 如果sence越来越多,这种if-else显然非常不合适,就需要我们借助Spring来完成策略模式 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 interface PayFacade extends InitializingBean { void pay(); Scene getSupportScene(); @Override default void afterPropertiesSet() throws Exception { PayFactory.register(getSupportScene(), this); } } @Component class WeiXinPay implements PayFacade { @Override public void pay() { // by weixin } @Override public Scene getSupportScene() { return Scene.TENCENT; } } @Component class AliPay implements PayFacade { @Override public void pay() { // by alipay } @Override public Scene getSupportScene() { return Scene.ALIBABA; } } class PayFactory { private static final Map<Scene, PayFacade> PAY_FACADE = Maps.newHashMap(); public static void register(Scene scene, PayFacade payFacade) { PAY_FACADE.put(scene, payFacade); } public static PayFacade get(Scene scene) { return PAY_FACADE.get(scene); } } public void use(Scene scene) { PayFactory.get(scene); } 这样子,调用方只需要调用PayFactory#get即可,不需要感知内部的实现细节和逻辑。 ...

March 22, 2026 · 3 min · santu

Spring事务失效可能是哪些原因?

典型回答 Spring中比较容易失效的就是通过@Transactional 定义的声明式事务,他在以下几个场景中会导致事务失效,首先,就是Spring的@Transactional是基于Spring的AOP机制实现的,而AOP机制又是基于动态代理实现的。那么如果代理失效了,事务也就会失效。 代理失效的情况 ✅Spring的AOP在什么场景下会失效? 在上面这篇中介绍过几种代理失效的情况,主要就是分为类的内部调用、final方法、static方法等。 所以就是以下几种情况就会失效: 1、@Transactional 应用在非 public 修饰的方法上 1 2 3 4 5 6 public class MyService { @Transactional private void doInternal() { System.out.println("Doing internal work..."); } } private方法,只会在当前对象中的其他方法中调用,也就是会进行对象的自调用,这种情况是用this调用的,并不会走到代理对象,而@Transactional是基于动态代理实现的,所以代理会失效。 2、同一个类中方法调用,导致@Transactional失效 1 2 3 4 5 6 7 8 9 10 public class MyService { public void doSomething() { doInternal(); // 自调用方法 } @Transactional public void doInternal() { System.out.println("Doing internal work..."); } } 以上,和private是一回事,因为没办法走到被代理的服务,所以事务会失效。 ...

March 22, 2026 · 2 min · santu

Spring Boot 如何让你的 bean 在其他 bean 之前加载

典型回答 面对这个问题的时候,读者朋友们可能会有点懵逼,但是我们回到Bean初始化的本质上来看,Bean初始化有两个时机: Spring容器主动去初始化该Bean 其他Bean依赖该Bean,该Bean会先被初始化 从这两个出发点来思考解决问题方案的话,大概有如下几种方式: 直接依赖某Bean 如下代码所示: 1 2 3 4 5 6 @Component public class A { @Autowired private B b; } 如上,在加载Bean A的时候,一定会先初始化Bean B DependsOn 对于应用之外的二方或者三方库来说,因为我们不能修改外部库的代码,如果想要二方库的Bean在初始化之前就初始化我们内部的某个bean,就不能用第一种直接依赖的方式,可以使用@DependsOn注解来完成,如下代码所示: 1 2 3 4 5 6 7 8 9 @Configuration public class BeanOrderConfiguration { @Bean @DependsOn("beanB") public BeanA beanA(){ return new BeanA(); } } 当然,DependsOn注解也可以作用在@Component注解上面。 一方库:由你自己或你的团队开发并使用。 二方库:由与你有直接合作关系的合作伙伴开发,通常用于双方的合作项目中。 三方库:由独立于你和你公司以外的第三方开发,通常是公开可用的,广泛用于多个项目中 BeanFactoryPostProcessor 前两种方式只能对于特定的Bean生效,如果我们希望某个Bean在其他所有Bean加载之前就初始化,用前面两种方式显然是不合适的,我们这个时候,就需要从Spring容器的生命周期中去找方法。 ✅SpringBean的初始化流程 通过上面的参考文章我们可以知道,Spring的Bean在初始化之前,会通过BeanFactoryPostProcessor#postProcessBeanFactory对工厂进行处理,我们可以依赖这个特性,在此刻提前初始化我们需要的bean 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Component public class PrimaryBeanProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { PrimaryBean bean = beanFactory.getBean(PrimaryBean.class); System.out.println(bean); } } @Component public class PrimaryBean { public PrimaryBean() { System.out.println("init primary bean"); } @Override public String toString() { return "PrimaryBean{aaa}"; } } 这个时候我们通过控制台发现,PrimaryBean的初始化等级会优于其他Bean,如下所示: ...

March 22, 2026 · 2 min · santu

留言给博主