9人参与 • 2025-06-10 • Java
平常开发中有可能需要实现在项目启动后执行的功能,springboot提供的一种简单的实现方案就是添加一个model并实现commandlinerunner
接口,实现功能的代码放在实现的run
方法中。也就是项目一启动之后,就立即需要执行的动作。只需要在项目里面简单的配置,就可以实现这个功能。
简单例子
import org.springframework.boot.commandlinerunner; import org.springframework.stereotype.component; @component public class mystartuprunner implements commandlinerunner { @override public void run(string... args) throws exception { system.out.println("项目已经启动"); } }
通过实现ordered
接口实现控制执行顺序
import lombok.extern.slf4j.slf4j; import org.springframework.boot.commandlinerunner; import org.springframework.core.ordered; import org.springframework.stereotype.component; /** * 优先级最高 * 该类期望在springboot 启动后第一顺位执行 * @since 12:57 **/ @slf4j @component public class highordercommandlinerunner implements commandlinerunner, ordered { @override public void run(string... args) throws exception { for (string arg : args) { log.info("arg = " + arg); } log.info("i am highorderrunner"); } @override public int getorder() { return integer.min_value+1; } }
import lombok.extern.slf4j.slf4j; import org.springframework.boot.commandlinerunner; import org.springframework.core.ordered; import org.springframework.stereotype.component; /** * 优先级低于{@code highordercommandlinerunner} * @since 12:59 **/ @slf4j @component public class lowordercommandlinerunner implements commandlinerunner, ordered { @override public void run(string... args) throws exception { log.info("i am loworderrunner"); } @override public int getorder() { return integer.min_value+1; } }
启动spring boot程序后,控制台按照预定的顺序打印出了结果:
2020-05-30 23:11:03.685 info 11976 --- [ main] o.s.b.w.embedded.tomcat.tomcatwebserver : tomcat started on port(s): 8080 (http) with context path ''
2020-05-30 23:11:03.701 info 11976 --- [ main] c.f.application : started springbootapplication in 4.272 seconds (jvm running for 6.316)
2020-05-30 23:11:03.706 info 11976 --- [ main] c.f.highordercommandlinerunner : i am highorderrunner
2020-05-30 23:11:03.706 info 11976 --- [ main] c.f.lowordercommandlinerunner : i am loworderrunner
通过@order
注解实现控制执行顺序
springboot在项目启动后会遍历所有实现commandlinerunner
的实体类并执行run
方法,如果需要按照一定的顺序去执行,那么就需要在实体类上使用一个@order
注解(或者实现order
接口)来表明顺序
import org.springframework.boot.commandlinerunner; import org.springframework.core.annotation.order; import org.springframework.stereotype.component; @component @order(value=2) public class mystartuprunner1 implements commandlinerunner { @override public void run(string... args) throws exception { system.out.println("执行2"); } }
import org.springframework.boot.commandlinerunner; import org.springframework.core.annotation.order; import org.springframework.stereotype.component; @component @order(value=1) public class mystartuprunner2 implements commandlinerunner { @override public void run(string... args) throws exception { system.out.println("执行1"); } }
控制台显示
执行1
执行2
根据控制台结果可判断,
@order
注解的执行优先级是按value值从小到大顺序。
@order
作用
项目启动之后,要执行的动作是比较的多,那么到底先执行哪个,那么就可以利用这个注解限定优先级。 :::danger ordered
接口并不能被 @order
注解所代替。
在spring boot 1.3.0又引入了一个和commandlinerunner
功能一样的接口applicationrunner
。commandlinerunner
接收可变参数string... args
,而applicationrunner
接收一个封装好的对象参数applicationarguments
。除此之外它们功能完全一样,甚至连方法名都一样。声明一个applicationrunner
并让它优先级最低:
import lombok.extern.slf4j.slf4j; import org.springframework.boot.applicationarguments; import org.springframework.boot.applicationrunner; import org.springframework.core.ordered; import org.springframework.stereotype.component; import java.util.arrays; import java.util.list; import java.util.set; /** * 优先级最低 **/ @slf4j @component public class defaultapplicationrunner implements applicationrunner, ordered { @override public void run(applicationarguments args) throws exception { log.info("i am applicationrunner"); set<string> optionnames = args.getoptionnames(); log.info("optionnames = " + optionnames); string[] sourceargs = args.getsourceargs(); log.info("sourceargs = " + arrays.tostring(sourceargs)); list<string> nonoptionargs = args.getnonoptionargs(); log.info("nonoptionargs = " + nonoptionargs); list<string> optionvalues = args.getoptionvalues("foo"); log.info("optionvalues = " + optionvalues); } @override public int getorder() { return integer.min_value+2; } }
按照顺序打印了三个类的执行结果:
2020-06-01 13:02:39.420 info 19032 --- [ main] c.f.mybatisresultmapapplication : started mybatisresultmapapplication in 1.801 seconds (jvm running for 2.266)
2020-06-01 13:02:39.423 info 19032 --- [ main] c.f.highordercommandlinerunner : i am highorderrunner
2020-06-01 13:02:39.423 info 19032 --- [ main] c.f.lowordercommandlinerunner : i am loworderrunner
2020-06-01 13:02:39.423 info 19032 --- [ main] c.f.defaultapplicationrunner : i am applicationrunner
2020-06-01 13:02:39.423 info 19032 --- [ main] c.f.defaultapplicationrunner : optionnames = []
2020-06-01 13:02:39.423 info 19032 --- [ main] c.f.defaultapplicationrunner : sourceargs = []
2020-06-01 13:02:39.423 info 19032 --- [ main] c.f.defaultapplicationrunner : nonoptionargs = []
2020-06-01 13:02:39.423 info 19032 --- [ main] c.f.defaultapplicationrunner : optionvalues = null
optionvalues = null
ordered
接口并不能被@order
注解所代替。
spring boot应用启动时是可以接受参数的,换句话说也就是spring boot
的main
方法是可以接受参数的。这些参数通过命令行 java -jar yourapp.jar
来传递。commandlinerunner
会原封不动照单全收这些接口,这些参数也可以封装到applicationarguments
对象中供applicationrunner
调用。看一下applicationarguments
的相关方法:
可以通过下面的命令运行一个 spring boot应用 jar
java -jar yourapp.jar --foo=bar --foo=baz --dev.name=fcant java fcantcn
或者在idea开发工具中打开spring boot应用main
方法的配置项,进行命令行参数的配置,其他ide工具同理。
运行spring boot应用,将会打印出:
2020-06-01 15:04:31.490 info 13208 --- [ main] c.f.highordercommandlinerunner : arg = --foo=bar
2020-06-01 15:04:31.490 info 13208 --- [ main] c.f.highordercommandlinerunner : arg = --foo=baz
2020-06-01 15:04:31.490 info 13208 --- [ main] c.f.highordercommandlinerunner : arg = --dev.name=fcant
2020-06-01 15:04:31.490 info 13208 --- [ main] c.f.highordercommandlinerunner : arg = java
2020-06-01 15:04:31.490 info 13208 --- [ main] c.f.highordercommandlinerunner : arg = fcantcn
2020-06-01 15:04:31.491 info 13208 --- [ main] c.f.highordercommandlinerunner : i am highorderrunner
2020-06-01 15:04:31.491 info 13208 --- [ main] c.f.lowordercommandlinerunner : i am loworderrunner
2020-06-01 15:04:31.491 info 13208 --- [ main] c.f.defaultapplicationrunner : i am applicationrunner
2020-06-01 15:04:31.491 info 13208 --- [ main] c.f.defaultapplicationrunner : optionnames = [dev.name, foo]
2020-06-01 15:04:31.491 info 13208 --- [ main] c.f.defaultapplicationrunner : sourceargs = [--foo=bar, --foo=baz, --dev.name=fcant, java, fcantcn]
2020-06-01 15:04:31.491 info 13208 --- [ main] c.f.defaultapplicationrunner : nonoptionargs = [java, fcantcn]
2020-06-01 15:04:31.491 info 13208 --- [ main] c.f.defaultapplicationrunner : optionvalues = [bar, baz]
然后就可以根据实际需要动态地执行一些逻辑。
通过源码理解一下底层实现。
run()
方法
跟进run
方法后,一路f6直达以下方法
public configurableapplicationcontext run(string... args) { stopwatch stopwatch = new stopwatch(); //设置线程启动计时器 stopwatch.start(); configurableapplicationcontext context = null; collection<springbootexceptionreporter> exceptionreporters = new arraylist<>(); //配置系统属性:默认缺失外部显示屏等允许启动 configureheadlessproperty(); //获取并启动事件监听器,如果项目中没有其他监听器,则默认只有eventpublishingrunlistener springapplicationrunlisteners listeners = getrunlisteners(args); //将事件广播给listeners listeners.starting(); try { //对于实现applicationrunner接口,用户设置applicationarguments参数进行封装 applicationarguments applicationarguments = new defaultapplicationarguments( args); //配置运行环境:例如激活应用***.yml配置文件 configurableenvironment environment = prepareenvironment(listeners, applicationarguments); configureignorebeaninfo(environment); //加载配置的banner(gif,txt...),即控制台图样 banner printedbanner = printbanner(environment); //创建上下文对象,并实例化 context = createapplicationcontext(); exceptionreporters = getspringfactoriesinstances( springbootexceptionreporter.class, new class[] { configurableapplicationcontext.class }, context); //配置spring容器 preparecontext(context, environment, listeners, applicationarguments, printedbanner); //刷新spring上下文,创建bean过程中 refreshcontext(context); //空方法,子类实现 afterrefresh(context, applicationarguments); //停止计时器:计算线程启动共用时间 stopwatch.stop(); if (this.logstartupinfo) { new startupinfologger(this.mainapplicationclass) .logstarted(getapplicationlog(), stopwatch); } //停止事件监听器 listeners.started(context); //开始加载资源 callrunners(context, applicationarguments); } catch (throwable ex) { handlerunfailure(context, listeners, exceptionreporters, ex); throw new illegalstateexception(ex); } listeners.running(context); return context; }
主要是熟悉springboot的commandlinerunner
接口实现原理。因此上面springboot启动过程方法不做过多介绍。直接进入callrunners()
方法内部。
callrunners
方法
private void callrunners(applicationcontext context, applicationarguments args) { //将实现applicationrunner和commandlinerunner接口的类,存储到集合中 list<object> runners = new arraylist<>(); runners.addall(context.getbeansoftype(applicationrunner.class).values()); runners.addall(context.getbeansoftype(commandlinerunner.class).values()); //按照加载先后顺序排序 annotationawareordercomparator.sort(runners); for (object runner : new linkedhashset<>(runners)) { if (runner instanceof applicationrunner) { callrunner((applicationrunner) runner, args); } if (runner instanceof commandlinerunner) { callrunner((commandlinerunner) runner, args); } } }
private void callrunner(commandlinerunner runner, applicationarguments args) { try { //调用各个实现类中的逻辑实现 (runner).run(args.getsourceargs()); } catch (exception ex) { throw new illegalstateexception("failed to execute commandlinerunner", ex); } }
到此结束,再跟进run()
方法,就可以看到资源加载逻辑。
到此这篇关于springboot 中 commandlinerunner的作用的文章就介绍到这了,更多相关springboot 中 commandlinerunner内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论