美文网首页spring cloud基础知识点
SpringBoot2.0深度实践学习笔记(五)之 Spring

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

作者: Sam_L | 来源:发表于2019-04-29 16:32 被阅读0次
    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构造器的阶段

    相关文章

      网友评论

        本文标题:SpringBoot2.0深度实践学习笔记(五)之 Spring

        本文链接:https://www.haomeiwen.com/subject/hdhynqtx.html