SpringApplication准备阶段
第三部分 加载应用上下文初始器和应用事件监听器
利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer 实现类,并排序对象集合。
技术
- 实现类: org.springframework.core.io.support.SpringFactoriesLoader
- 配置资源: META-INF/spring.factories
- 排序: AnnotationAwareOrderComparator#sort
SpringFactoriesLoader
这个实现类是通过加载 META-INF/spring.factories资源,这个资源有可能在jar包里或者文件系统里面
/**
* General purpose factory loading mechanism for internal use within the framework.
*
* <p>{@code SpringFactoriesLoader} {@linkplain #loadFactories loads} and instantiates
* factories of a given type from {@value #FACTORIES_RESOURCE_LOCATION} files which
* may be present in multiple JAR files in the classpath. The {@code spring.factories}
* file must be in {@link Properties} format, where the key is the fully qualified
* name of the interface or abstract class, and the value is a comma-separated list of
* implementation class names. For example:
*
* <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>
*
* where {@code example.MyService} is the name of the interface, and {@code MyServiceImpl1}
* and {@code MyServiceImpl2} are two implementations.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.2
*/
public final class SpringFactoriesLoader {
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
private SpringFactoriesLoader() {
}
加载完资源后对顺序进行排列,就要利用到AnnotationAwareOrderComparator接口,这个排序不是强制的,没有排的话有默认的方式
public interface Ordered {
/**
* Useful constant for the highest precedence value.
* @see java.lang.Integer#MIN_VALUE
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* Useful constant for the lowest precedence value.
* @see java.lang.Integer#MAX_VALUE
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
/**
* Get the order value of this object.
* <p>Higher values are interpreted as lower priority. As a consequence,
* the object with the lowest value has the highest priority (somewhat
* analogous to Servlet {@code load-on-startup} values).
* <p>Same order values will result in arbitrary sort positions for the
* affected objects.
* @return the order value
* @see #HIGHEST_PRECEDENCE
* @see #LOWEST_PRECEDENCE
*/
int getOrder();
}
还标注了一个Order的接口
默认值是一个最低优先级
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
/**
* The order value.
* <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
* @see Ordered#getOrder()
*/
int value() default Ordered.LOWEST_PRECEDENCE;
}
spring.factories
第一行就是ApplicationContextInitializer应用上下文初始器全类名和实现类,在SpringApplication准备的时候,实现类会进行初始化,他们也会进行排序,当你查看这2个源码的时候,
SharedMetadataReaderFactoryContextInitializer和ConditionEvaluationReportLoggingListener,并没有实现order接口,说明他们并不关心顺序的。
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
【栗子】加以说明排序问题
(1)按照源码分类,新建一个包context,下面新建一个HelloWorldApplicationContextInitializer,来实现ApplicationContextInitializer接口
ApplicationContextInitializer这个接口有个条件,有一个泛型的参数<C extends ConfigurableApplicationContext>也可以不写
标注优先级:最高级
@Order(Ordered.HIGHEST_PRECEDENCE)//排序:默认是最低优先级
public class HelloWorldApplicationContextInitializer<C extends ConfigurableApplicationContext>
implements ApplicationContextInitializer<C> {
@Override
public void initialize(C applicationContext) {
System.out.println("ConfigurableApplicationContext.id="+applicationContext.getId());
}
}
(2)新建一个类AfterHelloWorldApplicationContextInitializer ,实现ApplicationContextInitializer 和Ordered
,这里采用最低优先级
/**
* After HelloWorldApplicationContextInitializer
*/
public class AfterHelloWorldApplicationContextInitializer implements ApplicationContextInitializer, Ordered {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("After application.id ="+applicationContext.getId());
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
(3)spring-application模块下resources下面新建一个 META-INF/spring.factories ,添加上面的实现
org.springframework.context.ApplicationContextInitializer=\
com.cbt.diveinspringboot.context.AfterHelloWorldApplicationContextInitializer,\
com.cbt.diveinspringboot.context.HelloWorldApplicationContextInitializer
(4)运行:ConfigurableApplicationContext先出来的,已经按照order顺序排序了
1.png
说明:
1)这里只是简单举例说明ApplicationContext的配置(一般不这样做),实际上的应用中可以做很多操作,例如setEnvironment,setParent,getBeanFactory等等,getBean不行,还没初始化好。
2)web应用也可以这么去做,结果是一样的
public class SpringApplicationBootstrap {
public static void main(String[] args) {
//SpringApplication.run(ApplicationConfiguration.class,args);
Set<String> sources = new HashSet();
//配置class名称
sources.add(ApplicationConfiguration.class.getName());
SpringApplication springApplication = new SpringApplication();
springApplication.setSources(sources);
springApplication.run(args);
}
@SpringBootApplication
public static class ApplicationConfiguration {
}
}
3)在spring时代,ApplicationContextInitializer只能在web里使用
加载应用事件监听器
利用 Spring 工厂加载机制,实例化 ApplicationListener 实现类,并排序对象集合
在SpringApplication的构造器里我们可以看到几个特点:
- 配置了Source
- 配置webApplication
- ApplcationContextInitialize
同样的方式把ApplicationListener进行装载,ApplicationListener是关于spring事件监听的
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
我们在spring.factories中也能找到
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
BackgroundPreinitializer也标注了@Order,说明也会有加载的顺序,
这里会监听SpringApplicationEvent,这个事件是SpringBoot的事件
@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
public class BackgroundPreinitializer
implements ApplicationListener<SpringApplicationEvent> {
介绍几个spring事件
(1)ApplicationEvent 是个标记接口
继承了EventObject(标记接口,是所有事件的一个源)
public abstract class ApplicationEvent extends EventObject {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 7099057708183571937L;
/** System time when the event happened. */
private final long timestamp;
/**
* Create a new ApplicationEvent.
* @param source the object on which the event initially occurred (never {@code null})
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* Return the system time in milliseconds when the event happened.
*/
public final long getTimestamp() {
return this.timestamp;
}
}
(2)ApplicationContextEvent
是关于Application上下文的事件
public abstract class ApplicationContextEvent extends ApplicationEvent {
/**
* Create a new ContextStartedEvent.
* @param source the {@code ApplicationContext} that the event is raised for
* (must not be {@code null})
*/
public ApplicationContextEvent(ApplicationContext source) {
super(source);
}
/**
* Get the {@code ApplicationContext} that the event was raised for.
*/
public final ApplicationContext getApplicationContext() {
return (ApplicationContext) getSource();
}
}
(3)ContextRefreshedEvent
上下文刷新的时候的事件
public class ContextRefreshedEvent extends ApplicationContextEvent {
/**
* Create a new ContextRefreshedEvent.
* @param source the {@code ApplicationContext} that has been initialized
* or refreshed (must not be {@code null})
*/
public ContextRefreshedEvent(ApplicationContext source) {
super(source);
}
}
【栗子---监听ContextRefreshEvent事件】
按照源码分类:新建一个listener包,下面新建一个类HelloWorldApplicationListener,监听ContextRefreshEvent事件,标注@Order,采用最高优先级
/**
* HelloWorld {@link ApplicationListener} 监听{@link ContextRefreshedEvent}
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloWorldApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("HelloWorld :" + event.getApplicationContext().getId()
+ " , timestamp:" +event.getTimestamp());
}
}
新建一个类AfterHelloWorldApplicationListener,监听同一个事件,这里实现Ordered接口
/**
* After HelloWorld {@link ApplicationListener} 监听{@link ContextRefreshedEvent}
*/
public class AfterHelloWorldApplicationListener implements ApplicationListener<ContextRefreshedEvent>, Ordered {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("AfterHelloWorld :" + event.getApplicationContext().getId()
+ " , timestamp:" +event.getTimestamp());
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
(3)配置spring.factories
org.springframework.context.ApplicationListener=\
com.cbt.diveinspringboot.listener.AfterHelloWorldApplicationListener,\
com.cbt.diveinspringboot.listener.HelloWorldApplicationListener
(4)运行
7.png
当应用刷新之后,2个事件有先后,但是事件发生时间是一样的。
8.png
现在为止SpringApplication的准备阶段完成了。SpringApplication的准备阶段其实是SpringApplication构造器的阶段
网友评论