();
+ // 将初始化器的实例添加到Spring容器中
+ this.initializers.addAll(initializers);
+ }
+```
+
+
+
+#### 总结
+
+- SpringFactoriesLoader作用: SpringBoot框架中从类路径jar包中读取特定的文件(META-INF/spring.factories)实现扩展类的载入
+
+image-20210726172128392
+
+- LoadFactories流程
+
+image-20210726172754618
diff --git "a/springboot-source-code-analysis/(4)347円263円273円347円273円237円345円210円235円345円247円213円345円214円226円345円231円250円350円247円243円346円236円220円.md" "b/springboot-source-code-analysis/(4)347円263円273円347円273円237円345円210円235円345円247円213円345円214円226円345円231円250円350円247円243円346円236円220円.md"
new file mode 100644
index 0000000..b293a0e
--- /dev/null
+++ "b/springboot-source-code-analysis/(4)347円263円273円347円273円237円345円210円235円345円247円213円345円214円226円345円231円250円350円247円243円346円236円220円.md"
@@ -0,0 +1,215 @@
+### 系统初始化器解析
+
+#### 1、ApplicationContextInitializer作用
+
+```java
+/**
+ * Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
+ * prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
+ *
+ * Typically used within web applications that require some programmatic initialization
+ * of the application context. For example, registering property sources or activating
+ * profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
+ * context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
+ * for declaring a "contextInitializerClasses" context-param and init-param, respectively.
+ *
+ *
{@code ApplicationContextInitializer} processors are encouraged to detect
+ * whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
+ * implemented or if the @{@link org.springframework.core.annotation.Order Order}
+ * annotation is present and to sort instances accordingly if so prior to invocation.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @param the application context type
+ * @see org.springframework.web.context.ContextLoader#customizeContext
+ * @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
+ * @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
+ * @see org.springframework.web.servlet.FrameworkServlet#applyInitializers
+ */
+public interface ApplicationContextInitializer {
+
+ /**
+ * Initialize the given application context.
+ * @param applicationContext the application to configure
+ */
+ void initialize(C applicationContext);
+}
+
+```
+
+- 上下文刷新即refersh方法前调用;
+- 用来编码设置一些属性变量,通常用在web环境中;
+- 可以通过order接口进行排序;
+
+
+
+#### 2、ApplicationContextInitializer调用点,通过SpringFactoriesLoder中的this.initializers进行初始化
+
+image-20210802174243992
+
+- 步骤一:在刷新上下文前,调用prepareContext#()准备上下文的方法
+
+```java
+public ConfigurableApplicationContext run(String... args) {
+ StopWatch stopWatch = new StopWatch();
+ stopWatch.start();
+ ConfigurableApplicationContext context = null;
+ Collection exceptionReporters = new ArrayList();
+ configureHeadlessProperty();
+ SpringApplicationRunListeners listeners = getRunListeners(args);
+ listeners.starting();
+ try {
+ ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
+ ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
+ configureIgnoreBeanInfo(environment);
+ Banner printedBanner = printBanner(environment);
+ context = createApplicationContext();
+ exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
+ new Class[] { ConfigurableApplicationContext.class }, context);
+ // 准备上下文
+ prepareContext(context, environment, listeners, applicationArguments, printedBanner);
+ // 刷新上下文
+ refreshContext(context);
+ afterRefresh(context, applicationArguments);
+ stopWatch.stop();
+ if (this.logStartupInfo) {
+ new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
+ }
+ listeners.started(context);
+ callRunners(context, applicationArguments);
+ }
+ catch (Throwable ex) {
+ handleRunFailure(context, ex, exceptionReporters, listeners);
+ throw new IllegalStateException(ex);
+ }
+
+ try {
+ listeners.running(context);
+ }
+ catch (Throwable ex) {
+ handleRunFailure(context, ex, exceptionReporters, null);
+ throw new IllegalStateException(ex);
+ }
+ return context;
+ }
+```
+
+- 步骤二:调用初始化器 applyInitializers(context) 方法
+
+```java
+ private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
+ SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
+ context.setEnvironment(environment);
+ postProcessApplicationContext(context);
+ // 调用初始化器方法
+ applyInitializers(context);
+ listeners.contextPrepared(context);
+ if (this.logStartupInfo) {
+ logStartupInfo(context.getParent() == null);
+ logStartupProfileInfo(context);
+ }
+ // Add boot specific singleton beans
+ ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
+ beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
+ if (printedBanner != null) {
+ beanFactory.registerSingleton("springBootBanner", printedBanner);
+ }
+ if (beanFactory instanceof DefaultListableBeanFactory) {
+ ((DefaultListableBeanFactory) beanFactory)
+ .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
+ }
+ // Load the sources
+ Set sources = getAllSources();
+ Assert.notEmpty(sources, "Sources must not be empty");
+ load(context, sources.toArray(new Object[0]));
+ listeners.contextLoaded(context);
+ }
+```
+
+- 步骤三:系统初始化器的具体实现,调用 ApplicationContextInitializer.initialize() 方法
+
+```java
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected void applyInitializers(ConfigurableApplicationContext context) {
+ // getInitializers() 方法,从this.initializers 成员中,获取初始化器的实现
+ for (ApplicationContextInitializer initializer : getInitializers()) {
+ // 判断初始化器的实现类,是否实现了ApplicationContextInitializer接口类
+ Class> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
+ ApplicationContextInitializer.class);
+ Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
+ // 调用初始化器实现类中的 initialize#() 方法,进行初始化
+ initializer.initialize(context);
+ }
+ }
+```
+
+
+
+#### 3、添加系统初始化器 this.initializers 成员的两种方式
+
+- 第一种:定义在spring.factories文件中,通过SpringFactoriesLoader发现与注册
+
+ ```java
+ SpringBoot框架中从类路径jar包中读取特定的文件(META-INF/spring.factories)实现扩展类的载入
+ 最终调用SpringApplication.addInitializers#()方法添加到this.initializers成员中
+ ```
+
+
+
+- 第二种:通过SpringApplication.addInitializers#() 方法手动添加到系统初始化器中
+
+ ```java
+ public static void main(String[] args) {
+ // SpringApplication.run(SpringbootSourceCodeAnalysisApplication.class, args);
+ SpringApplication springApplication = new SpringApplication(SpringbootSourceCodeAnalysisApplication.class);
+ // 手动添加一个框架的初始化器
+ springApplication.addInitializers(new SecondInitializer());
+ springApplication.run();
+ }
+ ```
+
+
+
+- 第三种:在application.properties 配置文件中定义环境变量,通过DelegatingApplicationContextInitializer 发现与注册
+
+ - 步骤一:DelegatingApplicationContextInitializer 在Spring框架中,会被SpringFactoriesLoader 自动加载,且优先级最高 order = 0
+
+ ```java
+ # 在spring.factories配置文件中
+ # Application Context Initializers
+ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
+ ```
+
+ - 步骤二:通过DelegatingApplicationContextInitializer 中的 initialize#() 方法,获取ApplicationContextInitializer.class的实现
+
+ ```java
+ private static final String PROPERTY_NAME = "context.initializer.classes";
+
+ private int order = 0;
+
+ @Override
+ public void initialize(ConfigurableApplicationContext context) {
+ // 获取application.properties配置中的 context.initializer.classes 属性
+ ConfigurableEnvironment environment = context.getEnvironment();
+ // 获取所有 ApplicationContextInitializer.class 接口的实现类
+ List> initializerClasses = getInitializerClasses(environment);
+ if (!initializerClasses.isEmpty()) {
+ // 初始化系统初始化器
+ applyInitializerClasses(context, initializerClasses);
+ }
+ }
+ ```
+
+
+
+#### 4、ApplicationContextInitializer.class 的调用流程总结
+
+image-20210802183526156
+
+
+
+
+
+#### 5、推荐使用的系统初始化器【调用机制】
+
+image-20210803143602922
\ No newline at end of file
diff --git "a/springboot-source-code-analysis/(5)347円233円221円345円220円254円345円231円250円350円256円276円350円256円241円346円250円241円345円274円217円.md" "b/springboot-source-code-analysis/(5)347円233円221円345円220円254円345円231円250円350円256円276円350円256円241円346円250円241円345円274円217円.md"
new file mode 100644
index 0000000..5030614
--- /dev/null
+++ "b/springboot-source-code-analysis/(5)347円233円221円345円220円254円345円231円250円350円256円276円350円256円241円346円250円241円345円274円217円.md"
@@ -0,0 +1,189 @@
+## 监听器
+
+### 1、监听器模式介绍
+
+image-20210803144505140
+
+
+
+- 系统消息由广播器(Multicaster)发布出去,发布的内容统称为事件(Event),对关键事件产生订阅的是监听器(Listener);
+
+
+
+### 2、自定义事件-创建事件
+
+- 创建天气事件抽象类
+
+ ```java
+ // 天气事件抽象类
+ public abstract class WeatherEvent {
+ // 获取当前天气的事件
+ public abstract String getWeather();
+ }
+ ```
+
+- 创建下雨事件实现类
+
+ ```java
+ // 下雨事件
+ public class RainEvent extends WeatherEvent {
+ @Override
+ public String getWeather() {
+ return "rain";
+ }
+ }
+ ```
+
+- 创建下雪事件实现类
+
+ ```java
+ // 下雪事件
+ public class SnowEvent extends WeatherEvent {
+ @Override
+ public String getWeather() {
+ return "snow";
+ }
+ }
+ ```
+
+
+
+#### 3、自定义事件监听器-创建监听器
+
+- 创建天气事件监听器接口类
+
+ ```java
+ // 天气监听器
+ public interface WeatherListener {
+ // 监听天气事件
+ void onWeatherEvent(WeatherEvent event);
+ }
+ ```
+
+- 创建下雨事件监听器实现类
+
+ ```java
+ // 下雨监听器
+ public class RainListener implements WeatherListener {
+ @Override
+ public void onWeatherEvent(WeatherEvent event) {
+ if (event instanceof RainEvent) {
+ System.out.println("rain event ..." + event.getWeather());
+ }
+ }
+ }
+ ```
+
+- 创建下雪事件监听器实现类
+
+ ```java
+ // 下雪监听器
+ public class SnowListener implements WeatherListener {
+ @Override
+ public void onWeatherEvent(WeatherEvent event) {
+ if (event instanceof SnowEvent) {
+ System.out.println("snow event.." + event.getWeather());
+ }
+ }
+ }
+ ```
+
+
+
+#### 4、自定义事件广播器-创建事件广播器
+
+- 创建事件广播器接口
+
+ ```java
+ // 事件广播器
+ public interface EventMulticaster {
+ // 广播事件
+ void multicastEvent(WeatherEvent event);
+ // 添加监听器
+ void addListener(WeatherListener listener);
+ // 删除监听器
+ void removeListener(WeatherListener listener);
+ }
+
+- 创建事件广播器的抽象类
+
+ ```java
+
+ // 抽象的事件广播器
+ public abstract class AbstractEventMulticaster implements EventMulticaster {
+ // 事件监听器列表
+ private List listenerList = new CopyOnWriteArrayList();
+
+ @Override
+ public void multicastEvent(WeatherEvent event) {
+ // 广播所有事件监听器
+ listenerList.forEach(listener -> {
+ // 触发前-模版方法
+ doStart();
+ // 触发机制
+ listener.onWeatherEvent(event);
+ // 触发后-模版方法
+ doEnd();
+ });
+ }
+
+ // 添加事件监听器
+ @Override
+ public void addListener(WeatherListener listener) {
+ listenerList.add(listener);
+ }
+
+ // 删除事件监听器
+ @Override
+ public void removeListener(WeatherListener listener) {
+ listenerList.remove(listener);
+ }
+
+ // 模版方法
+ abstract void doStart();
+ abstract void doEnd();
+ }
+ ```
+
+- 创建事件广播器的实现类
+
+ ```java
+ // 天气事件广播器
+ public class WeatherEventMulticaster extends AbstractEventMulticaster {
+ @Override
+ public void doStart() {
+ System.out.println("multicaster event begin ...");
+ }
+ @Override
+ public void doEnd() {
+ System.out.println("multicaster evnet end ....");
+ }
+ }
+ ```
+
+
+
+#### 5、测试
+
+```java
+public class Test {
+
+ public static void main(String[] args) {
+ // 创建事件广播器
+ WeatherEventMulticaster multicaster = new WeatherEventMulticaster();
+
+ // 添加事件监听器
+ WeatherListener listener1 = new SnowListener();
+ WeatherListener listener2 = new RainListener();
+ multicaster.addListener(listener1);
+ multicaster.addListener(listener2);
+
+ // 广播事件
+ WeatherEvent event1 = new RainEvent();
+ WeatherEvent event2 = new SnowEvent();
+ multicaster.multicastEvent(event1);
+ multicaster.multicastEvent(event2);
+ }
+}
+```
+
diff --git "a/springboot-source-code-analysis/(6)346円241円206円346円236円266円345円206円205円347円233円221円345円220円254円345円231円250円350円256円276円350円256円241円346円250円241円345円274円217円344円270円216円345円256円236円347円216円260円.md" "b/springboot-source-code-analysis/(6)346円241円206円346円236円266円345円206円205円347円233円221円345円220円254円345円231円250円350円256円276円350円256円241円346円250円241円345円274円217円344円270円216円345円256円236円347円216円260円.md"
new file mode 100644
index 0000000..cc1a143
--- /dev/null
+++ "b/springboot-source-code-analysis/(6)346円241円206円346円236円266円345円206円205円347円233円221円345円220円254円345円231円250円350円256円276円350円256円241円346円250円241円345円274円217円344円270円216円345円256円236円347円216円260円.md"
@@ -0,0 +1,123 @@
+## SpringBoot监听器实现
+
+
+
+### SpringBoot 系统监听器介绍
+
+```java
+/**
+ * Interface to be implemented by application event listeners.
+ * Based on the standard {@code java.util.EventListener} interface
+ * for the Observer design pattern.
+ *
+ * As of Spring 3.0, an ApplicationListener can generically declare the event type
+ * that it is interested in. When registered with a Spring ApplicationContext, events
+ * will be filtered accordingly, with the listener getting invoked for matching event
+ * objects only.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @param the specific ApplicationEvent subclass to listen to
+ * @see org.springframework.context.event.ApplicationEventMulticaster
+ */
+@FunctionalInterface
+public interface ApplicationListener extends EventListener {
+
+ /**
+ * Handle an application event.
+ * @param event the event to respond to
+ */
+ void onApplicationEvent(E event);
+}
+```
+
+- SpringBoot中可以通过实现ApplicationListener接口类,来完成系统事件的监听
+- 这个监听器是机遇java.util.EventListener标准来实现的,是一个ObServer观察者模式
+- 从Spring3.0开始,ApplicationListener可以通过声明感兴趣的事件进行触发
+
+
+
+### SpringBoot 系统广播器介绍
+
+ ```java
+ /**
+ * Interface to be implemented by objects that can manage a number of
+ * {@link ApplicationListener} objects, and publish events to them.
+ *
+ * An {@link org.springframework.context.ApplicationEventPublisher}, typically
+ * a Spring {@link org.springframework.context.ApplicationContext}, can use an
+ * ApplicationEventMulticaster as a delegate for actually publishing events.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @author Stephane Nicoll
+ */
+ public interface ApplicationEventMulticaster {
+ ```
+
+- 用来管理ApplicationListener事件监听器,比如事件监听器的添加、删除等
+- 用来遍历所有的ApplicationListener事件监听器,触发ApplicationEvent事件
+
+
+
+#### (1)Spring 系统广播器的具体方法介绍
+
+```java
+public interface ApplicationEventMulticaster {
+
+ // 添加一个 ApplicationListener 事件监听器
+ void addApplicationListener(ApplicationListener> listener);
+
+ // 删除一个 ApplicationListener 事件监听器
+ void removeApplicationListener(ApplicationListener> listener);
+
+ // 删除所有事件监听器
+ void removeAllListeners();
+
+ // 广播 ApplicationEvent 事件
+ void multicastEvent(ApplicationEvent event);
+}
+```
+
+
+
+### Spring 系统事件介绍
+
+image-20210803171306277
+
+- EventObject:具体的事件描述
+- ApplicationEvent:应用事件描述
+- SpringApplicationEvent:框架应用事件描述
+- ApplicationContextInitializedEvent:框架应用上下文初始化阶段的事件
+- ApplicationEnvironmentPreparedEvent:框架应用环境属性准备好阶段的事件
+- ApplicationFeiledEvent:框架启动失败时的事件
+- ApplicationPreparedEvent:框架准备阶段的事件
+- ApplicationReadyEvent:框架准备好时的事件
+- ApplicationStartedEvent:框架启动完成时的事件
+- ApplicationStartingEvent:框架启动中的事件
+
+
+
+### Spring 事件发送的顺序
+
+image-20210803172047686
+
+- 框架准备启动
+- 触发ApplicationStaringEvent事件,告知框架已开始启动
+- 触发ApplicationEnvironmentPreparedEvent事件,告知框架环境属性、我们指定的一些属性已经准备完成
+- 触发ApplicationContextInitializedEvent事件,告知框架ApplicationContextInitializer的系统上下文已经初始化好
+- Bean的准备、启动、准备好阶段,会分别触发ApplicationPreparedEvent、ApplicationStartedEvent、ApplicationReadyEvent事件;
+- 整个SpringBoot启动失败时,会触发ApplicationFailedEvent事件;
+- 框架启动完毕
+
+
+
+### 监听器注册
+
+image-20210809153150882
+
+- 定义SpringApplicationListener接口实现类
+- 将SpringApplicationListeners接口实现类配置到spring.factories配置文件中
+- 通过SpringFactoriesLoader系统工程加载类加载spring.factories配置文件中的SpringApplicationListeners接口实现类
+- 将SpringApplicationListeners初始化后放入this.listeners属性中进行排序
+
diff --git "a/springboot-source-code-analysis/(7)344円272円213円344円273円266円347円233円221円345円220円254円345円231円250円350円247円246円345円217円221円346円234円272円345円210円266円350円247円243円346円236円220円.md" "b/springboot-source-code-analysis/(7)344円272円213円344円273円266円347円233円221円345円220円254円345円231円250円350円247円246円345円217円221円346円234円272円345円210円266円350円247円243円346円236円220円.md"
new file mode 100644
index 0000000..de9c491
--- /dev/null
+++ "b/springboot-source-code-analysis/(7)344円272円213円344円273円266円347円233円221円345円220円254円345円231円250円350円247円246円345円217円221円346円234円272円345円210円266円350円247円243円346円236円220円.md"
@@ -0,0 +1,372 @@
+### 事件监听器触发机制解析
+
+
+
+#### 以SpringBoot Staring事件具例子
+
+- SpringContext#run方法中的,listeners.staring()方法
+
+```java
+ /**
+ * Run the Spring application, creating and refreshing a new
+ * {@link ApplicationContext}.
+ * @param args the application arguments (usually passed from a Java main method)
+ * @return a running {@link ApplicationContext}
+ */
+ public ConfigurableApplicationContext run(String... args) {
+ StopWatch stopWatch = new StopWatch();
+ stopWatch.start();
+ ConfigurableApplicationContext context = null;
+ Collection exceptionReporters = new ArrayList();
+ configureHeadlessProperty();
+ SpringApplicationRunListeners listeners = getRunListeners(args);
+ // 告知框架已经开始启动
+ listeners.starting();
+ try {
+```
+
+
+
+- SpringApplicationRunListeners#starting() 内部方法
+
+```java
+ public void starting() {
+ // 调用 SpringApplicationRunListenser#staring() 方法
+ for (SpringApplicationRunListener listener : this.listeners) {
+ listener.starting();
+ }
+ }
+```
+
+
+
+- SpringApplicationRunListener方法,定义了框架各个阶段的事件监听方法
+
+```java
+/**
+ * Listener for the {@link SpringApplication} {@code run} method.
+ * {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}
+ * and should declare a public constructor that accepts a {@link SpringApplication}
+ * instance and a {@code String[]} of arguments. A new
+ * {@link SpringApplicationRunListener} instance will be created for each run.
+ *
+ * @author Phillip Webb
+ * @author Dave Syer
+ * @author Andy Wilkinson
+ * @since 1.0.0
+ */
+public interface SpringApplicationRunListener {
+
+ /**
+ * Called immediately when the run method has first started. Can be used for very
+ * early initialization.
+ */
+ void starting();
+
+ /**
+ * Called once the environment has been prepared, but before the
+ * {@link ApplicationContext} has been created.
+ * @param environment the environment
+ */
+ void environmentPrepared(ConfigurableEnvironment environment);
+
+ /**
+ * Called once the {@link ApplicationContext} has been created and prepared, but
+ * before sources have been loaded.
+ * @param context the application context
+ */
+ void contextPrepared(ConfigurableApplicationContext context);
+
+ /**
+ * Called once the application context has been loaded but before it has been
+ * refreshed.
+ * @param context the application context
+ */
+ void contextLoaded(ConfigurableApplicationContext context);
+
+ /**
+ * The context has been refreshed and the application has started but
+ * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
+ * ApplicationRunners} have not been called.
+ * @param context the application context.
+ * @since 2.0.0
+ */
+ void started(ConfigurableApplicationContext context);
+
+ /**
+ * Called immediately before the run method finishes, when the application context has
+ * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
+ * {@link ApplicationRunner ApplicationRunners} have been called.
+ * @param context the application context.
+ * @since 2.0.0
+ */
+ void running(ConfigurableApplicationContext context);
+
+ /**
+ * Called when a failure occurs when running the application.
+ * @param context the application context or {@code null} if a failure occurred before
+ * the context was created
+ * @param exception the failure
+ * @since 2.0.0
+ */
+ void failed(ConfigurableApplicationContext context, Throwable exception);
+
+}
+```
+
+
+
+- SpringApplicationRunListener#starting() 方法,通过 SimpleApplicationEventMulticaster 广播器发送 ApplicationStartingEvent 事件
+
+```java
+/**
+ * {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s.
+ *
+ * Uses an internal {@link ApplicationEventMulticaster} for the events that are fired
+ * before the context is actually refreshed.
+ *
+ * @author Phillip Webb
+ * @author Stephane Nicoll
+ * @author Andy Wilkinson
+ * @author Artsiom Yudovin
+ * @since 1.0.0
+ */
+public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
+
+ private final SpringApplication application;
+
+ private final String[] args;
+
+ private final SimpleApplicationEventMulticaster initialMulticaster;
+
+ 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);
+ }
+ }
+
+ @Override
+ public void starting() {
+ // 通过广播器,发送ApplicationStaringEvent事件
+ this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
+ }
+
+```
+
+
+
+- SimpleApplicationEventMulticaster#multicastEvent() 广播器发送方法
+
+```java
+public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
+
+ @Override
+ public void multicastEvent(ApplicationEvent event) {
+ multicastEvent(event, resolveDefaultEventType(event));
+ }
+
+ @Override
+ public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
+ ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
+ Executor executor = getTaskExecutor();
+ // 通过AbstractApplicationEventMulticaster#getApplicationListeners() 方法获得对该事件感兴趣的监听器列表
+ for (ApplicationListener> listener : getApplicationListeners(event, type)) {
+ if (executor != null) {
+ executor.execute(() -> invokeListener(listener, event));
+ }
+ else {
+ invokeListener(listener, event);
+ }
+ }
+ }
+```
+
+
+
+- AbstractApplicationEventMulticaster#getApplicationListeners() 方法,获得对该事件感兴趣的监听器列表
+
+```java
+ /**
+ * Return a Collection of ApplicationListeners matching the given
+ * event type. Non-matching listeners get excluded early.
+ * @param event the event to be propagated. Allows for excluding
+ * non-matching listeners early, based on cached matching information.
+ * @param eventType the event type
+ * @return a Collection of ApplicationListeners
+ * @see org.springframework.context.ApplicationListener
+ */
+ protected Collection> getApplicationListeners(
+ ApplicationEvent event, ResolvableType eventType) {
+
+ Object source = event.getSource();
+ Class> sourceType = (source != null ? source.getClass() : null);
+ ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
+
+ // Quick check for existing entry on ConcurrentHashMap...
+ ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
+ if (retriever != null) {
+ return retriever.getApplicationListeners();
+ }
+
+ if (this.beanClassLoader == null ||
+ (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
+ (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
+ // Fully synchronized building and caching of a ListenerRetriever
+ // 避免其它线程操作ListenersCache缓存
+ synchronized (this.retrievalMutex) {
+ retriever = this.retrieverCache.get(cacheKey);
+ // 双重检测锁
+ if (retriever != null) {
+ return retriever.getApplicationListeners();
+ }
+ retriever = new ListenerRetriever(true);
+ // 通过AbstractApplicationEventMulicaster#retrieveApplicationListeners()获取感兴趣的事件监听列表
+ Collection> listeners =
+ retrieveApplicationListeners(eventType, sourceType, retriever);
+ this.retrieverCache.put(cacheKey, retriever);
+ return listeners;
+ }
+ }
+ else {
+ // No ListenerRetriever caching -> no synchronization necessary
+ return retrieveApplicationListeners(eventType, sourceType, null);
+ }
+ }
+```
+
+
+
+- 如果 this.retrieverCache 缓存获取不到 监听器列表,就从AbstractApplicationEventMulticaster#retrieveApplicationListeners()中获取
+
+```java
+/**
+ * Actually retrieve the application listeners for the given event and source type.
+ * @param eventType the event type
+ * @param sourceType the event source type
+ * @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes)
+ * @return the pre-filtered list of application listeners for the given event and source type
+ */
+ private Collection> retrieveApplicationListeners(
+ ResolvableType eventType, @Nullable Class> sourceType, @Nullable ListenerRetriever retriever) {
+
+ List> allListeners = new ArrayList();
+ Set> listeners;
+ Set listenerBeans;
+ // 通过互斥锁初始化Set对象
+ synchronized (this.retrievalMutex) {
+ listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
+ listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
+ }
+ /**
+ 获取所有事件监听器
+ 在 spring.factories 中定义的实践监听器
+ # Application Listeners
+ org.springframework.context.ApplicationListener=\
+ org.springframework.boot.ClearCachesApplicationListener,\
+ org.springframework.boot.builder.ParentContextCloserApplicationListener,\
+ org.springframework.boot.context.FileEncodingApplicationListener,\
+ org.springframework.boot.context.config.AnsiOutputApplicationListener,\
+ org.springframework.boot.context.config.ConfigFileApplicationListener,\
+ org.springframework.boot.context.config.DelegatingApplicationListener,\
+ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
+ org.springframework.boot.context.logging.LoggingApplicationListener,\
+ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
+ */
+ for (ApplicationListener> listener : listeners) {
+ // 判断是否对该事件感兴趣
+ if (supportsEvent(listener, eventType, sourceType)) {
+ if (retriever != null) {
+ retriever.applicationListeners.add(listener);
+ }
+ // 感兴趣就加入到集合当中
+ allListeners.add(listener);
+ }
+ }
+ if (!listenerBeans.isEmpty()) {
+ BeanFactory beanFactory = getBeanFactory();
+ for (String listenerBeanName : listenerBeans) {
+ try {
+ Class> listenerType = beanFactory.getType(listenerBeanName);
+ if (listenerType == null || supportsEvent(listenerType, eventType)) {
+ ApplicationListener> listener =
+ beanFactory.getBean(listenerBeanName, ApplicationListener.class);
+ if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
+ if (retriever != null) {
+ if (beanFactory.isSingleton(listenerBeanName)) {
+ retriever.applicationListeners.add(listener);
+ }
+ else {
+ retriever.applicationListenerBeans.add(listenerBeanName);
+ }
+ }
+ allListeners.add(listener);
+ }
+ }
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ // Singleton listener instance (without backing bean definition) disappeared -
+ // probably in the middle of the destruction phase
+ }
+ }
+ }
+ // 监听器排序
+ AnnotationAwareOrderComparator.sort(allListeners);
+ if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
+ retriever.applicationListeners.clear();
+ retriever.applicationListeners.addAll(allListeners);
+ }
+ return allListeners;
+ }
+```
+
+
+
+- 通过AbstractApplicationEventMuliticaster#supportEvent() 方法,判断该事件监听器,是否对该事件感兴趣?
+
+```java
+ /**
+ * Determine whether the given listener supports the given event.
+ * The default implementation detects the {@link SmartApplicationListener}
+ * and {@link GenericApplicationListener} interfaces. In case of a standard
+ * {@link ApplicationListener}, a {@link GenericApplicationListenerAdapter}
+ * will be used to introspect the generically declared type of the target listener.
+ * @param listener the target listener to check
+ * @param eventType the event type to check against
+ * @param sourceType the source type to check against
+ * @return whether the given listener should be included in the candidates
+ * for the given event type
+ */
+ protected boolean supportsEvent(
+ ApplicationListener> listener, ResolvableType eventType, @Nullable Class> sourceType) {
+
+ // 判断当前事件监听器是否是GenericApplicationListerner接口类的实现类,如果不是的话,就做个委派,方便获取该事件监听器感兴趣事件
+ GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
+ (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
+ // 判断当前监听器是否感兴趣该事件
+ return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
+ }
+```
+
+
+
+
+
+#### 获取监听器列表逻辑
+
+image-20210920200242597
+
+- 通过AbstractApplicationEventMulticaster#getApplicationListeners()方法获取感兴趣事件监听器列表
+- 判断当前SpringBoot缓存中是否存在该事件感兴趣的监听器列表
+- 如果没有,调用通过AbstractApplicationEventMulicaster#retrieveApplicationListener()方法,获取所有事件监听器列表
+- 遍历所有事件监听器列表
+- 通过AbstractApplicationEventMuliticaster#supportsEvent() 方法查找出对该事件感兴趣的事件监听器
+- 将感兴趣的事件监听器列表加入SpirngBoot缓存中,并返回给事件触发器
+
+
+
+#### AbstractApplicationEventMuliticaster#supportEvent() 事件监听器匹配器触发条件
+
+image-20210920201046825
\ No newline at end of file
diff --git "a/springboot-source-code-analysis/(8)350円207円252円345円256円232円344円271円211円347円233円221円345円220円254円345円231円250円345円256円236円346円210円230円.md" "b/springboot-source-code-analysis/(8)350円207円252円345円256円232円344円271円211円347円233円221円345円220円254円345円231円250円345円256円236円346円210円230円.md"
new file mode 100644
index 0000000..748b4c4
--- /dev/null
+++ "b/springboot-source-code-analysis/(8)350円207円252円345円256円232円344円271円211円347円233円221円345円220円254円345円231円250円345円256円236円346円210円230円.md"
@@ -0,0 +1,155 @@
+### 自定义监听器实战
+
+#### 实现方式一
+
+- 实现ApplicationListener接口,ApplicationListener声明感兴趣的事件
+
+```java
+@Order(1)
+public class FirstListener implements ApplicationListener {
+
+ private final static Logger logger = LoggerFactory.getLogger(FirstListener.class);
+
+ @Override
+ public void onApplicationEvent(ApplicationStartedEvent event) {
+ logger.info("设置框架事件监听器【ApplicationListener】成功 : run firstListener");
+
+ }
+}
+
+```
+
+- 在spring.factories 配置文件中配置FirstListener事件监听器,key值为org.springframework.context.ApplicationListener
+
+```java
+# 通过系统事件监听器,设置自定义事件监听器
+org.springframework.context.ApplicationListener=\
+ com.example.springboot.source.code.analysis.listener.FirstListener
+```
+
+
+
+#### 实现方式三
+
+- 实现ApplicationListener接口,声明感兴趣的事件ApplicationListener
+
+```java
+@Order(3)
+public class ThirdListener implements ApplicationListener {
+
+ private final static Logger logger = LoggerFactory.getLogger(ThirdListener.class);
+
+ @Override
+ public void onApplicationEvent(ApplicationStartedEvent event) {
+ logger.info("设置框架事件监听器【ApplicationListener】成功 : run thirdListener");
+ }
+}
+```
+
+- 在application.properties 内配置事件监听器,key值为context.listener.classes
+
+```java
+# 通过系统事件监听器,监听事件
+context.listener.classes=\
+ com.example.springboot.source.code.analysis.listener.ThirdListener,\
+```
+
+> application.properties 内配置的 conext.listener.classes 事件监听器执行优先级最高
+>
+> ```java
+> // context.listener.classes 是通过 DelegatingApplicationListener 委派给 ApplicationListener
+> public class DelegatingApplicationListener implements ApplicationListener, Ordered {
+> private static final String PROPERTY_NAME = "context.listener.classes";
+> // 在委派过程中,DelegatingApplicationListener 会将 order 优先级设置为最高
+> private int order = 0;
+> private SimpleApplicationEventMulticaster multicaster;
+>
+> ```
+
+
+
+#### 实现方式四
+
+- 实现SmartApplicationListener接口
+
+```java
+@Order(4)
+public class FourthListener implements SmartApplicationListener {
+
+ private final static Logger logger = LoggerFactory.getLogger(FourthListener.class);
+
+ // 通过重写 supportEventType()方法 判断该监听器对哪些事件感兴趣
+ @Override
+ public boolean supportsEventType(Class extends ApplicationEvent> eventType) {
+ return ApplicationStartedEvent.class.isAssignableFrom(eventType)
+ || ApplicationPreparedEvent.class.isAssignableFrom(eventType);
+ }
+
+ // 编写事件触发逻辑
+ @Override
+ public void onApplicationEvent(ApplicationEvent event) {
+ logger.info("设置框架事件监听器【SmartApplicationListener】成功 : run fourthListener");
+ }
+}
+
+```
+
+- 可以通过spring.factories 或 application.properties 或 new SpringApplication().addListeners() 三种方式注入该事件
+
+
+
+#### 通过 ContextRefershedEvent 获取 Spirng 上下文
+
+- 实现ApplicationListener接口,关注ContextRefershedEvent事件
+
+```java
+@Component
+@Order(1)
+public class ApplicationContextContainer implements ApplicationListener {
+
+ private ApplicationContext applicationContext;
+
+ private static ApplicationContextContainer applicationContextContainer;
+
+ // 当容器上下文刷新完成后,会触发此方法
+ @Override
+ public void onApplicationEvent(@Nullable ContextRefreshedEvent event) {
+ // 双重检测锁,获取当前容器上下文
+ if (event != null && applicationContext == null) {
+ synchronized (ApplicationContextContainer.class) {
+ if (applicationContext == null) {
+ applicationContext = event.getApplicationContext();
+ }
+ }
+ }
+ applicationContextContainer = this;
+ }
+
+ // 返回当前自定义容器类的实例
+ public static ApplicationContextContainer getInstance() {
+ return applicationContextContainer;
+ }
+
+ // 从 Spring 容器中获取 Bean
+ public T getBean(Class clazz) {
+ return applicationContext.getBean(clazz);
+ }
+}
+```
+
+- 测试,通过普通方法获取Spring容器中的Bean
+
+```java
+@SpringBootTest(classes = SpringbootSourceCodeAnalysisApplication.class)
+@RunWith(SpringJUnit4ClassRunner.class)
+public class SpringbootSourceCodeAnalysisApplicationTests {
+
+ // 测试ContextRefreshEvent事件
+ @Test
+ public void testContextRefreshEvent() {
+ WeatherRunListener weatherRunListener = ApplicationContextContainer.getInstance().getBean(WeatherRunListener.class);
+ weatherRunListener.snow();
+ weatherRunListener.rain();
+ }
+```
+
diff --git "a/springboot-source-code-analysis/(9)IoC346円200円235円346円203円263円.md" "b/springboot-source-code-analysis/(9)IoC346円200円235円346円203円263円.md"
new file mode 100644
index 0000000..f6d2ee9
--- /dev/null
+++ "b/springboot-source-code-analysis/(9)IoC346円200円235円346円203円263円.md"
@@ -0,0 +1,15 @@
+### IoC思想
+
+
+
+image-20210921195432177
+
+
+
+- 松耦合
+ - 降低强耦合关系,对象与对象之间的依赖不用new,而是通过Spring容器@Autowired注入完成
+- 灵活性
+ - 不需要初始化使用类的构造方法,而是通过Spring容器完成初始化工作
+- 可维护
+ - 通过Spring xml或注解 的方式,可以清晰的了解类与类之间的依赖关系,提升可读性
+
diff --git a/springboot-source-code-analysis/.gitignore b/springboot-source-code-analysis/.gitignore
new file mode 100644
index 0000000..549e00a
--- /dev/null
+++ b/springboot-source-code-analysis/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/springboot-source-code-analysis/.mvn/wrapper/MavenWrapperDownloader.java b/springboot-source-code-analysis/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000..a45eb6b
--- /dev/null
+++ b/springboot-source-code-analysis/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/springboot-source-code-analysis/.mvn/wrapper/maven-wrapper.jar b/springboot-source-code-analysis/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..2cc7d4a
Binary files /dev/null and b/springboot-source-code-analysis/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/springboot-source-code-analysis/.mvn/wrapper/maven-wrapper.properties b/springboot-source-code-analysis/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..ffdc10e
--- /dev/null
+++ b/springboot-source-code-analysis/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/springboot-source-code-analysis/mvnw b/springboot-source-code-analysis/mvnw
new file mode 100755
index 0000000..a16b543
--- /dev/null
+++ b/springboot-source-code-analysis/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - 0ドル may be a link to maven's home
+ PRG="0ドル"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*'> /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly.">&2
+ echo " We cannot execute $JAVACMD">&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "1ドル" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="1ドル"
+ wdir="1ドル"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "1ドル" ]; then
+ echo "$(tr -s '\n' ' ' < "1ドル")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget> /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl> /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/springboot-source-code-analysis/mvnw.cmd b/springboot-source-code-analysis/mvnw.cmd
new file mode 100644
index 0000000..c8d4337
--- /dev/null
+++ b/springboot-source-code-analysis/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment.>&2
+echo Please set the JAVA_HOME variable in your environment to match the>&2
+echo location of your Java installation.>&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory.>&2
+echo JAVA_HOME = "%JAVA_HOME%">&2
+echo Please set the JAVA_HOME variable in your environment to match the>&2
+echo location of your Java installation.>&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/springboot-source-code-analysis/pom.xml b/springboot-source-code-analysis/pom.xml
new file mode 100644
index 0000000..ce53e12
--- /dev/null
+++ b/springboot-source-code-analysis/pom.xml
@@ -0,0 +1,50 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.7.RELEASE
+
+
+ com.ipman.source.code.analysis.springboot
+ springboot-source-code-analysis
+ 0.0.1-SNAPSHOT
+ springboot-source-code-analysis
+ Demo project for Spring Boot
+
+ 1.8
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/SpringbootSourceCodeAnalysisApplication.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/SpringbootSourceCodeAnalysisApplication.java
new file mode 100644
index 0000000..af01364
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/SpringbootSourceCodeAnalysisApplication.java
@@ -0,0 +1,24 @@
+package com.example.springboot.source.code.analysis;
+
+import com.example.springboot.source.code.analysis.initializer.SecondInitializer;
+import com.example.springboot.source.code.analysis.listener.SecondListener;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringbootSourceCodeAnalysisApplication {
+
+ public static void main(String[] args) {
+ // SpringApplication.run(SpringbootSourceCodeAnalysisApplication.class, args);
+ SpringApplication springApplication = new SpringApplication(SpringbootSourceCodeAnalysisApplication.class);
+
+ // 手动添加一个框架的初始化器
+ springApplication.addInitializers(new SecondInitializer());
+
+ // 手动添加一个框架事件监听器
+ springApplication.addListeners(new SecondListener());
+
+ springApplication.run();
+
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/controller/InitializerController.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/controller/InitializerController.java
new file mode 100644
index 0000000..98366d8
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/controller/InitializerController.java
@@ -0,0 +1,54 @@
+package com.example.springboot.source.code.analysis.controller;
+
+import com.example.springboot.source.code.analysis.service.TestFirstInitializerService;
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * InitializerController 测试框架初始化器 ApplicationContextInitializer
+ *
+ * @author ipipman
+ * @version V1.0
+ * @date 2021年6月5日
+ * @date 2021年6月5日 8:45 下午
+ */
+@RequestMapping("/initializer")
+@RestController
+public class InitializerController {
+ /**
+ * 测试框架初始化器 ApplicationContextInitializer
+ */
+ @Autowired
+ private TestFirstInitializerService firstInitializerService;
+
+ /**
+ * 测试框架初始化器 ApplicationContextInitializer
+ * 通过在 META-INF/spring.factories 中配置 ApplicationContextInitializer 的方式
+ */
+ @RequestMapping(value = "first", method = RequestMethod.GET)
+ @ResponseBody
+ public String testFirstInitializer() {
+ return firstInitializerService.getCustomEnvironmentProperty("firstKey");
+ }
+
+ /**
+ * 测试框架初始化器 ApplicationContextInitializer
+ * 通过 springApplication.addInitializers(new SecondInitializer()) 的方式
+ */
+ @RequestMapping(value = "second", method = RequestMethod.GET)
+ @ResponseBody
+ public String testSecondInitializer() {
+ return firstInitializerService.getCustomEnvironmentProperty("secondKey");
+ }
+
+ /**
+ * 测试框架初始化器 ApplicationContextInitializer
+ * 通过在 application.property 中配置 context.initializer.classes 的方式
+ */
+ @RequestMapping(value = "third", method = RequestMethod.GET)
+ @ResponseBody
+ public String testThirdInitializer() {
+ return firstInitializerService.getCustomEnvironmentProperty("thirdKey");
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/AbstractEventMulticaster.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/AbstractEventMulticaster.java
new file mode 100644
index 0000000..b2a865c
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/AbstractEventMulticaster.java
@@ -0,0 +1,55 @@
+package com.example.springboot.source.code.analysis.event;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Created by ipipman on 2021年8月3日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月3日 3:20 下午
+ */
+
+
+// 抽象的事件广播器
+@Component
+public abstract class AbstractEventMulticaster implements EventMulticaster {
+
+ // 事件监听器列表
+ @Autowired
+ private List listenerList;
+
+ @Override
+ public void multicastEvent(WeatherEvent event) {
+ // 广播所有事件监听器
+ listenerList.forEach(listener -> {
+ doStart();
+ // 触发
+ listener.onWeatherEvent(event);
+ doEnd();
+ });
+ }
+
+ // 添加事件监听器
+ @Override
+ public void addListener(WeatherListener listener) {
+ listenerList.add(listener);
+ }
+
+ // 删除事件监听器
+ @Override
+ public void removeListener(WeatherListener listener) {
+ listenerList.remove(listener);
+ }
+
+
+ // 模版方法
+ abstract void doStart();
+
+ abstract void doEnd();
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/EventMulticaster.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/EventMulticaster.java
new file mode 100644
index 0000000..e94fc66
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/EventMulticaster.java
@@ -0,0 +1,23 @@
+package com.example.springboot.source.code.analysis.event;
+
+/**
+ * Created by ipipman on 2021年8月3日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月3日 3:04 下午
+ */
+
+// 事件广播器
+public interface EventMulticaster {
+
+ // 广播事件
+ void multicastEvent(WeatherEvent event);
+
+ // 添加监听器
+ void addListener(WeatherListener listener);
+
+ // 删除监听器
+ void removeListener(WeatherListener listener);
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/RainEvent.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/RainEvent.java
new file mode 100644
index 0000000..a6f127e
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/RainEvent.java
@@ -0,0 +1,19 @@
+package com.example.springboot.source.code.analysis.event;
+
+/**
+ * Created by ipipman on 2021年8月3日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月3日 2:52 下午
+ */
+
+// 下雨事件
+public class RainEvent extends WeatherEvent {
+
+ @Override
+ public String getWeather() {
+ return "rain";
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/RainListener.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/RainListener.java
new file mode 100644
index 0000000..37ca02a
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/RainListener.java
@@ -0,0 +1,25 @@
+package com.example.springboot.source.code.analysis.event;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by ipipman on 2021年8月3日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月3日 3:00 下午
+ */
+
+
+// 下雨监听器
+@Component
+public class RainListener implements WeatherListener {
+
+ @Override
+ public void onWeatherEvent(WeatherEvent event) {
+ if (event instanceof RainEvent) {
+ System.out.println("rain event ..." + event.getWeather());
+ }
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/SnowEvent.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/SnowEvent.java
new file mode 100644
index 0000000..c12c5ca
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/SnowEvent.java
@@ -0,0 +1,19 @@
+package com.example.springboot.source.code.analysis.event;
+
+/**
+ * Created by ipipman on 2021年8月3日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月3日 2:51 下午
+ */
+
+// 下雪事件
+public class SnowEvent extends WeatherEvent {
+
+ @Override
+ public String getWeather() {
+ return "snow";
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/SnowListener.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/SnowListener.java
new file mode 100644
index 0000000..579ee36
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/SnowListener.java
@@ -0,0 +1,24 @@
+package com.example.springboot.source.code.analysis.event;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by ipipman on 2021年8月3日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月3日 3:02 下午
+ */
+
+// 下雪监听器
+@Component
+public class SnowListener implements WeatherListener {
+
+ @Override
+ public void onWeatherEvent(WeatherEvent event) {
+ if (event instanceof SnowEvent) {
+ System.out.println("snow event.." + event.getWeather());
+ }
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/Test.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/Test.java
new file mode 100644
index 0000000..0aca9cc
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/Test.java
@@ -0,0 +1,32 @@
+package com.example.springboot.source.code.analysis.event;
+
+/**
+ * Created by ipipman on 2021年8月3日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月3日 3:29 下午
+ */
+
+
+public class Test {
+
+ public static void main(String[] args) {
+ // 创建事件广播器
+ WeatherEventMulticaster multicaster = new WeatherEventMulticaster();
+
+ // 添加事件监听器
+ WeatherListener listener1 = new SnowListener();
+ WeatherListener listener2 = new RainListener();
+ multicaster.addListener(listener1);
+ multicaster.addListener(listener2);
+
+ // 广播事件
+ WeatherEvent event1 = new RainEvent();
+ WeatherEvent event2 = new SnowEvent();
+ multicaster.multicastEvent(event1);
+ multicaster.multicastEvent(event2);
+ }
+
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherEvent.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherEvent.java
new file mode 100644
index 0000000..8238dcc
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherEvent.java
@@ -0,0 +1,19 @@
+package com.example.springboot.source.code.analysis.event;
+
+/**
+ * Created by ipipman on 2021年8月3日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月3日 2:50 下午
+ */
+
+
+// 天气事件抽象类
+public abstract class WeatherEvent {
+
+ // 获取当前天气的事件
+ public abstract String getWeather();
+
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherEventMulticaster.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherEventMulticaster.java
new file mode 100644
index 0000000..11bbfff
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherEventMulticaster.java
@@ -0,0 +1,27 @@
+package com.example.springboot.source.code.analysis.event;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by ipipman on 2021年8月3日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月3日 3:27 下午
+ */
+
+// 天气事件广播器
+@Component
+public class WeatherEventMulticaster extends AbstractEventMulticaster {
+
+ @Override
+ public void doStart() {
+ System.out.println("multicaster event begin ...");
+ }
+
+ @Override
+ public void doEnd() {
+ System.out.println("multicaster evnet end ....");
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherListener.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherListener.java
new file mode 100644
index 0000000..ff237ce
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherListener.java
@@ -0,0 +1,17 @@
+package com.example.springboot.source.code.analysis.event;
+
+/**
+ * Created by ipipman on 2021年8月3日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月3日 2:58 下午
+ */
+
+// 天气监听器
+public interface WeatherListener {
+
+ // 监听天气事件
+ void onWeatherEvent(WeatherEvent event);
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherRunListener.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherRunListener.java
new file mode 100644
index 0000000..a7a1530
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/event/WeatherRunListener.java
@@ -0,0 +1,35 @@
+package com.example.springboot.source.code.analysis.event;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by ipipman on 2021年8月9日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.event
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年8月9日 3:46 下午
+ */
+
+@Component
+public class WeatherRunListener {
+
+ @Autowired
+ private WeatherEventMulticaster weatherEventMulticaster;
+
+ // 下雪事件
+ public void snow(){
+ weatherEventMulticaster.multicastEvent(new SnowEvent());
+ }
+
+ // 下雨事件
+ public void rain(){
+ weatherEventMulticaster.multicastEvent(new RainEvent());
+ }
+
+ // 天津监听器
+ public void addListener(WeatherListener listener){
+ weatherEventMulticaster.addListener(listener);
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/initializer/FirstInitializer.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/initializer/FirstInitializer.java
new file mode 100644
index 0000000..38edbf3
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/initializer/FirstInitializer.java
@@ -0,0 +1,45 @@
+package com.example.springboot.source.code.analysis.initializer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.MapPropertySource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * FirstInitializer 系统初始化器
+ *
+ * @author ipipman
+ * @version V1.0
+ * @date 2021年6月5日
+ * @date 2021年6月5日 8:20 下午
+ */
+@Order(1) // 设置执行顺序
+public class FirstInitializer implements ApplicationContextInitializer {
+
+ private static final Logger logger = LoggerFactory.getLogger(FirstInitializer.class);
+
+ @Override
+ public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
+ // 获取系统属性
+ ConfigurableEnvironment environment =
+ configurableApplicationContext.getEnvironment();
+
+ // 设置必填属性
+ environment.setRequiredProperties("app.enviroment");
+
+ // 设置自定义属性
+ Map attributeMap = new HashMap();
+ attributeMap.put("firstKey", "firstValue");
+
+ // 添加自定义属性
+ MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", attributeMap);
+ environment.getPropertySources().addLast(mapPropertySource);
+ logger.info("设置框架初始化器【ApplicationContextInitializer】成功 : run firstInitializer");
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/initializer/SecondInitializer.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/initializer/SecondInitializer.java
new file mode 100644
index 0000000..149ea8a
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/initializer/SecondInitializer.java
@@ -0,0 +1,42 @@
+package com.example.springboot.source.code.analysis.initializer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.MapPropertySource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * FirstInitializer 系统初始化器
+ *
+ * @author ipipman
+ * @version V1.0
+ * @date 2021年6月5日
+ * @date 2021年6月5日 8:20 下午
+ */
+@Order(2) // 设置执行顺序
+public class SecondInitializer implements ApplicationContextInitializer {
+
+ private static final Logger logger = LoggerFactory.getLogger(SecondInitializer.class);
+
+ @Override
+ public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
+ // 获取系统属性
+ ConfigurableEnvironment environment =
+ configurableApplicationContext.getEnvironment();
+
+ // 设置自定义属性
+ Map attributeMap = new HashMap();
+ attributeMap.put("secondKey", "secondValue");
+
+ // 添加自定义属性
+ MapPropertySource mapPropertySource = new MapPropertySource("secondInitializer", attributeMap);
+ environment.getPropertySources().addLast(mapPropertySource);
+ logger.info("设置框架初始化器【ApplicationContextInitializer】成功 : run secondInitializer");
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/initializer/ThirdInitializer.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/initializer/ThirdInitializer.java
new file mode 100644
index 0000000..62f062b
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/initializer/ThirdInitializer.java
@@ -0,0 +1,42 @@
+package com.example.springboot.source.code.analysis.initializer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.MapPropertySource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * FirstInitializer 系统初始化器
+ *
+ * @author ipipman
+ * @version V1.0
+ * @date 2021年6月5日
+ * @date 2021年6月5日 8:20 下午
+ */
+@Order(3) // 设置执行顺序
+public class ThirdInitializer implements ApplicationContextInitializer {
+
+ private static final Logger logger = LoggerFactory.getLogger(ThirdInitializer.class);
+
+ @Override
+ public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
+ // 获取系统属性
+ ConfigurableEnvironment environment =
+ configurableApplicationContext.getEnvironment();
+
+ // 设置自定义属性
+ Map attributeMap = new HashMap();
+ attributeMap.put("thirdKey", "thirdValue");
+
+ // 添加自定义属性
+ MapPropertySource mapPropertySource = new MapPropertySource("thirdInitializer", attributeMap);
+ environment.getPropertySources().addLast(mapPropertySource);
+ logger.info("设置框架初始化器【ApplicationContextInitializer】成功 : run thirdInitializer");
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyBeanConfiguration.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyBeanConfiguration.java
new file mode 100644
index 0000000..547e15c
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyBeanConfiguration.java
@@ -0,0 +1,27 @@
+package com.example.springboot.source.code.analysis.ioc.ann;
+
+import com.example.springboot.source.code.analysis.ioc.pojo.Animal;
+import com.example.springboot.source.code.analysis.ioc.pojo.Dog;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Created by ipipman on 2021年10月16日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.ann
+ * @Description: (通过 @Configuration注解 注入一个Bean)
+ * @date 2021年10月16日 12:04 下午
+ */
+@Configuration
+public class MyBeanConfiguration {
+
+ /**
+ * 通过 @Configuration注解 注入一个Bean
+ */
+ @Bean("dog")
+ public Animal getDog() {
+ return new Dog();
+ }
+
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyBeanDefinitionRegistry.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyBeanDefinitionRegistry.java
new file mode 100644
index 0000000..ab84f26
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyBeanDefinitionRegistry.java
@@ -0,0 +1,37 @@
+package com.example.springboot.source.code.analysis.ioc.ann;
+
+import com.example.springboot.source.code.analysis.ioc.pojo.Monkey;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+
+/**
+ * Created by ipipman on 2021年10月16日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.ann
+ * @Description: (通过BeanDefinitionRegistryPostProcessor 进行Bean的注入)
+ * @date 2021年10月16日 3:18 下午
+ */
+@Component
+public class MyBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {
+
+ /**
+ * 通过BeanDefinitionRegistryPostProcessor
+ */
+ @Override
+ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
+ RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
+ rootBeanDefinition.setBeanClass(Monkey.class);
+ beanDefinitionRegistry.registerBeanDefinition("monkey", rootBeanDefinition);
+ }
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
+
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyBeanFacotoryPostProcessor.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyBeanFacotoryPostProcessor.java
new file mode 100644
index 0000000..a08d25e
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyBeanFacotoryPostProcessor.java
@@ -0,0 +1,32 @@
+package com.example.springboot.source.code.analysis.ioc.ann;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by ipipman on 2021年10月17日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.ann
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年10月17日 6:47 下午
+ */
+
+// 调用时机:在BeanFactory标准初始化之后调用,这时所有的Bean定义已经保存加载到BeanFactory,但是Bean的实例还未创建
+// 作用:来定制和修改BeanFactory内容,如覆盖或添加属性
+@Component
+public class MyBeanFacotoryPostProcessor implements BeanFactoryPostProcessor {
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
+ // 给DemoBean添加属性
+ BeanDefinition beanDemo = configurableListableBeanFactory.getBeanDefinition("demoBean");
+ MutablePropertyValues propertyValues = beanDemo.getPropertyValues();
+ propertyValues.addPropertyValue("name", "ipman");
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyFactoryBean.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyFactoryBean.java
new file mode 100644
index 0000000..486a909
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyFactoryBean.java
@@ -0,0 +1,41 @@
+package com.example.springboot.source.code.analysis.ioc.ann;
+
+import com.example.springboot.source.code.analysis.ioc.pojo.Animal;
+import com.example.springboot.source.code.analysis.ioc.pojo.Cat;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by ipipman on 2021年10月16日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.ann
+ * @Description: (通过实现 FactoryBean < ?> 接口实现Bean的注入)
+ * @date 2021年10月16日 12:15 下午
+ */
+@Component
+public class MyFactoryBean implements FactoryBean {
+
+ /**
+ * 返回要注入的类
+ */
+ @Override
+ public Animal getObject() throws Exception {
+ return new Cat();
+ }
+
+ /**
+ * 获取要注入类的类型
+ */
+ @Override
+ public Class> getObjectType() {
+ return Animal.class;
+ }
+
+ @Override
+ public boolean isSingleton() {
+ return FactoryBean.super.isSingleton();
+ }
+
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyImportBeanDefinitionRegistrar.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyImportBeanDefinitionRegistrar.java
new file mode 100644
index 0000000..f04aca8
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/ann/MyImportBeanDefinitionRegistrar.java
@@ -0,0 +1,28 @@
+package com.example.springboot.source.code.analysis.ioc.ann;
+
+import com.example.springboot.source.code.analysis.ioc.pojo.Bird;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
+import org.springframework.core.type.AnnotationMetadata;
+
+/**
+ * Created by ipipman on 2021年10月16日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.ann
+ * @Description: (通过ImportBeanDefinitionRegistrar注入Bean)
+ * @date 2021年10月16日 3:32 下午
+ */
+public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
+
+ /**
+ * 通过ImportBeanDefinitionRegistrar注入Bean
+ */
+ @Override
+ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
+ RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
+ rootBeanDefinition.setBeanClass(Bird.class);
+ registry.registerBeanDefinition("bird", rootBeanDefinition);
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Animal.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Animal.java
new file mode 100644
index 0000000..74d5d78
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Animal.java
@@ -0,0 +1,14 @@
+package com.example.springboot.source.code.analysis.ioc.pojo;
+
+/**
+ * Created by ipipman on 2021年10月16日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.pojo
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年10月16日 12:06 下午
+ */
+public abstract class Animal {
+
+ public abstract String getName();
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Bird.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Bird.java
new file mode 100644
index 0000000..e65bf5e
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Bird.java
@@ -0,0 +1,16 @@
+package com.example.springboot.source.code.analysis.ioc.pojo;
+
+/**
+ * Created by ipipman on 2021年10月16日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.pojo
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年10月16日 3:35 下午
+ */
+public class Bird extends Animal{
+ @Override
+ public String getName() {
+ return "Bird";
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Cat.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Cat.java
new file mode 100644
index 0000000..79bb1ab
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Cat.java
@@ -0,0 +1,16 @@
+package com.example.springboot.source.code.analysis.ioc.pojo;
+
+/**
+ * Created by ipipman on 2021年10月16日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.pojo
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年10月16日 12:09 下午
+ */
+public class Cat extends Animal{
+ @Override
+ public String getName() {
+ return "Cat";
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/DemoBean.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/DemoBean.java
new file mode 100644
index 0000000..72f65ee
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/DemoBean.java
@@ -0,0 +1,27 @@
+package com.example.springboot.source.code.analysis.ioc.pojo;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by ipipman on 2021年10月17日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.pojo
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年10月17日 6:53 下午
+ */
+@Component
+public class DemoBean {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Dog.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Dog.java
new file mode 100644
index 0000000..5dcd836
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Dog.java
@@ -0,0 +1,16 @@
+package com.example.springboot.source.code.analysis.ioc.pojo;
+
+/**
+ * Created by ipipman on 2021年10月16日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.pojo
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年10月16日 12:08 下午
+ */
+public class Dog extends Animal{
+ @Override
+ public String getName() {
+ return "Dog";
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Monkey.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Monkey.java
new file mode 100644
index 0000000..7e922ad
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/pojo/Monkey.java
@@ -0,0 +1,16 @@
+package com.example.springboot.source.code.analysis.ioc.pojo;
+
+/**
+ * Created by ipipman on 2021年10月16日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.pojo
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年10月16日 12:25 下午
+ */
+public class Monkey extends Animal {
+ @Override
+ public String getName() {
+ return "Monkey";
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/service/HelloService.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/service/HelloService.java
new file mode 100644
index 0000000..ec6f7d0
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/ioc/service/HelloService.java
@@ -0,0 +1,37 @@
+package com.example.springboot.source.code.analysis.ioc.service;
+
+import com.example.springboot.source.code.analysis.ioc.pojo.Animal;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import javax.swing.event.AncestorEvent;
+
+/**
+ * Created by ipipman on 2021年10月16日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.ioc.service
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年10月16日 12:10 下午
+ */
+
+@Component
+public class HelloService {
+
+ /**
+ * animal
+ */
+ @Autowired
+ // 如果有多个同类型的Bean,那么用@Qualifier指定Bean的名称后进行注入
+ //@Qulifier("dog") // 通过@Configuration注解进行Bean的注入
+ //@Qualifier("myFactoryBean") // 通过FacotryBean>接口进行Bean的注入
+ //@Qualifier("monkey") // 通过BeanDefinitionRegistryPostProcessor接口进行Bean的注入
+ @Qualifier("bird") // 通过ImpoortBeanDefinitionRegistrar接口进行Bean的注入
+ private Animal animal;
+
+
+ public String hello() {
+ return animal.getName();
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/ApplicationContextContainer.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/ApplicationContextContainer.java
new file mode 100644
index 0000000..adaea1d
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/ApplicationContextContainer.java
@@ -0,0 +1,45 @@
+package com.example.springboot.source.code.analysis.listener;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.core.annotation.Order;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+
+/**
+ * Created by ipipman on 2021年9月21日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.listener
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年9月21日 5:49 下午
+ */
+@Component
+@Order(1)
+public class ApplicationContextContainer implements ApplicationListener {
+
+ private ApplicationContext applicationContext;
+
+ private static ApplicationContextContainer applicationContextContainer;
+
+ @Override
+ public void onApplicationEvent(@Nullable ContextRefreshedEvent event) {
+ if (event != null && applicationContext == null) {
+ synchronized (ApplicationContextContainer.class) {
+ if (applicationContext == null) {
+ applicationContext = event.getApplicationContext();
+ }
+ }
+ }
+ applicationContextContainer = this;
+ }
+
+ public static ApplicationContextContainer getInstance() {
+ return applicationContextContainer;
+ }
+
+ public T getBean(Class clazz) {
+ return applicationContext.getBean(clazz);
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/FirstListener.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/FirstListener.java
new file mode 100644
index 0000000..29be824
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/FirstListener.java
@@ -0,0 +1,28 @@
+package com.example.springboot.source.code.analysis.listener;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.annotation.Order;
+
+/**
+ * Created by ipipman on 2021年9月21日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.listener
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年9月21日 4:12 下午
+ */
+
+@Order(1)
+public class FirstListener implements ApplicationListener {
+
+ private final static Logger logger = LoggerFactory.getLogger(FirstListener.class);
+
+ @Override
+ public void onApplicationEvent(ApplicationStartedEvent event) {
+ logger.info("设置框架事件监听器【ApplicationListener】成功 : run firstListener");
+
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/FourthListener.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/FourthListener.java
new file mode 100644
index 0000000..6b8eaba
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/FourthListener.java
@@ -0,0 +1,34 @@
+package com.example.springboot.source.code.analysis.listener;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.event.ApplicationPreparedEvent;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.event.SmartApplicationListener;
+import org.springframework.core.annotation.Order;
+
+/**
+ * Created by ipipman on 2021年9月21日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.listener
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年9月21日 4:26 下午
+ */
+@Order(4)
+public class FourthListener implements SmartApplicationListener {
+
+ private final static Logger logger = LoggerFactory.getLogger(FourthListener.class);
+
+ @Override
+ public boolean supportsEventType(Class extends ApplicationEvent> eventType) {
+ return ApplicationStartedEvent.class.isAssignableFrom(eventType)
+ || ApplicationPreparedEvent.class.isAssignableFrom(eventType);
+ }
+
+ @Override
+ public void onApplicationEvent(ApplicationEvent event) {
+ logger.info("设置框架事件监听器【SmartApplicationListener】成功 : run fourthListener");
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/SecondListener.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/SecondListener.java
new file mode 100644
index 0000000..dd54f03
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/SecondListener.java
@@ -0,0 +1,26 @@
+package com.example.springboot.source.code.analysis.listener;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.annotation.Order;
+
+/**
+ * Created by ipipman on 2021年9月21日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.listener
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年9月21日 4:19 下午
+ */
+@Order(2)
+public class SecondListener implements ApplicationListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(SecondListener.class);
+
+ @Override
+ public void onApplicationEvent(ApplicationStartedEvent event) {
+ logger.info("设置框架事件监听器【ApplicationListener】成功 : run secondListener");
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/ThirdListener.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/ThirdListener.java
new file mode 100644
index 0000000..1c69a4b
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/listener/ThirdListener.java
@@ -0,0 +1,27 @@
+package com.example.springboot.source.code.analysis.listener;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.annotation.Order;
+
+
+/**
+ * Created by ipipman on 2021年9月21日.
+ *
+ * @version V1.0
+ * @Package com.example.springboot.source.code.analysis.listener
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年9月21日 4:21 下午
+ */
+@Order(3)
+public class ThirdListener implements ApplicationListener {
+
+ private final static Logger logger = LoggerFactory.getLogger(ThirdListener.class);
+
+ @Override
+ public void onApplicationEvent(ApplicationStartedEvent event) {
+ logger.info("设置框架事件监听器【ApplicationListener】成功 : run thirdListener");
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/service/TestFirstInitializerService.java b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/service/TestFirstInitializerService.java
new file mode 100644
index 0000000..1a62f1b
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/java/com/example/springboot/source/code/analysis/service/TestFirstInitializerService.java
@@ -0,0 +1,45 @@
+package com.example.springboot.source.code.analysis.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+/**
+ * 测试框架初始化器,获取自定义的环境属性
+ *
+ * @author ipipman
+ * @version V1.0
+ * @date 2021年6月5日
+ * @date 2021年6月5日 8:35 下午
+ */
+@Component
+public class TestFirstInitializerService implements ApplicationContextAware {
+
+ private static final Logger logger = LoggerFactory.getLogger(TestFirstInitializerService.class);
+
+ /**
+ * 框架应用上下文
+ */
+ private ApplicationContext applicationContext;
+
+ /**
+ * 获取当前框架上下文
+ */
+ @Override
+ public void setApplicationContext(@Nullable ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
+ /**
+ * 获取在框架初始化器阶段设置的配置信息
+ */
+ public String getCustomEnvironmentProperty(final String propertyKey) {
+ return Optional.ofNullable(applicationContext.getEnvironment().getProperty(propertyKey)).orElse(null);
+ }
+}
diff --git a/springboot-source-code-analysis/src/main/resources/META-INF/spring.factories b/springboot-source-code-analysis/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..b386fd2
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,8 @@
+# 通过系统初始化器,设置自定义环境属性
+org.springframework.context.ApplicationContextInitializer=\
+ com.example.springboot.source.code.analysis.initializer.FirstInitializer
+
+
+# 通过系统事件监听器,设置自定义事件监听器
+org.springframework.context.ApplicationListener=\
+ com.example.springboot.source.code.analysis.listener.FirstListener
\ No newline at end of file
diff --git a/springboot-source-code-analysis/src/main/resources/application.properties b/springboot-source-code-analysis/src/main/resources/application.properties
new file mode 100644
index 0000000..856742a
--- /dev/null
+++ b/springboot-source-code-analysis/src/main/resources/application.properties
@@ -0,0 +1,12 @@
+# 通过系统初始化器,设置环境属性
+context.initializer.classes=\
+ com.example.springboot.source.code.analysis.initializer.ThirdInitializer
+
+# 通过系统事件监听器,监听事件
+context.listener.classes=\
+ com.example.springboot.source.code.analysis.listener.ThirdListener,\
+ com.example.springboot.source.code.analysis.listener.FourthListener
+
+
+# 通过ConfigurableEnviroment.requiredProperty()方法设置的必填属性
+app.enviroment=ipman
\ No newline at end of file
diff --git a/springboot-source-code-analysis/src/test/java/com/example/springboot/source/code/analysis/SpringbootSourceCodeAnalysisApplicationTests.java b/springboot-source-code-analysis/src/test/java/com/example/springboot/source/code/analysis/SpringbootSourceCodeAnalysisApplicationTests.java
new file mode 100644
index 0000000..b78658e
--- /dev/null
+++ b/springboot-source-code-analysis/src/test/java/com/example/springboot/source/code/analysis/SpringbootSourceCodeAnalysisApplicationTests.java
@@ -0,0 +1,68 @@
+package com.example.springboot.source.code.analysis;
+
+import com.example.springboot.source.code.analysis.event.RainListener;
+import com.example.springboot.source.code.analysis.event.WeatherRunListener;
+import com.example.springboot.source.code.analysis.ioc.ann.MyImportBeanDefinitionRegistrar;
+import com.example.springboot.source.code.analysis.ioc.pojo.DemoBean;
+import com.example.springboot.source.code.analysis.ioc.service.HelloService;
+import com.example.springboot.source.code.analysis.listener.ApplicationContextContainer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+@SpringBootTest(classes = SpringbootSourceCodeAnalysisApplication.class)
+@RunWith(SpringJUnit4ClassRunner.class)
+// 通过XML注入Bean
+//@ContextConfiguration(locations = "classpath:ioc/demo.xml")
+// 通过ImportBeanDefinitionRegistrar进行Bean的注入
+@Import(MyImportBeanDefinitionRegistrar.class)
+public class SpringbootSourceCodeAnalysisApplicationTests {
+
+ // 引入事件监听器
+ @Autowired
+ private WeatherRunListener weatherRunListener;
+
+ // 测试事件监听器
+ @Test
+ public void testEvent() {
+ weatherRunListener.addListener(new RainListener());
+ weatherRunListener.snow();
+ weatherRunListener.rain();
+ }
+
+ // 测试ContextRefreshEvent事件
+ @Test
+ public void testContextRefreshEvent() {
+ WeatherRunListener weatherRunListener = ApplicationContextContainer.getInstance().getBean(WeatherRunListener.class);
+ weatherRunListener.snow();
+ weatherRunListener.rain();
+ }
+
+
+ @Autowired
+ HelloService helloService;
+
+ @Test
+ public void testHello() {
+ System.out.println(helloService.hello());
+ }
+
+
+ @Test
+ public void contextLoads() {
+ }
+
+
+ @Autowired
+ DemoBean demoBean;
+
+ @Test
+ public void sayDemoBean(){
+ System.out.println(demoBean.getName());
+ }
+
+
+}
diff --git a/springboot-swagger-sample/.gitignore b/springboot-swagger-sample/.gitignore
new file mode 100644
index 0000000..549e00a
--- /dev/null
+++ b/springboot-swagger-sample/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/springboot-swagger-sample/.mvn/wrapper/MavenWrapperDownloader.java b/springboot-swagger-sample/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000..a45eb6b
--- /dev/null
+++ b/springboot-swagger-sample/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/springboot-swagger-sample/.mvn/wrapper/maven-wrapper.jar b/springboot-swagger-sample/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..2cc7d4a
Binary files /dev/null and b/springboot-swagger-sample/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/springboot-swagger-sample/.mvn/wrapper/maven-wrapper.properties b/springboot-swagger-sample/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..abd303b
--- /dev/null
+++ b/springboot-swagger-sample/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/springboot-swagger-sample/mvnw b/springboot-swagger-sample/mvnw
new file mode 100755
index 0000000..a16b543
--- /dev/null
+++ b/springboot-swagger-sample/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - 0ドル may be a link to maven's home
+ PRG="0ドル"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*'> /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly.">&2
+ echo " We cannot execute $JAVACMD">&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "1ドル" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="1ドル"
+ wdir="1ドル"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "1ドル" ]; then
+ echo "$(tr -s '\n' ' ' < "1ドル")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget> /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl> /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/springboot-swagger-sample/mvnw.cmd b/springboot-swagger-sample/mvnw.cmd
new file mode 100644
index 0000000..c8d4337
--- /dev/null
+++ b/springboot-swagger-sample/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment.>&2
+echo Please set the JAVA_HOME variable in your environment to match the>&2
+echo location of your Java installation.>&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory.>&2
+echo JAVA_HOME = "%JAVA_HOME%">&2
+echo Please set the JAVA_HOME variable in your environment to match the>&2
+echo location of your Java installation.>&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/springboot-swagger-sample/pom.xml b/springboot-swagger-sample/pom.xml
new file mode 100644
index 0000000..ef7743a
--- /dev/null
+++ b/springboot-swagger-sample/pom.xml
@@ -0,0 +1,81 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.5.4
+
+
+ com.ipman.swagger.sample
+ springboot-swagger-sample
+ 0.0.1-SNAPSHOT
+ springboot-swagger-sample
+ Demo project for Spring Boot
+
+ 1.8
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ io.springfox
+ springfox-swagger2
+ 2.9.2
+
+
+
+ io.springfox
+ springfox-swagger-ui
+ 2.9.2
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.2
+ provided
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ com.ipman.swagger.sample.SpringbootSwaggerSampleApplication
+
+
+
+
+
+
diff --git a/springboot-swagger-sample/readme.md b/springboot-swagger-sample/readme.md
new file mode 100644
index 0000000..056622f
--- /dev/null
+++ b/springboot-swagger-sample/readme.md
@@ -0,0 +1,143 @@
+### SpringBoot集成Swagger用例
+
+
+
+#### 什么是 Swagger 2
+
+**Swagger 2** 是一个开源软件框架,可以帮助开发人员设计、构建、记录和使用 **RESTful Web** 服务,它将代码和文档融为一体,可以完美解决文档编写繁琐、维护不方便等问题。使得开发人员可以将大部分精力集中到业务中,而不是繁杂琐碎的文档中。
+
+
+
+ #### 在Maven工程中引入Swagger和Swagger UI
+
+```java
+
+ io.springfox
+ springfox-swagger2
+ 2.9.2
+
+
+
+ io.springfox
+ springfox-swagger-ui
+ 2.9.2
+
+```
+
+
+
+#### 配置Swagger Bean
+
+```java
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+ /**
+ * 注入SwaggerDocketBean
+ */
+ @Bean
+ public Docket docket() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .apiInfo(apiInfo())
+ .select()
+ .apis(RequestHandlerSelectors.basePackage("com.ipman.swagger.sample.controller"))
+ .paths(PathSelectors.any())
+ .build();
+ }
+
+ /**
+ * 配置Swagger文档说明
+ */
+ private ApiInfo apiInfo() {
+ return new ApiInfoBuilder()
+ .title("API测试文档")
+ .description("DEMO项目的接口测试文档")
+ .termsOfServiceUrl("http://www.hangge.com")
+ .version("1.0")
+ .contact(new Contact("ipman",
+ "http://www.ipman.com",
+ "ipipman@163.com"))
+ .build();
+ }
+}
+```
+
+
+
+#### 编写Controller,配置Swagger接口文档说明
+
+```java
+@RestController
+@Api(tags = "用户数据接口") // @Api 注解标注在类上用来描述整个 Controller 信息。
+public class UserController {
+
+ /**
+ * @ApiOperation 注解标注在方法上,用来描述一个方法的基本信息。
+ */
+ @ApiOperation(value = "修改用户", notes = "传入用户信息进行更新修改")
+ @PutMapping("/user")
+ public String updateUser(@RequestBody User user){
+ return user.toString();
+ }
+
+ /**
+ * @ApiImplicitParam 注解标注在方法上,用来描述方法的参数。其中 paramType 是指方法参数的类型,有如下可选值:
+ * path:参数获取方式为 @PathVariable
+ * query:参数获取方式为 @RequestParam
+ * header:参数获取方式为 @RequestHeader
+ * body
+ * form
+ */
+ @ApiOperation(value ="查询用户", notes = "根据 id 查询用户")
+ @ApiImplicitParam(paramType = "path", name = "id", value = "用户 id", required = true)
+ @GetMapping("/user/{id}")
+ public String getUserById(@PathVariable Integer id){
+ return "查找的用户id是:" + id;
+ }
+
+ /**
+ * 如果有多个参数,可以将多个参数的 @ApiImplicitParam 注解放到 @ApiImplicitParams 中
+ */
+ @ApiOperation(value = "新增用户", notes = "根据传入的用户名和密码添加一个新用户")
+ @ApiImplicitParams({
+ @ApiImplicitParam(paramType = "query", name = "username",
+ value = "用户名", required = true, defaultValue = "test"),
+ @ApiImplicitParam(paramType = "query", name = "password",
+ value = "密码", required = true, defaultValue = "123")
+ })
+ @PostMapping("/user")
+ public String addUser(@RequestParam String username, @RequestParam String password) {
+ return "新增用户:" + username + " " + password;
+ }
+
+ /**
+ * @ApiResponse 是对响应结果的描述。code 表示响应码,message 为相应的描述信息。如果有多个 @ApiResponse,则放在一个 @ApiResponses 中
+ */
+ @ApiOperation(value = "删除用户", notes = "根据 id 删除用户")
+ @ApiResponses({
+ @ApiResponse(code = 200, message = "删除成功!"),
+ @ApiResponse(code = 500, message = "删除失败!")
+ })
+ @DeleteMapping("/user/{id}")
+ public Integer deleteUserById(@PathVariable Integer id) {
+ return id;
+ }
+
+ /**
+ * @ApiIgnore 注解表示不对某个接口生成文档。
+ */
+ @ApiIgnore
+ @GetMapping("/user/test")
+ public String test() {
+ return "这是一个测试接口,不需要在api文档中显示。";
+ }
+}
+```
+
+
+
+#### 启动项目,访问Swagger-UI:http://127.0.0.1:8080/swagger-ui.html#/%E7%94%A8%E6%88%B7%E6%95%B0%E6%8D%AE%E6%8E%A5%E5%8F%A3
+
+image-20210922203731622
+
diff --git a/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/SpringbootSwaggerSampleApplication.java b/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/SpringbootSwaggerSampleApplication.java
new file mode 100644
index 0000000..fae8037
--- /dev/null
+++ b/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/SpringbootSwaggerSampleApplication.java
@@ -0,0 +1,13 @@
+package com.ipman.swagger.sample;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringbootSwaggerSampleApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringbootSwaggerSampleApplication.class, args);
+ }
+
+}
diff --git a/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/config/SwaggerConfig.java b/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/config/SwaggerConfig.java
new file mode 100644
index 0000000..d7293da
--- /dev/null
+++ b/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/config/SwaggerConfig.java
@@ -0,0 +1,53 @@
+package com.ipman.swagger.sample.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * @author ipipman
+ * @version V1.0
+ * @date 2021年9月22日
+ * @Package com.ipman.swagger.sample.config
+ * @Description: (Swagger配置)
+ * @date 2021年9月22日 6:17 下午
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+ /**
+ * 注入SwaggerDocketBean
+ */
+ @Bean
+ public Docket docket() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .apiInfo(apiInfo())
+ .select()
+ .apis(RequestHandlerSelectors.basePackage("com.ipman.swagger.sample.controller"))
+ .paths(PathSelectors.any())
+ .build();
+ }
+
+ /**
+ * 配置Swagger文档说明
+ */
+ private ApiInfo apiInfo() {
+ return new ApiInfoBuilder()
+ .title("API测试文档")
+ .description("DEMO项目的接口测试文档")
+ .termsOfServiceUrl("http://www.hangge.com")
+ .version("1.0")
+ .contact(new Contact("ipman",
+ "http://www.ipman.com",
+ "ipipman@163.com"))
+ .build();
+ }
+}
diff --git a/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/controller/UserController.java b/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/controller/UserController.java
new file mode 100644
index 0000000..2b02396
--- /dev/null
+++ b/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/controller/UserController.java
@@ -0,0 +1,80 @@
+package com.ipman.swagger.sample.controller;
+
+import com.ipman.swagger.sample.entity.User;
+import io.swagger.annotations.*;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+/**
+ * Created by ipipman on 2021年9月22日.
+ *
+ * @version V1.0
+ * @Package com.ipman.swagger.sample.controller
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年9月22日 6:29 下午
+ */
+@RestController
+@Api(tags = "用户数据接口") // @Api 注解标注在类上用来描述整个 Controller 信息。
+public class UserController {
+
+ /**
+ * @ApiOperation 注解标注在方法上,用来描述一个方法的基本信息。
+ */
+ @ApiOperation(value = "修改用户", notes = "传入用户信息进行更新修改")
+ @PutMapping("/user")
+ public String updateUser(@RequestBody User user){
+ return user.toString();
+ }
+
+ /**
+ * @ApiImplicitParam 注解标注在方法上,用来描述方法的参数。其中 paramType 是指方法参数的类型,有如下可选值:
+ * path:参数获取方式为 @PathVariable
+ * query:参数获取方式为 @RequestParam
+ * header:参数获取方式为 @RequestHeader
+ * body
+ * form
+ */
+ @ApiOperation(value ="查询用户", notes = "根据 id 查询用户")
+ @ApiImplicitParam(paramType = "path", name = "id", value = "用户 id", required = true)
+ @GetMapping("/user/{id}")
+ public String getUserById(@PathVariable Integer id){
+ return "查找的用户id是:" + id;
+ }
+
+ /**
+ * 如果有多个参数,可以将多个参数的 @ApiImplicitParam 注解放到 @ApiImplicitParams 中
+ */
+ @ApiOperation(value = "新增用户", notes = "根据传入的用户名和密码添加一个新用户")
+ @ApiImplicitParams({
+ @ApiImplicitParam(paramType = "query", name = "username",
+ value = "用户名", required = true, defaultValue = "test"),
+ @ApiImplicitParam(paramType = "query", name = "password",
+ value = "密码", required = true, defaultValue = "123")
+ })
+ @PostMapping("/user")
+ public String addUser(@RequestParam String username, @RequestParam String password) {
+ return "新增用户:" + username + " " + password;
+ }
+
+ /**
+ * @ApiResponse 是对响应结果的描述。code 表示响应码,message 为相应的描述信息。如果有多个 @ApiResponse,则放在一个 @ApiResponses 中
+ */
+ @ApiOperation(value = "删除用户", notes = "根据 id 删除用户")
+ @ApiResponses({
+ @ApiResponse(code = 200, message = "删除成功!"),
+ @ApiResponse(code = 500, message = "删除失败!")
+ })
+ @DeleteMapping("/user/{id}")
+ public Integer deleteUserById(@PathVariable Integer id) {
+ return id;
+ }
+
+ /**
+ * @ApiIgnore 注解表示不对某个接口生成文档。
+ */
+ @ApiIgnore
+ @GetMapping("/user/test")
+ public String test() {
+ return "这是一个测试接口,不需要在api文档中显示。";
+ }
+}
diff --git a/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/entity/User.java b/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/entity/User.java
new file mode 100644
index 0000000..da75542
--- /dev/null
+++ b/springboot-swagger-sample/src/main/java/com/ipman/swagger/sample/entity/User.java
@@ -0,0 +1,29 @@
+package com.ipman.swagger.sample.entity;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.*;
+
+/**
+ * Created by ipipman on 2021年9月22日.
+ *
+ * @version V1.0
+ * @Package com.ipman.swagger.sample.entity
+ * @Description: (用一句话描述该文件做什么)
+ * @date 2021年9月22日 6:37 下午
+ */
+@Getter
+@Setter
+@ToString
+@ApiModel(value = "用户实体类", description = "用户信息描述类")
+public class User {
+
+ @ApiModelProperty(value = "用户id")
+ private Integer id;
+
+ @ApiModelProperty(value = "用户名")
+ private String username;
+
+ @ApiModelProperty(value = "用户密码")
+ private String password;
+}
\ No newline at end of file
diff --git a/springboot-swagger-sample/src/main/resources/application.properties b/springboot-swagger-sample/src/main/resources/application.properties
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/springboot-swagger-sample/src/main/resources/application.properties
@@ -0,0 +1 @@
+
diff --git a/springboot-swagger-sample/src/test/java/com/ipman/swagger/sample/SpringbootSwaggerSampleApplicationTests.java b/springboot-swagger-sample/src/test/java/com/ipman/swagger/sample/SpringbootSwaggerSampleApplicationTests.java
new file mode 100644
index 0000000..dd489b9
--- /dev/null
+++ b/springboot-swagger-sample/src/test/java/com/ipman/swagger/sample/SpringbootSwaggerSampleApplicationTests.java
@@ -0,0 +1,13 @@
+package com.ipman.swagger.sample;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class SpringbootSwaggerSampleApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/springboot-transactional-sample/README.md b/springboot-transactional-sample/README.md
new file mode 100644
index 0000000..3c52024
--- /dev/null
+++ b/springboot-transactional-sample/README.md
@@ -0,0 +1,41 @@
+# Spring的事务7种传播机制和隔离级别
+
+### Propagation(事务传播属性)
+Propagation 属性确定代理应该对那些方法增加事务行为,这样属性最重要的部分是传播行为:
+> - **PROPAGATION_REQUIRED** ----> 支持当前事务,如果当前没有事务,则新建一个事务,这是最常见的选择,也是 Spring 默认的一个事务传播行为;
+> - **PROPAGATION_SUPPORTS** ----> 支持当前事务,如果当前没有事务,则以非事务的方式执行;
+> - **PROPAGATION_MANDATORY** ----> 支持当前事务,如果当前没有事务,则抛出异常;
+> - **PROPAGATION_REQUIRES_NEW** ----> 新建事务,如果当前存在事务,把当前事务挂起;
+> - **PROPAGATION_NOT_SUPPORTED** ----> 以非事务的方式执行操作,如果当前存在事务,就把当前事务挂起;
+> - **PROPAGATION_NEVER** ----> 以非事务方式执行,如果当前存在事务,则抛出异常;
+> - **PROPAGATION_NESTED** ----> Nested的事务和它的父事务是相依的,它的提交是等它的父事务一块提交的;
+
+
+#### (一)PROPAGATION_REQUIRED
+注解用法比如:@Transactional(propagation = Propagation.REQUIRED)
+默认的 spring 事务传播级别,使用该级别的特点是:如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文不存在事务,则新建事务执行
+在大多数业务场景下通常都能满足
+
+#### (二)PROPAGATION_SUPPORTS
+该传播级别的特点是:如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包在transactionTemplate.execute中的代码都会有事务支持。这个通常是用来处理那些非原子性的非核心业务逻辑操作。
+应用场景较少
+
+#### (三)PROPAGATION_MANDATORY
+该级别的事务要求上下文中必须要存在事务,否则就会抛出异常
+配置改方法的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段
+比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别
+
+#### (四)PROPAGATION_REQUIRES_NEW
+该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行
+
+#### (五)PROPAGATION_NOT_SUPPORTED
+该传播属性不支持事务,该级别的特点就上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务
+好处:可以帮助我们把事务缩小范围。因为一个事务越大,它存在的风险也就越多。所以在处理事务过程中,要保证尽可能的缩小范围
+
+#### (六)PROPAGATION_NEVER
+不能在事务中运行,该事务传播级别要求中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行
+
+#### (七)PROPAGATION_NESTED
+理解Nested的关键是savepoint
+它与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与它的父事务相互独立
+而Nested的事务和它的父事务是相依的,它的提交是要等和它的父事务一块提交的。也就是说,如果父事务最后回滚,它也要回滚的
\ No newline at end of file
diff --git a/springcloud-consul-config-sample/src/main/java/com/ipman/springcloud/consul/config/center/sample/SpringcloudConsulConfigSampleApplication.java b/springcloud-consul-config-sample/src/main/java/com/ipman/springcloud/consul/config/center/sample/SpringcloudConsulConfigSampleApplication.java
index cd4c623..e833700 100644
--- a/springcloud-consul-config-sample/src/main/java/com/ipman/springcloud/consul/config/center/sample/SpringcloudConsulConfigSampleApplication.java
+++ b/springcloud-consul-config-sample/src/main/java/com/ipman/springcloud/consul/config/center/sample/SpringcloudConsulConfigSampleApplication.java
@@ -8,6 +8,7 @@
@EnableDiscoveryClient //让注册中心进行服务发现,将服务注册到服务组件上
public class SpringcloudConsulConfigSampleApplication {
+ // hello
public static void main(String[] args) {
SpringApplication.run(SpringcloudConsulConfigSampleApplication.class, args);
}