一,springboot的启动原理
@SpringBootApplication
public class Application {
public static void main(String
[] args
) {
SpringApplication
.run(Application
.class,args
);
}
}
public static ConfigurableApplicationContext
run(Class
<?>[] primarySources
, String
[] args
) {
return new SpringApplication(primarySources
).run(args
);
}
1.创建SpringApplication
进入new SpringApplication()
public SpringApplication(ResourceLoader resourceLoader
, Class
<?>... primarySources
) {
this.resourceLoader
= resourceLoader
;
Assert
.notNull(primarySources
, "PrimarySources must not be null");
this.primarySources
= new LinkedHashSet<>(Arrays
.asList(primarySources
));
this.webApplicationType
= WebApplicationType
.deduceFromClasspath();
setInitializers((Collection
) getSpringFactoriesInstances(ApplicationContextInitializer
.class));
setListeners((Collection
) getSpringFactoriesInstances(ApplicationListener
.class));
this.mainApplicationClass
= deduceMainApplicationClass();
}
再来看getSpringFactoriesInstances()
private <T> Collection
<T> getSpringFactoriesInstances(Class
<T> type
, Class
<?>[] parameterTypes
, Object
... args
) {
ClassLoader classLoader
= getClassLoader();
Set
<String> names
= new LinkedHashSet<>(SpringFactoriesLoader
.loadFactoryNames(type
, classLoader
));
List
<T> instances
= createSpringFactoriesInstances(type
, parameterTypes
, classLoader
, args
, names
);
AnnotationAwareOrderComparator
.sort(instances
);
return instances
;
}
接着看loadFactoryNames()
public static List
<String> loadFactoryNames(Class
<?> factoryType
, @Nullable ClassLoader classLoader
) {
String factoryTypeName
= factoryType
.getName();
return loadSpringFactories(classLoader
).getOrDefault(factoryTypeName
, Collections
.emptyList());
}
private static Map
<String
, List
<String>> loadSpringFactories(@Nullable ClassLoader classLoader
) {
MultiValueMap
<String, String> result
= cache
.get(classLoader
);
if (result
!= null
) {
return result
;
}
try {
Enumeration
<URL> urls
= (classLoader
!= null
?
classLoader
.getResources(FACTORIES_RESOURCE_LOCATION
) :
ClassLoader
.getSystemResources(FACTORIES_RESOURCE_LOCATION
));
result
= new LinkedMultiValueMap<>();
while (urls
.hasMoreElements()) {
URL url
= urls
.nextElement();
UrlResource resource
= new UrlResource(url
);
Properties properties
= PropertiesLoaderUtils
.loadProperties(resource
);
for (Map
.Entry
<?, ?> entry
: properties
.entrySet()) {
String factoryTypeName
= ((String
) entry
.getKey()).trim();
for (String factoryImplementationName
: StringUtils
.commaDelimitedListToStringArray((String
) entry
.getValue())) {
result
.add(factoryTypeName
, factoryImplementationName
.trim());
}
}
}
cache
.put(classLoader
, result
);
return result
;
}
catch (IOException ex
) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION
+ "]", ex
);
}
}
再回到SpringApplication()看setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
private <T> Collection
<T> getSpringFactoriesInstances(Class
<T> type
) {
return getSpringFactoriesInstances(type
, new Class<?>[] {});
}
private <T> Collection
<T> getSpringFactoriesInstances(Class
<T> type
, Class
<?>[] parameterTypes
, Object
... args
) {
ClassLoader classLoader
= getClassLoader();
Set
<String> names
= new LinkedHashSet<>(SpringFactoriesLoader
.loadFactoryNames(type
, classLoader
));
List
<T> instances
= createSpringFactoriesInstances(type
, parameterTypes
, classLoader
, args
, names
);
AnnotationAwareOrderComparator
.sort(instances
);
return instances
;
}
这里就和上一步其实是一样的,从META-INF/spring.factories加载bean。
2.执行run()
public static ConfigurableApplicationContext
run(Class
<?>[] primarySources
, String
[] args
) {
return new SpringApplication(primarySources
).run(args
);
}
点击进入run()
public ConfigurableApplicationContext
run(String
... args
) {
StopWatch stopWatch
= new StopWatch();
stopWatch
.start();
ConfigurableApplicationContext context
= null
;
Collection
<SpringBootExceptionReporter> 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
;
}
prepareContext()
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
);
}
ConfigurableListableBeanFactory beanFactory
= context
.getBeanFactory();
beanFactory
.registerSingleton("springApplicationArguments", applicationArguments
);
if (printedBanner
!= null
) {
beanFactory
.registerSingleton("springBootBanner", printedBanner
);
}
if (beanFactory
instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory
) beanFactory
)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding
);
}
if (this.lazyInitialization
) {
context
.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
Set
<Object> sources
= getAllSources();
Assert
.notEmpty(sources
, "Sources must not be empty");
load(context
, sources
.toArray(new Object[0]));
listeners
.contextLoaded(context
);
}
refresh()
public void refresh() throws BeansException
, IllegalStateException
{
synchronized (this.startupShutdownMonitor
) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory
= obtainFreshBeanFactory();
prepareBeanFactory(beanFactory
);
try {
postProcessBeanFactory(beanFactory
);
invokeBeanFactoryPostProcessors(beanFactory
);
registerBeanPostProcessors(beanFactory
);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory
);
finishRefresh();
}
catch (BeansException ex
) {
if (logger
.isWarnEnabled()) {
logger
.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex
);
}
destroyBeans();
cancelRefresh(ex
);
throw ex
;
}
finally {
resetCommonCaches();
}
}
}
二,自定义starter
1.原理分析
1、这个场景需要使用到的依赖是什么?
2、如何编写自动配置
@Configuration
@ConditionalOnXXX
@AutoConfigureAfter
@Bean
@ConfigurationPropertie结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties
自动配置类要能加载
将需要启动就加载的自动配置类,配置在META‐INF
/spring
.factories
org
.springframework
.boot
.autoconfigure
.EnableAutoConfiguration
=\
org
.springframework
.boot
.autoconfigure
.admin
.SpringApplicationAdminJmxAutoConfiguration
,\
org
.springframework
.boot
.autoconfigure
.aop
.AopAutoConfiguration
,\
3、模式: 启动器只用来做依赖导入; 专门来写一个自动配置模块; 启动器依赖自动配置;别人只需要引入启动器(starter) mybatis-spring-boot-starter;自定义启动器名-spring-boot-starter
2.代码
1.首先创建两个maven工程
yhd-spring-boot-starter yhd-spring-boot-starter-autoconfigurer
2.引入依赖
在yhd-spring-boot-starter-autoconfigurer里面引入
<dependencies>
<dependency>
<groupId>org.springframework.boot
</groupId>
<artifactId>spring-boot-starter
</artifactId>
<version>2.2.6.RELEASE
</version>
</dependency>
</dependencies>
在yhd-spring-boot-starter里面引入
<!-- 引入自动配置模块
-->
<dependencies>
<dependency>
<groupId>com
.yhd
</groupId
>
<artifactId>yhd
-spring
-boot
-starter
-autoconfigurer
</artifactId
>
<version>1.0-SNAPSHOT
</version
>
</dependency
>
</dependencies
>
3.编写yhd-spring-boot-starter-autoconfigurer
1.创建一个service
public class HelloService {
@Autowired
private HelloServiceProperties helloServiceProperties
;
public String
sayHello(String name
){
return helloServiceProperties
.getPrefix()+" "+name
+" " +helloServiceProperties
.getSuffix();
}
}
2.创建一个HelloServiceProperties
@Component
@ConfigurationProperties(prefix
= "yhd.hello")
public class HelloServiceProperties {
private String prefix
;
private String suffix
;
public String
getPrefix() {
return prefix
;
}
public void setPrefix(String prefix
) {
this.prefix
= prefix
;
}
public String
getSuffix() {
return suffix
;
}
public void setSuffix(String suffix
) {
this.suffix
= suffix
;
}
}
3.创建一个HelloServiceAutoConfiguration
@SpringBootConfiguration
@ConditionalOnWebApplication
@EnableConfigurationProperties(HelloServiceProperties
.class)
public class HelloServiceAutoConfiguration {
@Autowired
private HelloServiceProperties helloServiceProperties
;
@Bean
public HelloService
helloService(){
return new HelloService();
}
}
4.在resources目录下创建META-INF目录
5.在META-INF下创建spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.yhd.config.HelloServiceAutoConfiguration
4.将这两个项目安装到仓库
5.别的项目引入yhd-spring-boot-starter依赖
@RestController
public class HelloController {
@Autowired
private HelloService helloService
;
@GetMapping("/hello")
public String
hello(){
return helloService
.sayHello("尹会东");
}
}
配置文件:
yhd.hello.prefix=Hello
yhd.hello.suffix=!
访问localhost:8080,输出:Hello 尹会东 !。 至此,starter创建成功。