SpringBoot源码分析(二)

启动SpringBoot

  public static void main(String[] args) {
    SpringApplication.run(SpringbootBaseApplication.class, args);
  }

执行构造器并传递参数运行

	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

构造器方法

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 资源为空
		this.resourceLoader = resourceLoader;
    // 断言判断是否传入自动文件如果如果没有传入则关闭
    // 如开头我们传入:new Class[] {}空类,就会执行这句报错
		Assert.notNull(primarySources, "PrimarySources must not be null");
    // 创建一个有序的双向链表,并把启动类数组转换为List传入俩表中
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    /**默认是SERVLET类型,方法内判断了如果不是webflux,或者其他未知的,那么就是SERVLET
    推断逻辑是:
      先是判断默认的classloader中是否存在org.springframework.web.reactive.DispatcherHandler、且不存在org.springframework.web.servlet.DispatcherServlet、org.glassfish.jersey.servlet.ServletContainer,如果为true返回WebApplicationType.REACTIVE;
      然后循环String数组,判断如果默认的classloader中是否不存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext,如果不存在,则认为不是web应用程序,返回WebApplicationType.NONE;
      最后是返回WebApplicationType.SERVLET。
      三种返回类型的解释如下:
              1、WebApplicationType.NONE:不是web应用程序
              2、WebApplicationType.SERVLET:基于servlet的Web应用程序运行
              3、WebApplicationType.REACTIVE:响应式的web应用程序
          this.webApplicationType = WebApplicationType.deduceFromClasspath();
     **/
    // 获取指定集合配置类,并初始化他们(通过这个是的我们注入属性成为可能!)
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 获取指定类型的监听事件集合,并初始化他们(通过这个我们可以做自己的事件监听器)
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 运行时反射推断那个是main所在类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

备注:下一章我们在讲解运行执行了哪些,我们先消化下构造函数,构造函数中我们提到了属性注入和监听事件,我们下边看看如何实现!本文还是基于SpringBoot,MVC等请自行配置XML,本文使用JavaConfig方式进行演示!

自定义初始化参数类

1、配置文件优先级最高,spring.factories 其次,代码定义优先级最低。

2、自定义配置类,就是实现ApplicationContextInitializer接口

编写配置类

package com.pv3.springboot_base.Initializer;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

/** @author sulwan 此类作用是动态更改刷新Redis集群服务器(以后我带大家写框架的时候会用到) */
public class RedisApplicationContextInitializer implements ApplicationContextInitializer {
  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
    ConfigurableEnvironment environment = applicationContext.getEnvironment();

    Map<String, Object> map = new HashMap<>();
    map.put("redis1", "192.168.168.10");
    map.put("redis2", "192.168.168.11");
    map.put("redis3", "192.168.168.12");

    MapPropertySource mapPropertySource = new MapPropertySource("redis", map);
    environment.getPropertySources().addLast(mapPropertySource);

    System.out.println("run RedisApplicationContextInitializer");
  }
}

加载配置类

package com.pv3.springboot_base;

import com.pv3.springboot_base.Initializer.RedisApplicationContextInitializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/** @author sulwan */
@RestController
@SpringBootApplication
public class SpringbootBaseApplication {
  @Autowired private ConfigurableEnvironment environment;

  public static void main(String[] args) {
    SpringApplication springApplication = new SpringApplication(SpringbootBaseApplication.class);
    springApplication.addInitializers(new RedisApplicationContextInitializer());
    springApplication.run(args);
  }

  @RequestMapping("/")
  public String Redis() {
    String redis = environment.getProperty("redis1");
    System.out.println(redis);

    Map<String, Object> map = new HashMap<>(3);
    map.put("redis1", "292.168.168.10");
    map.put("redis2", "292.168.168.11");
    map.put("redis3", "292.168.168.12");
    MapPropertySource mapPropertySource = new MapPropertySource("redis", map);
    environment.getPropertySources().addLast(mapPropertySource);

    return null;
  }
}

浏览效果

http://localhost:8080/

打开第一次看console,在打开一次在看

自定义监视器

> Spring默认已经实现好的事件,当我们继承ApplicationListener,默认事件的时候使用即可,因为太简单这里不讲,我们讲解自定义事件。
> **默认事件如下:**
> 1、ContextRefreshedEvent:当ApplicationContext初始化或者刷新时触发该事件
> 2、ContextClosedEvent:ApplicationContext被关闭时触发该事件.容器被关闭时,其管理的所有单例Bean都被销毁3、RequestHandleEvent:在Web应用中,当一个Http请求结束时触发该事件
> 4、ContextStartedEvent:当容器调用start()方法时触发
> 5、ContextStopEvent:当容器调用stop()方法时触发 
>
> **事件一般用容器发出**

自定义一个事件

package com.pv3.springboot_base.Listener.Event;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;

public class RedisEvent extends ApplicationEvent {

  private String message;

  public RedisEvent(ApplicationContext source, String message) {
    super(source);
    this.message = message;
  }

  public String getMessage() {
    return message;
  }
}

监听事件

package com.pv3.springboot_base.Listener;

import com.pv3.springboot_base.Listener.Event.RedisEvent;
import org.springframework.context.ApplicationListener;

public class RedisApplicationListener implements ApplicationListener<RedisEvent> {
  @Override
  public void onApplicationEvent(RedisEvent event) {
    System.out.println(event.getMessage());
  }
}

演示监听事件

package com.pv3.springboot_base;

import com.pv3.springboot_base.Listener.Event.RedisEvent;
import com.pv3.springboot_base.Listener.RedisApplicationListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/** @author sulwan */
@RestController
@SpringBootApplication
public class SpringbootBaseApplication {
  @Autowired private ConfigurableEnvironment environment;

  @Autowired private ApplicationContext applicationContext;

  public static void main(String[] args) {
    SpringApplication springApplication = new SpringApplication(SpringbootBaseApplication.class);
    springApplication.addListeners(new RedisApplicationListener());
    springApplication.run(args);
  }

  @RequestMapping("/")
  public String Redis() {
    applicationContext.publishEvent(new RedisEvent(applicationContext, "redis事件发出了,就是这么简单"));
    return null;
  }
}

演示效果

http://localhost:8080/