Spring Boot 2.0之理解SpringApplication

SpringApplication的基本使用

SpringApplication 运行

SpringApplication.run(SpringApplicationBootstrap.class,args);

自定义 SpringApplication

通过 SpringApplication API 调整
SpringApplication springApplication = new SpringApplication(SpringApplicationBootstrap.class);
springApplication.setBannerMode(Banner.Mode.CONSOLE);
springApplication.setWebApplicationType(WebApplicationType.NONE);
springApplication.setAdditionalProfiles("prod");
springApplication.setHeadless(true);
通过 SpringApplicationBuilder API 调整

流式方法处理,更优雅

new SpringApplicationBuilder(SpringApplicationBootstrap.class)
.bannerMode(Banner.Mode.CONSOLE)
.web(WebApplicationType.NONE)
.profiles("prod")
.headless(true)
.run(args);

SpringApplication 准备阶段

配置 Spring Boot Bean 源

Java 配置 Class 或 XML 上下文配置文件集合,用于 Spring Boot BeanDefinitionLoader 读取并且将配置源解析加载为Spring Bean 定义
Java 配置 Class
用于 Spring 注解驱动中 Java 配置类,大多数情况是 Spring 模式注解所标注的类,如 @Configuration。在spring boot中是@SpringBootApplication
@SpringBootApplication—>@SpringBootConfiguration—>@Configuration

/**
 * {@link SpringApplication}
 * Created by Yuk on 2019/3/9.
 */
@SpringBootApplication
public class SpringApplicationBootstrap {

    public static void main(String[] args) {

        SpringApplication.run(SpringApplicationBootstrap.class,args);
    }
}
@SpringBootApplication
public class SpringApplicationBootstrap {

    public static void main(String[] args) {

        //SpringApplication.run(SpringApplicationBootstrap.class,args);
        Set sources = new HashSet();
        sources.add(SpringApplicationBootstrap.class.getName());

        SpringApplication application = new SpringApplication();
        application.setSources(new HashSet<>(sources));
        application.run(args);
    }
}

这两种方式都可以正常启动,重点看application.setSources(new HashSet<>(sources));

/**
	 * Set additional sources that will be used to create an ApplicationContext. A source
	 * can be: a class name, package name, or an XML resource location.
	 * <p>
	 * Sources set here will be used in addition to any primary sources set in the
	 * constructor.
	 * @param sources the application sources to set
	 * @see #SpringApplication(Class...)
	 * @see #getAllSources()
	 */
	public void setSources(Set<String> sources) {
		Assert.notNull(sources, "Sources must not be null");
		this.sources = new LinkedHashSet<>(sources);
	}

看英文注释可以知道
A source can be: a class name, package name, or an XML resource location.
这里Set集合的sources可以是className(但必须是加了@Configuration),可以是包名,也可以是spring的配置文件

推断 Web 应用类型

根据当前应用 ClassPath 中是否存在相关实现类来推断 Web 应用的类型,包括:

  • Web Reactive: WebApplicationType.REACTIVE
  • Web Servlet: WebApplicationType.SERVLET
  • 非 Web: WebApplicationType.NONE

参考方法:
spring boot 2.0.7版本

this.webApplicationType = WebApplicationType.deduceFromClasspath();
static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

推断引导类(Main Class)

这里可能会有个疑问,引导类不就是Main Class吗?
注意,这样写确实是的

@SpringBootApplication
public class SpringApplicationBootstrap {

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

但是这样写,引导类就不是当前运行main方法的class了

public class SpringApplicationBootstrap {

    public static void main(String[] args) {

        SpringApplication.run(ApplicationConfiguration.class,args);
    }

    @SpringBootApplication
    public static class ApplicationConfiguration{

    }
}

参考方法org.springframework.boot.SpringApplication#deduceMainApplicationClass
抛出异常获取堆栈,然后判断堆栈的方法是否为main,从而获取类名,反射得到引导类

private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

我们可以断点看一下
在这里插入图片描述

加载应用上下文初始器 ( ApplicationContextInitializer )

初始化时可以拿到上下文ConfigurableApplicationContext,做一些其他事情。

setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

跟踪这个方法,最后发现会去META-INF/spring.factories中查找。利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer 的实现类,并排序对象集合。
可以看到spring boot默认有两个实现类实现了 ApplicationContextInitializer
在这里插入图片描述
并且,AnnotationAwareOrderComparator.sort(instances);对实现类进行排序,看截图可知。
ApplicationContextInitializer 的实现类定义排序有三种方式:
1.实现Ordered接口
2.注解Order
3.注解priority
在这里插入图片描述

自定义上下文初始器
  • 实现Ordered接口定义优先级
public class AfterApplicationContextInitiazer implements ApplicationContextInitializer,Ordered{
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE; // 最低优先级
    }

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("After ApplicationContext.id="+applicationContext.getId());
    }
}
  • @Order注解定义优先级
@Order(Ordered.HIGHEST_PRECEDENCE)// 最高优先级
public class HelloWorldApplicationContextInitiazer implements ApplicationContextInitializer{

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("helloWorld ApplicationContext.id="+applicationContext.getId());
    }
}

在这里插入图片描述
spring.factories

# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.imooc.diveinspringboot.context.AfterApplicationContextInitiazer,\
com.imooc.diveinspringboot.context.HelloWorldApplicationContextInitiazer
测试

控制台打印

helloWorld ApplicationContext.id=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@327b636c
After ApplicationContext.id=application

加载应用事件监听器( ApplicationListener )

利用 Spring 工厂加载机制,实例化 ApplicationListener 的实现类,并排序对象集合,与加载应用上下文初始器的机制一样。

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
自定义事件监听器

这里只是自定义监听器,监听的是spring默认的事件
在这里插入图片描述

public class AfterApplicationListener implements ApplicationListener<ContextRefreshedEvent>,Ordered {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("After ApplicationListener :"+event.getApplicationContext().getId()
                +",timestamp:"+event.getTimestamp());
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloWorldApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("HelloWorld ApplicationListener :"+event.getApplicationContext().getId()
            +",timestamp:"+event.getTimestamp());
    }
}

spring.factories

org.springframework.context.ApplicationListener=\
com.imooc.diveinspringboot.listen.AfterApplicationListener,\
com.imooc.diveinspringboot.listen.HelloWorldApplicationListener
测试

控制台打印

HelloWorld ApplicationListener :application,timestamp:1552139922079
After ApplicationListener :application,timestamp:1552139922079

spring的事件监听特别好用,可以多个监听一个事件,是观察者模式的一种体现。这里只是自定义了监听器,其实还可以自定义事件,到时候在讲spring源码的时候具体会写一篇文章。

SpringApplication 运行阶段

加载 SpringApplication 运行监听器( SpringApplicationRunListeners )

利用 Spring 工厂加载机制,读取 SpringApplicationRunListener 对象集合,并且封装到组合类
SpringApplicationRunListeners
SpringApplication最终的run方法中

SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

其中getSpringFactoriesInstances方法,上面也讲到过,就是根据类型去spring.factories中获取相对应的类。这里可以看到获取的是实现了SpringApplicationRunListener接口的实现类

运行 SpringApplication 运行监听器( SpringApplicationRunListeners )

SpringApplicationRunListener有7个方法,对应容器运行阶段的7种状态,可以自定义实现不同方法。比如容器开始时要做什么,启动时要做什么等等。。。

public interface SpringApplicationRunListener {

	void starting();
	
	void environmentPrepared(ConfigurableEnvironment environment);

	void contextPrepared(ConfigurableApplicationContext context);

	void contextLoaded(ConfigurableApplicationContext context);

	void started(ConfigurableApplicationContext context);

	void running(ConfigurableApplicationContext context);

	void failed(ConfigurableApplicationContext context, Throwable exception);

}
监听方法阶段说明Spring Boot 起始版本
starting()Spring 应用刚启动1.0
environmentPrepared(ConfigurableEnvironment)ConfigurableEnvironment 准备妥当,允许将其调整1.5
contextPrepared(ConfigurableApplicationContext)ConfigurableApplicationContext 准备妥当,允许将其调整1.0
contextLoaded(ConfigurableApplicationContext context)ConfigurableApplicationContext 已装载,但仍未启动1.0
started(ConfigurableApplicationContext context)ConfigurableApplicationContext 已启动,此时 Spring Bean 已初始化完成2.0
running(ConfigurableApplicationContext)Spring 应用正在运行2.0
failed(ConfigurableApplicationContext,Throwable)Spring 应用运行失败1.0

监听 Spring Boot 事件 / Spring 事件

Spring Boot 通过 SpringApplicationRunListener 的实现类 EventPublishingRunListener 利用 Spring Framework 事件API ,广播 Spring Boot 事件。
在这里插入图片描述
listeners.starting();时,发布了一个ApplicationStartingEvent事件,如果自定义监听器监听了该事件,则会在这里触发监听器。
EventPublishingRunListener.java

public void starting() {
		this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
	}
EventPublishingRunListener 监听方法与 Spring Boot 事件对应关系
监听方法Spring Boot 事件Spring Boot 起始版本
starting()ApplicationStartingEvent1.5
environmentPrepared(ConfigurableEnvironment)ApplicationEnvironmentPreparedEvent1.0
contextPrepared(ConfigurableApplicationContext)
contextLoaded(ConfigurableApplicationContext)ApplicationPreparedEvent1.0
started(ConfigurableApplicationContext)ApplicationStartedEvent2.0
running(ConfigurableApplicationContext)ApplicationReadyEvent2.0
failed(ConfigurableApplicationContext,Throwable)ApplicationFailedEvent1.0
Spring Framework 事件/监听器编程模型
  • Spring 应用事件

    • 普通应用事件: ApplicationEvent
    • 应用上下文事件: ApplicationContextEvent
  • Spring 应用监听器

  • Spring 应用事广播器

    • 接口: ApplicationEventMulticaster
    • 实现类: SimpleApplicationEventMulticaster
    • 执行模式:同步或异步

事件监听默认为同步,异步就是在onApplicationEvent方法上加@Async

@Async
public void onApplicationEvent(ContextRefreshedEvent event) {
    System.out.println("After ApplicationListener :"+event.getApplicationContext().getId()
            +",timestamp:"+event.getTimestamp());
}

自定义 SpringApplication 运行监听器

public class HelloApplicationRunListener implements SpringApplicationRunListener{
    @Override
    public void starting() {
        System.out.println("Hello ApplicationRunListener is starting.");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {

    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {

    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {

    }

    @Override
    public void started(ConfigurableApplicationContext context) {

    }

    @Override
    public void running(ConfigurableApplicationContext context) {

    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {

    }
}

添加到spring.factories中

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
com.imooc.diveinspringboot.run.HelloApplicationRunListener

运行报错:
Caused by: java.lang.NoSuchMethodException: com.imooc.diveinspringboot.run.HelloApplicationRunListener.<init>(org.springframework.boot.SpringApplication, [Ljava.lang.String;) at java.lang.Class.getConstructor0(Class.java:3082) at java.lang.Class.getDeclaredConstructor(Class.java:2178) at org.springframework.boot.SpringApplication.createSpringFactoriesInstances(SpringApplication.java:434) ... 6 more
构造方法报错,我们看一下spring boot的运行运行监听器EventPublishingRunListener怎么实现的,它有一个带参数的构造函数

public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

我么仿照着写一个一样的构造函数,启动正常,打印出了Hello ApplicationRunListener is starting.
为什么要加一个这样的构造函数,是因为创建运行监听器实例的时候,通过带参数的构造函数实例化的

private <T> List<T> createSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
			Set<String> names) {
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				Assert.isAssignable(type, instanceClass);
				Constructor<?> constructor = instanceClass
						.getDeclaredConstructor(parameterTypes);
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException(
						"Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}

创建 Spring 应用上下文( ConfigurableApplicationContext )

根据准备阶段的推断 Web 应用类型创建对应的 ConfigurableApplicationContext 实例:

  • Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
  • Web Servlet: AnnotationConfigServletWebServerApplicationContext
  • 非 Web: AnnotationConfigApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, "
								+ "please specify an ApplicationContextClass",
						ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

创建 Environment

根据准备阶段的推断 Web 应用类型创建对应的 ConfigurableEnvironment 实例:

  • Web Reactive: StandardReactiveWebEnvironment
  • Web Servlet: StandardServletEnvironment
  • 非 Web: StandardEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

刷新上下文

run方法中会执行refreshContext(context);,最后执行的就是AbstractApplicationContext中的refresh()方法
spring加载流程之refresh()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要在 Spring Boot 应用中引入 Activiti 7,需要按照以下步骤操作: 1. 在项目的 pom.xml 文件中添加 Activiti 7 的依赖,例如: ``` <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter-basic</artifactId> <version>7.1.0</version> </dependency> ``` 2. 创建一个名为 `application.properties` 的配置文件,并在其中指定 Activiti 所使用的数据库连接信息,例如: ``` spring.datasource.url=jdbc:h2:mem:activiti;DB_CLOSE_DELAY=-1 spring.datasource.username=sa spring.datasource.password= spring.datasource.driver-class-name=org.h2.Driver ``` 3. 在应用的启动类中添加 `@EnableProcessEngine` 注解,例如: ``` @SpringBootApplication @EnableProcessEngine public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } ``` 4. 在应用中使用 Activiti 相关的 API 进行开发即可。 希望这些信息能够帮到你! ### 回答2: Spring Boot是一个用于快速构建Spring应用程序的框架,而Activiti是一个开源的工作流引擎。将Activiti7引入Spring Boot可以让开发人员更方便地在Spring应用程序中集成工作流系统。 引入Activiti7到Spring Boot的步骤如下: 1. 在Spring Boot项目的依赖管理文件(pom.xml)中添加Activiti7的依赖。可以通过在Maven或Gradle中加入相应的依赖引入Activiti7。 2. 配置Activiti7的相关属性。在Spring Boot的配置文件(application.properties或application.yml)中设置Activiti7的配置参数,例如数据库连接、用户名密码等。 3. 创建工作流模型。使用Activiti7提供的图形化工作流设计器,或者直接编写BPMN 2.0规范的XML文件来定义工作流程。 4. 基于工作流模型创建流程实例。使用Activiti7的API来加载并部署工作流模型,然后可以根据需要创建流程实例。 5. 处理工作流任务。根据工作流的定义和流程实例的状态,使用Activiti7的API来处理工作流任务,例如启动工作流实例、完成任务等。 6. 监控和管理工作流。利用Activiti7提供的监控和管理功能,可以查看工作流的执行情况、统计数据等。 通过将Activiti7引入到Spring Boot中,可以大大简化工作流系统的开发过程,提高开发效率。同时,由于Spring Boot的自动配置特性,可以将工作流引擎与其他Spring相关组件无缝集成,提供更强大的功能。 ### 回答3: Spring Boot是一个用于开发微服务的框架,而Activiti 是一个用于构建和管理业务流程的开源工作流引擎。将Spring Boot与Activiti 7整合,可以使开发者更加方便地使用工作流引擎来管理和控制业务流程。 首先,使用Spring Boot引入Activiti 7,需要在项目的pom.xml文件中添加Activiti的依赖。在依赖中指定Activiti的版本号,然后使用Maven或Gradle进行依赖的下载和安装。 接下来,在Spring Boot的配置文件中配置Activiti的相关属性,如数据库连接信息、流程定义文件的存放路径等。可以使用YAML 或 properties 格式的配置文件进行配置。 然后,创建流程定义文件(BPMN文件),定义业务流程的各个环节和流程节点。在每个节点中,可以配置相应的操作、表单和响应等信息。可以使用Activiti Designer等工具来创建和编辑BPMN文件。 在Spring Boot的代码中,通过Activiti的API,可以使用Activiti的各种功能和组件。例如,可以使用Activiti 的流程引擎来启动、挂起、审核、撤回或终止流程实例。还可以使用任务服务来查询、领取、完成或委托任务等。 此外,也可以使用Activiti的表单服务来创建、编辑和提交表单。还可以使用历史服务来跟踪和查询流程实例的历史记录,如流程的启动时间、结束时间、当前状态等。 最后,在Spring Boot的应用中,可以根据需要监听和处理Activiti的事件。例如,可以监听流程实例的启动、挂起、完成等事件,并在事件发生时触发相应的业务逻辑。 综上所述,通过在Spring Boot中引入Activiti 7,可以方便地使用工作流引擎来构建和管理业务流程。这样,开发者可以更加高效地实现复杂的业务逻辑,并有效地提高工作效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值