<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[Heck's  Blog]]></title> 
<link>https://www.heckjj.com/index.php</link> 
<description><![CDATA[一瞬间的决定，往往可以改变很多，事实上，让自己成功的往往不是知识，是精神！ 如果你总是为自己找借口，那只好让成功推迟。执行力，今天！]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[Heck's  Blog]]></copyright>
<item>
<link>https://www.heckjj.com/post//</link>
<title><![CDATA[spring boot启动后自动执行代码方式汇总]]></title> 
<author>Heck &lt;@hecks.tk&gt;</author>
<category><![CDATA[编程杂谈]]></category>
<pubDate>Thu, 23 Nov 2023 06:27:23 +0000</pubDate> 
<guid>https://www.heckjj.com/post//</guid> 
<description>
<![CDATA[ 
	在实际项目开发过程中，我们有时候需要让项目在启动时执行特定方法。如要实现这些功能：<br/>提前加载相应的数据到缓存中；<br/>检查当前项目运行环境；<br/>检查程序授权信息，若未授权则不能使用后续功能；<br/>执行某个特定方法；<br/><br/>一直想整理一下Springboot启动后自执行某段代码或者方法相关的点，囿于时间问题及担心理解不是太全面所以没整理，后来一想先整理出来，将这一方面的东西记录下来。其实这篇文章不应该体现spring boot，因为自动执行方式不仅限于spring boot，java spring细化到bean的初始化都可以完成。<br/><br/>一、java自身的启动时加载方式<br/><br/>1、static代码块<br/>static静态代码块，在类加载的时候即自动执行。 由于静态代码块没有名字，我们并不能主动调用，它会在类加载的时候，自动执行。所以静态代码块，可以更早的给类中的静态属性，进行初始化赋值操作。并且，静态代码块只会自动被执行一次，因为JVM在一次运行中，对一个类只会加载一次！<br/><br/>2、构造函数constructor<br/>在对象初始化时执行。执行顺序在static静态代码块之后。<br/><br/>3、@PostConstruct注解<br/>@PostConstruct<br/>public void testInit() &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;System.out.printin(&quot;postConstruct“);<br/>&#125;<br/>@PostConstruct注解使用在方法上，这个方法在对象依赖注入初始化之后执行。执行节点在BeanPostProcessor的postProcessBeforeInitialization之后，在postProcessAfterInitialization之前。<br/>很多人以为该注解是Spring提供的。其实是Java自己的注解。Java中该注解的说明：@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行，并且只会被服务器执行一次。PostConstruct在构造函数之后执行，init()方法之前执行。<br/>注意：加了postconstruct注解的方法，如果执行失败，整个程序会无法正常启动！这个方法执行不完，整个程序也启动不了！也不建议将耗时逻辑放到这里面来。<br/><br/>二、Servlet加载的方式<br/>1、实现ServletContextListener接口contextInitialized方法<br/>import lombok.extern.slf4j.Slf4j;<br/>import org.springframework.stereotype.Component;<br/>import javax.servlet.ServletContextEvent;<br/>import javax.servlet.ServletContextListener;<br/><br/>@Slf4j<br/>@Component<br/>public class ServletContextListenerImpl implements ServletContextListener &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;/**<br/>&nbsp;&nbsp;&nbsp;&nbsp; * 在初始化Web应用程序中的任何过滤器或Servlet之前，将通知所有ServletContextListener上下文初始化。<br/>&nbsp;&nbsp;&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;@Override<br/>&nbsp;&nbsp;&nbsp;&nbsp;public void contextInitialized(ServletContextEvent sce) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(&quot;启动时自动执行 ServletContextListener 的 contextInitialized 方法&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/>注意：该方法会在填充完普通Bean的属性，但是还没有进行Bean的初始化之前执行<br/><br/>三、Spring启动时加载方式<br/>1、实现ServletContextAware接口setServletContext 方法<br/>@Component<br/>public class StartInitServletContext implements ServletContextAware &#123;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;@Override<br/>&nbsp;&nbsp;&nbsp;&nbsp;public void setServletContext(ServletContext servletContext) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&quot;StartInitServletContext: 开始处理事情&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>2、ApplicationListener监听器<br/>创建Listener的方式有两种：<br/>方式1：编程实现ApplicationListener接口<br/>方式2：使用@EventListener注解<br/>注意监听的事件，通常是ApplicationStartedEvent 或者ApplicationReadyEvent，其他的事件可能无法注入bean。<br/><br/>编程实现ApplicationListener接口<br/>/**<br/> * 监听的是 ApplicationStartedEvent 事件，<br/> * 则 ApplicationListener 一定会在 CommandLineRunner 和 ApplicationRunner 之前执行；<br/> */<br/>@Component<br/>public class StartInitStartedEvent implements ApplicationListener&lt;ApplicationStartedEvent&gt; &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;@Override<br/>&nbsp;&nbsp;&nbsp;&nbsp;public void onApplicationEvent(ApplicationStartedEvent event) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&quot;StartInitStartedEvent：开始处理事情&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/>/**<br/> * 监听的是 ApplicationReadyEvent 事件，<br/> * 则 ApplicationListener 一定会在 CommandLineRunner 和 ApplicationRunner 之后执行；<br/> */<br/>@Component<br/>public class StartInitReadyEvent implements ApplicationListener&lt;ApplicationReadyEvent&gt; &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;@Override<br/>&nbsp;&nbsp;&nbsp;&nbsp;public void onApplicationEvent(ApplicationReadyEvent event) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&quot;StartInitReadyEvent: 开始处理事情&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/>实现 public interface ApplicationListener&lt;E extends ApplicationEvent&gt; 接口，监听 ApplicationEvent 及其下面的子事件：<br/>/**<br/> * 事件监听器一般是由开发者自己定义<br/> * 定义事件监听器<br/> */<br/>@Component<br/>//@Lazy<br/>public class MyApplicationListener implements ApplicationListener&#123;<br/> <br/>&nbsp;&nbsp;@Override<br/>&nbsp;&nbsp;public void onApplicationEvent(ApplicationEvent event) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;event = (PayloadApplicationEvent)event;<br/>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(((PayloadApplicationEvent&lt;?&gt;) event).getPayload());<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>@EventListener方式<br/>将要执行的方法所在的类交个Spring容器扫描(@Component),并且在要执行的方法上添加@EventListener注解执行<br/>import lombok.extern.slf4j.Slf4j;<br/>import org.springframework.context.event.ContextRefreshedEvent;<br/>import org.springframework.context.event.EventListener;<br/>import org.springframework.stereotype.Component;<br/><br/>@Slf4j<br/>@Component<br/>public class EventListenerTest &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;@EventListener<br/>&nbsp;&nbsp;&nbsp;&nbsp;public void onApplicationEvent(ContextRefreshedEvent event) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(&quot;启动时自动执行&nbsp;&nbsp;@EventListener 注解方法&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>3、InitializingBean接口<br/>@Component<br/>@Data<br/>public class TestInit implements InitializingBean,eanFactoryAware，ApplicationContextAware&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private BeanFactory beanFactory:<br/>&nbsp;&nbsp;&nbsp;&nbsp;private ApplicationContext applicationContext;<br/>&nbsp;&nbsp;&nbsp;&nbsp;private String name;<br/>@Override<br/>public voidafterPropertiesSet(hrows Exception &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;System.out.printin(&quot;InIt heck&quot;);<br/> &#125;<br/>&#125;<br/>Spring为bean提供了两种初始化bean的方式，实现InitializingBean接口，实现afterPropertiesSet方法，或者在配置文件中通过init-method指定，两种方式可以同时使用。<br/>实现InitializingBean接口是直接调用afterPropertiesSet方法，比通过反射调用init-method指定的方法效率要高一点，但是init-method方式消除了对spring的依赖。先调用afterPropertieSet()方法，然后再调用init-method中指定的方法。<br/><br/>4、ApplicationRunner和CommandLineRunner<br/>SpringBoot提供了两个接口来实现Spring容器启动完成后执行的功能，两个接口分别为CommandLineRunner和ApplicationRunner。这两个接口需要实现一个run方法，将代码在run中实现即可。这两个接口功能基本一致，其区别在于run方法的入参。ApplicationRunner的run方法入参为ApplicationArguments，为CommandLineRunner的run方法入参为String数组。<br/>当有多个类实现了CommandLineRunner和ApplicationRunner接口时，可以通过在类上添加@Order注解来设定运行顺序。<br/>注意:使用ApplicationRunner或者CommandLineRunner时如果报错或者出现超时的情况会导致整个程序崩溃。可以将run内的逻辑单独开一个线程使用。<br/>import lombok.extern.slf4j.Slf4j;<br/>import org.springframework.boot.ApplicationArguments;<br/>import org.springframework.boot.ApplicationRunner;<br/>import org.springframework.stereotype.Component;<br/>import java.util.Set;<br/><br/>@Slf4j<br/>@Component<br/>public class ApplicationRunnerImpl implements ApplicationRunner &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;/**<br/>&nbsp;&nbsp;&nbsp;&nbsp; * 用于指示bean包含在SpringApplication中时应运行的接口。可以定义多个ApplicationRunner bean<br/>&nbsp;&nbsp;&nbsp;&nbsp; * 在同一应用程序上下文中，可以使用有序接口或@order注释对其进行排序。<br/>&nbsp;&nbsp;&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;@Override<br/>&nbsp;&nbsp;&nbsp;&nbsp;public void run(ApplicationArguments args) throws Exception &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(&quot;启动时自动执行 ApplicationRunner 的 run 方法&quot;);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set&lt;String&gt; optionNames = args.getOptionNames();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (String optionName : optionNames) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(&quot;这是传过来的参数[&#123;&#125;]&quot;, optionName);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String[] sourceArgs = args.getSourceArgs();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (String sourceArg : sourceArgs) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(&quot;这是传过来sourceArgs[&#123;&#125;]&quot;, sourceArg);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>通过实现Ordered接口并重写getOrder方法实现，数字越小越先执行<br/><br/>import lombok.extern.slf4j.Slf4j;<br/>import org.springframework.boot.CommandLineRunner;<br/>import org.springframework.stereotype.Component;<br/><br/>@Slf4j<br/>@Component<br/>public class CommandLineRunnerImpl implements CommandLineRunner &#123;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;@Override<br/>&nbsp;&nbsp;&nbsp;&nbsp;public void run(String... args) throws Exception &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(&quot;启动时自动执行 CommandLineRunner 的 run 方法&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/>总结一下：<br/>Spring应用启动过程中，肯定是要自动扫描有@Component注解的类，加载类并初始化对象进行自动注入。加载类时首先要执行static静态代码块中的代码，之后再初始化对象时会执行构造方法。最新 Spring Boot 面试题整理好了，点击Java面试库小程序在线刷题。<br/>在对象注入完成后，调用带有@PostConstruct注解的方法。当容器启动成功后，再根据@Order注解的顺序调用CommandLineRunner和ApplicationRunner接口类中的run方法。<br/>说明：<br/>　　1）默认执行顺序ApplicationContextInitializer、ApplicationRunner、CommandLineRunner<br/>　　2）ApplicationRunner与CommandLineRunner可通过@Order改变顺序使CommandLineRunner早于ApplicationRunner执行<br/>　　3）两个Runner的区别：CommandLineRunner参数是原始的，ApplicationRunner是对原始参数的封装<br/><br/>执行顺序为static&gt;constructor&gt;初始化前&gt;ServletContextListener&gt;@PostConstruct&gt;ServletContextAware&gt;@EventListener&gt;InitializingBean&gt;初始化后&gt;ApplicationRunner和CommandLineRunner。<br/>
]]>
</description>
</item><item>
<link>https://www.heckjj.com/post//#blogcomment</link>
<title><![CDATA[[评论] spring boot启动后自动执行代码方式汇总]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>https://www.heckjj.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>