目录html
@SpringBootApplication public class LearnspringbootApplication { public static void main(String[] args) { SpringApplication.run(LearnspringbootApplication.class, args); } }
你们能够看到,如上所示是一个很常见的SpringBoot启动类,咱们能够看到仅仅使用了一个Main方法就启动了整个SpringBoot项目是否是很神奇,下面咱们来仔细剖析如下这一段代码,这段代码中咱们能够仔细地观察到最重要的两个部分,分别是@SpringBootApplication
注解和SpringApplication
这个类。java
打开这个@SpringBootApplication
注解,以下所示react
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; }
咱们能够发现;咱们能够在@SpringBootApplication
注解中使用exclude()
,excludeName()
,scanBasePackages()
,scanBasePackageClasses()
这四个方法来进行自定义咱们须要排除装配的Bean,扫描包路径,扫描类路径。web
搭眼一瞅,在@SpringBootApplication
注解上还有下面那么多注解。redis
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
这四个注解不用看,就是关于注解的一些定义spring
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited
@SpringBootConfiguration
注解咱们点进去看一下,发现是下面这个样子,@SpringBootConfiguration注解上又标注了@Configuration注解,想必在@Configuration注解上也标注了@Component注解,这不就是咱们上一章节说的Spring的模式注解。总的来讲嘛,SpringBootConfiguration注解的做用就是把类变成能够被Spring管理的Beanjson
结论 1 :也就是说标注
@SpringBootApplication
注解的类会成为Spring的Beanspringboot
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }
咱们再来看一下@EnableAutoConfiguration
,从名字上咱们就能够看到“启用自动装配”
的意思。那咱们可要仔细看一下。从下面咱们能够看到只有两个咱们须要了解的注解,分别是@AutoConfigurationPackage
和@Import
注解。websocket
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
当我点进去@AutoConfigurationPackage
注解中,发现该注解又启用了一个模块装配@Import(AutoConfigurationPackages.Registrar.class)
(上一章节有讲到);因此又点进去AutoConfigurationPackages.Registrar.class
这个类,发现这个类又实现了两个接口ImportBeanDefinitionRegistrar
和DeterminableImports
在该类的实现方法中的PackageImport()
;最后终于发现了下面这段代码。session
PackageImport(AnnotationMetadata metadata) { this.packageName = ClassUtils.getPackageName(metadata.getClassName()); }
结论 2 :SpringBoot默认会装配启动类路径的全部包下可装配的Bean;也就是说若是你把SpringBoot启动类放在一个单独的包中,则SpringBoot不会装配到你的其余Bean。这时候你就要使用
@SpringBootApplication
的scanBasePackages()
方法进行另行配置。
此时@EnableAutoConfiguration
注解仅仅就剩下@Import(AutoConfigurationImportSelector.class)
没有看了,不过从注解上咱们能够看到使用@Import
注解,因此能够知道SpringBoot使用的是模块装配的接口实现方式
。因此我么针对AutoConfigurationImportSelector
这个类仔细剖析一下。AutoConfigurationImportSelector
- > AutoConfigurationImportSelector.selectImports()
->getAutoConfigurationEntry()
-> getCandidateConfigurations()
->SpringFactoriesLoader.loadFactoryNames()
-> loadFactoryNames()
->loadSpringFactories()
; 哈哈果真源码不经扒;看下面源码;META-INF/spring.factories
这不就是配置SpringBoot自动配置的文件嘛。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
结论 3 :SpringBoot在启动时会自动加载Classpth路径下的
META-INF/spring.factories
文件,因此咱们能够将须要自动配置的Bean写入这个文件,SPringBoot会替咱们自动装配。这也正是配置SpringBoot自动配置的步骤。
@SpringBootApplication public class LearnspringbootApplication { public static void main(String[] args) { SpringApplication.run(LearnspringbootApplication.class, args); } }
在Main方法中咱们能够看到SpringApplication做为一个启动类来启动SpringBoot应用程序。那么SpringApplication类是如何进行启动整个应用程序的呢?
public SpringApplication(Class<?>... primarySources) { this(null, primarySources); }
从SpringApplication类的构造方法中咱们能够看到,这里传入了一个主Bean来源;由于咱们将标注了@SpringBootApplication
注解的LearnspringbootApplication.class
传递了进来,因此@SpringBootApplication
扫描到的Bean和自动装配的Bean会做为主Bean来源。固然咱们能够调用该类的setSources()
方法设置本身的SpringXML配置。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
从上面一句代码(既SpringApplication初始化方法中一行代码);咱们观察deduceFromClasspath()
方法能够看到,SpringBoot判断类路径下是否存在下面类进而判断SpringBoot的应用类型。三种类型分别是NONE
,SERVLET
,REACTIVE
。固然咱们也能够调用setWebApplicationType()
自行设置。
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework." + "web.servlet.DispatcherServlet"; private static final String WEBFLUX_INDICATOR_CLASS = "org." + "springframework.web.reactive.DispatcherHandler"; private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer"; private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext"; private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
this.mainApplicationClass = deduceMainApplicationClass();
从上面一句代码(既SpringApplication初始化方法中一行代码);咱们能够经过deduceMainApplicationClass()
方法能够看到SpringBoot根据Main 线程执行堆栈判断实际的引导类。(PS:存在一种状况就是标注@SpringBootApplication
注解的并非引导类状况)
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
从上面一句代码(既SpringApplication初始化方法中一行代码);咱们发现setInitializers()
方法会调用getSpringFactoriesInstances()
-> getSpringFactoriesInstances()
-> getSpringFactoriesInstances()
该方法会使用SpringFactoriesLoader
类记进行加载配置资源既META-INF/spring.factories
,利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer 实现类,并排序对象集合。并使用AnnotationAwareOrderComparator
类的sort()
方法进行排序。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
从上面一句代码(既SpringApplication初始化方法中一行代码);从setListeners()
方法中能够看到该方法仍然调用getSpringFactoriesInstances()
方法,不一样的是利用 Spring 工厂加载META-INF/spring.factories
,实例化 ApplicationListener
实现类,并排序对象集合
从SpringApplication
类的run()
方法中,咱们能够看到下面代码getRunListeners()
方法一样利用 Spring 工厂加载机制,读取 SpringApplicationRunListener 对象集合,而且封装到组合类
SpringApplicationRunListeners对象中并启动运行监听器。
SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting();
Spring Boot 经过 SpringApplicationRunListener 的实现类 EventPublishingRunListener 利用 Spring Framework 事件
API ,广播 Spring Boot 事件。
context = createApplicationContext();
从SpringApplication
类的run()
方法中,咱们可看到createApplicationContext()
根据第二步推断的SpringBoot应用类型建立相应的上下文。
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
根据推断的SpringBoot应用类型建立下面三种之一的上下文
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment);
从SpringApplication
类的run()
方法中,咱们可看到prepareEnvironment()
根据第二步推断的SpringBoot应用类型建立相应的上下文。建立不一样的Environment
。从下面能够看到他们分别是StandardServletEnvironment
,StandardReactiveWebEnvironment
,StandardEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } switch (this.webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default: return new StandardEnvironment(); } }
标注@SpringBootApplication
注解的类会成为Spring的Bean
SpringBoot默认会装配启动类路径的全部包下可装配的Bean;也就是说若是你把SpringBoot启动类放在一个单独的包中,则SpringBoot不会装配到你的其余Bean。这时候你就要使用@SpringBootApplication
的scanBasePackages()
方法进行另行配置。
SpringBoot在启动时会自动加载Classpth路径下的META-INF/spring.factories
文件,因此咱们能够将须要自动配置的Bean写入这个文件。一样SpringBoot也会扫描Jar包中的META-INF/spring.factories
文件;例如当导入spring-boot-starter
起步依赖的时候,而且启用了自动装配注解@EnableAutoConfiguration
,就将会替咱们自动装配如以下类;若是知足条件的话。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\ org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\ org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
该教程所属Java工程师之SpringBoot系列教程,本系列相关博文目录 Java工程师之SpringBoot系列教程前言&目录