前言
2020/12/3 14:41.周五. 美好的周末要来临了,今天公司没什么业务需要我做出处理。那么就简单的来聊一下标题提及的,springboot捕获应用上下文以及如何加载第三方bean的问题。
springboot捕获应用上下文
之前在聊起来spring的观察者模式的时候,大家还记得事件是怎么进行推送的吗? applicationContext.pushEvent(XXX); 这里的applicationContext,这个Bean我们是直接注入的。
这样其实没有什么问题,但是这样直接注入其实不太好,因为你并不确定什么时候捕获到了这个上下文对象。那怎么确定呢?当这个上下文在被springboot加载的时候,尝试使用监听器将其捕获下。首先来实现下spring的监听器接口:
package com.cmdc.utils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import java.util.LinkedList;
@Slf4j
public class XyatticSpringApplicationRunListener implements SpringApplicationRunListener {
private static final LinkedList<ConfigurableApplicationContext> APPLICATION_CONTEXTS = Lists.newLinkedList();
public XyatticSpringApplicationRunListener(final SpringApplication application, final String[] args) {
}
@Override
public void starting() {
}
@Override
public void environmentPrepared(final ConfigurableEnvironment environment) {
}
@Override
public void contextPrepared(final ConfigurableApplicationContext context) {
}
@Override
public void contextLoaded(final ConfigurableApplicationContext context) {
log.info("we get applicationContext,is:{}",context);
APPLICATION_CONTEXTS.add(context);
}
@Override
public void started(final ConfigurableApplicationContext context) {
}
@Override
public void running(final ConfigurableApplicationContext context) {
}
@Override
public void failed(final ConfigurableApplicationContext context, final Throwable exception) {
}
public static ConfigurableApplicationContext getApplicationContext() {
if (APPLICATION_CONTEXTS.isEmpty()) {
return null;
}
ConfigurableApplicationContext applicationContext = APPLICATION_CONTEXTS.getLast();
try {
applicationContext.getAutowireCapableBeanFactory();
} catch (IllegalStateException e) {
APPLICATION_CONTEXTS.removeLast();
return getApplicationContext();
}
return applicationContext;
}
}
复制代码
注意,咱们在contextLoaded,即上下文已经加载的情况下将其添加到集合中,这个集合是一个静态的全局变量,不属于类的任何实例,而是属于这个类。
接着,注册这个监听器,怎么注册呢,利用下spring.factories文件。 在resources目录下新建MATA-INF文件夹,然后在里面新建spring.factories文件
内容如下:
org.springframework.boot.SpringApplicationRunListener=com.cmdc.utils.XyatticSpringApplicationRunListener
复制代码
ok的,接下来启动下项目试一试。
ok,看来contextLoadeed方法执行成功。并且还知道了,实际的applicationContext对象是configurableApplicationContext一个非常重要的间接子类 AnnotationConfigServletWebServerApplicationContext,不妨来看下继承关系图:
实际的之前在玩观察者模式的时候咱们捕获到的applicationContext的真正类型也是这个AnnotationConfigServletWebServerApplicationContext。
往后咱们再捕获applicationContext就可以使用下面的这个方法捕获了。
springboot项目加载三方bean
在实际的业务场景中,要加载第三方bean的情形是不多见的。但是真的需要这么去做的时候,最好能知道怎么办,加载三方bean虽然业务中遇到不多,但是有一天你自己写框架的时候这却是非常常见的。 怎么做呢。咱们首先自己写两个类来模仿下,假装它们是第三方的bean. 第一个类:
package com.cmdc.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
public class ApplicationReadyEventListener implements ApplicationListener<ApplicationReadyEvent> {
private static Logger logger = LoggerFactory.getLogger(ApplicationReadyEventListener.class);
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
logger.info("springBoot service start successfully!");
}
}
复制代码
第二个类:
package com.cmdc.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.context.ApplicationListener;
public class ApplicationFailedEventListener implements ApplicationListener<ApplicationFailedEvent> {
private static Logger logger = LoggerFactory.getLogger(ApplicationFailedEventListener.class);
@Override
public void onApplicationEvent(ApplicationFailedEvent event) {
logger.error("springBoot service start failed!");
}
}
复制代码
现在打算将他们注入,但是因为它们是三方库里面的,无法在上面直接写@Component。咱们这么去做:
package com.cmdc.utils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author : wuwensheng
* @date : 10:05 2021/12/3
*/
public class ApplicationEventListenerConfiguration {
@Bean
@ConditionalOnMissingBean(value = ApplicationFailedEventListener.class)
public ApplicationFailedEventListener getApplicationFailedEventListener(){
return new ApplicationFailedEventListener();
}
@Bean
@ConditionalOnMissingBean(value = ApplicationReadyEventListener.class)
public ApplicationReadyEventListener getApplicationReadyEventListener(){
return new ApplicationReadyEventListener();
}
}
复制代码
ok,现在假设的情景是什么?ApplicationReadyEventListener和ApplicationFailedEventListener都在三方库里面,只有ApplicationEventListenerConfiguration是存在于自己项目下的。
另外解释下@ConditionalOnMissingBean的含义,当且仅当不存在指定的bean的时候才加载指定的bean。
注意,我并未在ApplicationEventListenerConfiguration类上添加@Configuration注解。而实将此类声明在spring.factories里面。 声明如下:
ok,我的意思就是,尽管没添加@Conuration注解,这个类依旧是我的配置类!
那么咱们启动起来看一下:
ok,已经注入成功。
总结
写业务代码的思想和框架代码层面的思想差太多了,基本一个阳春白雪、一个下里巴人。大家平时也要多看框架源码。