在咱们用 springboot 搭建项目的时候,有时候会碰到在项目启动时初始化一些操做的需求 ,针对这种需求 spring boot为咱们提供了如下几种方案供咱们选择:html
ApplicationRunner
与 CommandLineRunner
接口java
Spring容器初始化时InitializingBean接口和@PostConstruct
web
Spring的事件机制spring
咱们能够实现 ApplicationRunner
或 CommandLineRunner
接口, 这两个接口工做方式相同,都只提供单一的run方法,该方法在SpringApplication.run(…)完成以前调用,不知道你们还对我上一篇文章结尾有没有印象,咱们先来看看这两个接口springboot
public interface ApplicationRunner { void run(ApplicationArguments var1) throws Exception; } public interface CommandLineRunner { void run(String... var1) throws Exception; }
都只提供单一的run方法,接下来咱们来看看具体的使用app
构造一个类实现ApplicationRunner接口框架
//须要加入到Spring容器中 @Component public class ApplicationRunnerTest implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner"); } }
很简单,首先要使用@Component将实现类加入到Spring容器中,为何要这样作咱们待会再看,而后实现其run方法实现本身的初始化数据逻辑就能够了ide
对于这两个接口而言,咱们能够经过Order注解或者使用Ordered接口来指定调用顺序, @Order()
中的值越小,优先级越高源码分析
//须要加入到Spring容器中 @Component @Order(1) public class CommandLineRunnerTest implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("CommandLineRunner..."); } }
一样须要加入到Spring容器中,CommandLineRunner的参数是最原始的参数,没有进行任何处理,ApplicationRunner的参数是ApplicationArguments,是对原始参数的进一步封装post
你们回顾一下我上一篇文章,也就是SpringApplication.run方法的最后一步第八步:执行Runners,这里我直接把代码复制过来
private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<Object>(); //获取容器中全部的ApplicationRunner的Bean实例 runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); //获取容器中全部的CommandLineRunner的Bean实例 runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<Object>(runners)) { if (runner instanceof ApplicationRunner) { //执行ApplicationRunner的run方法 callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { //执行CommandLineRunner的run方法 callRunner((CommandLineRunner) runner, args); } } }
很明显,是直接从Spring容器中获取ApplicationRunner和CommandLineRunner的实例,并调用其run方法,这也就是为何我要使用@Component将ApplicationRunner和CommandLineRunner接口的实现类加入到Spring容器中了。
在spring初始化bean的时候,若是bean实现了 InitializingBean
接口,在对象的全部属性被初始化后以后才会调用afterPropertiesSet()方法
@Component public class InitialingzingBeanTest implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean.."); } }
咱们能够看出spring初始化bean确定会在 ApplicationRunner和CommandLineRunner接口调用以前。
@Component public class PostConstructTest { @PostConstruct public void postConstruct() { System.out.println("init..."); } }
咱们能够看到,只用在方法上添加@PostConstruct注解,并将类注入到Spring容器中就能够了。咱们来看看@PostConstruct注解的方法是什么时候执行的
在Spring初始化bean时,对bean的实例赋值时,populateBean方法下面有一个initializeBean(beanName, exposedObject, mbd)方法,这个就是用来执行用户设定的初始化操做。咱们看下方法体:
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { // 激活 Aware 方法 invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { // 对特殊的 bean 处理:Aware、BeanClassLoaderAware、BeanFactoryAware invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // 后处理器 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // 激活用户自定义的 init 方法 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { // 后处理器 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
咱们看到会先执行后处理器而后执行invokeInitMethods方法,咱们来看下applyBeanPostProcessorsBeforeInitialization
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { result = beanProcessor.postProcessBeforeInitialization(result, beanName); if (result == null) { return result; } } return result; } public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
获取容器中全部的后置处理器,循环调用后置处理器的postProcessBeforeInitialization方法,这里咱们来看一个BeanPostProcessor
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable { public CommonAnnotationBeanPostProcessor() { this.setOrder(2147483644); //设置初始化参数为PostConstruct.class this.setInitAnnotationType(PostConstruct.class); this.setDestroyAnnotationType(PreDestroy.class); this.ignoreResourceType("javax.xml.ws.WebServiceContext"); } //略... }
在构造器中设置了一个属性为PostConstruct.class,再次观察CommonAnnotationBeanPostProcessor这个类,它继承自InitDestroyAnnotationBeanPostProcessor。InitDestroyAnnotationBeanPostProcessor顾名思义,就是在Bean初始化和销毁的时候所做的一个前置/后置处理器。查看InitDestroyAnnotationBeanPostProcessor类下的postProcessBeforeInitialization方法:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass()); try { metadata.invokeInitMethods(bean, beanName); } catch (InvocationTargetException ex) { throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException()); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Couldn't invoke init method", ex); } return bean; } private LifecycleMetadata buildLifecycleMetadata(final Class clazz) { final LifecycleMetadata newMetadata = new LifecycleMetadata(); final boolean debug = logger.isDebugEnabled(); ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() { public void doWith(Method method) { if (initAnnotationType != null) { //判断clazz中的methon是否有initAnnotationType注解,也就是PostConstruct.class注解 if (method.getAnnotation(initAnnotationType) != null) { //若是有就将方法添加进LifecycleMetadata中 newMetadata.addInitMethod(method); if (debug) { logger.debug("Found init method on class [" + clazz.getName() + "]: " + method); } } } if (destroyAnnotationType != null) { //判断clazz中的methon是否有destroyAnnotationType注解 if (method.getAnnotation(destroyAnnotationType) != null) { newMetadata.addDestroyMethod(method); if (debug) { logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method); } } } } }); return newMetadata; }
在这里会去判断某方法是否有PostConstruct.class注解,若是有,则添加到init/destroy队列中,后续一一执行。@PostConstruct注解的方法会在此时执行,咱们接着来看invokeInitMethods
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { // 是否实现 InitializingBean // 若是实现了 InitializingBean 接口,则只掉调用bean的 afterPropertiesSet() boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isDebugEnabled()) { logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { // 直接调用 afterPropertiesSet() ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null && bean.getClass() != NullBean.class) { // 判断是否指定了 init-method(), // 若是指定了 init-method(),则再调用制定的init-method String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { // 利用反射机制执行 invokeCustomInitMethod(beanName, bean, mbd); } } }
首先检测当前 bean 是否实现了 InitializingBean 接口,若是实现了则调用其 afterPropertiesSet()
,而后再检查是否也指定了 init-method()
,若是指定了则经过反射机制调用指定的 init-method()
。
咱们也能够发现@PostConstruct会在实现 InitializingBean 接口的afterPropertiesSet()方法以前执行
Spring的事件驱动模型由三部分组成
ApplicationEvent
,继承自JDK的 EventObject
,全部事件都要继承它,也就是被观察者ApplicationEventPublisher
及 ApplicationEventMulticaster
接口,使用这个接口,就能够发布事件了ApplicationListener
,继承JDK的 EventListener
,全部监听者都继承它,也就是咱们所说的观察者,固然咱们也可使用注解 @EventListener
,效果是同样的在Spring框架中,默认对ApplicationEvent事件提供了以下支持:
构造一个类继承ApplicationEvent
public class TestEvent extends ApplicationEvent { private String message; public TestEvent(Object source) { super(source); } public void getMessage() { System.out.println(message); } public void setMessage(String message) { this.message = message; } }
有两种方法能够建立监听者,一种是直接实现ApplicationListener的接口,一种是使用注解 @EventListener
, 注解是添加在监听方法上的 ,下面的例子是直接实现的接口
@Component public class ApplicationListenerTest implements ApplicationListener<TestEvent> { @Override public void onApplicationEvent(TestEvent testEvent) { testEvent.getMessage(); } }
对于事件发布,表明者是 ApplicationEventPublisher
和 ApplicationEventMulticaster
,ApplicationContext接口继承了ApplicationEventPublisher,并在AbstractApplicationContext实现了具体代码,实际执行是委托给ApplicationEventMulticaster(能够认为是多播)
下面是一个事件发布者的测试实例:
@RunWith(SpringRunner.class) @SpringBootTest public class EventTest { @Autowired private ApplicationContext applicationContext; @Test public void publishTest() { TestEvent testEvent = new TestEvent(""); testEvent.setMessage("hello world"); applicationContext.publishEvent(testEvent); } }
利用 ContextRefreshedEvent
事件进行初始化,该事件是 ApplicationContext
初始化完成后调用的事件,因此咱们能够利用这个事件,对应实现一个 监听器 ,在其 onApplicationEvent()
方法里初始化操做
@Component public class ApplicationListenerTest implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { System.out.println("容器刷新完成后,我被调用了.."); } }