SpringBoot2.0深度实践学习笔记(五)之 SpringApplication(三)

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.
 *
 * 

{@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: * *

example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
* * 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. *

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> 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.
     * 

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). *

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.
     * 

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这个接口有个条件,有一个泛型的参数也可以不写
标注优先级:最高级

@Order(Ordered.HIGHEST_PRECEDENCE)//排序:默认是最低优先级
public class HelloWorldApplicationContextInitializer
        implements ApplicationContextInitializer {

    @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 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 {

介绍几个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 {
    @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, 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构造器的阶段

你可能感兴趣的:(SpringBoot2.0深度实践学习笔记(五)之 SpringApplication(三))