注:本文提到的Spring容器或者Bean容器,或者Spring Bean容器,都是指同一个事情,那就是代指BeanFactory。关于BeanFactory,后面有机会会再说下。
花絮几年前接触过SpringBoot,跑过Demo,当时刚入行,连Spring都没搞明白,更别说SpringBoot了,就是觉得,哇塞,好厉害,然后一脸懵逼。
工作中没有用到,又没有去主动学习它。觉得很恐惧,这么厉害的东西,肯定是很深奥,很复杂吧!。
这种心理也造成了一定程度上,对某些事物的望而却步,其实只要向前迈出了步子,一步步慢慢来,才发现,以前的那种恐惧心理是多么的幼稚、胆怯、可笑!
序言SpringBoot本身并没有多大的花样,所有的知识点其实还都是Spring Framework的。
在SpringBoot之前,使用Spring可以说,并不是那么的方便,其实也主要是在搭建一个基于Spring Framework的项目时这个困扰。Spring本身的配置,整合SpringMVC,整合Struts2,整合mybatis,整合Hibernate,整合SpringSecurity等等,如果是Web应用还有个web.xml需要配置。什么都要你去配置一下,第一步就是去找怎么配置,记住这么配置是如何配的,其实并没有切实的意义,毕竟又不是经常需要去搭建一个项目。正因为不常这么配置,不值得记住如何配置,导致每次实际用到时,很麻烦,到处去找如何配置的XML配置文件。
SpringBoot的出现,正是为了解决这个问题,让你可以不去做任何配置的情况下,运行一个Spring应用,或者Web应用。需要做的仅仅是引入SpringBoot的maven或者gradle依赖即可。
SpringBoot要做的就是,让你开箱即用!
将使用Spring的成本降到尽可能低,为用户带来了极大的便利。
当然SpringBoot做的也不仅仅只有这些,不过这里仅讨论下它的自动化配置,不讨论其他的。
如果了解Spring对@Configuration这个注解的处理过程,会更加容易理解SpringBoot的自动化配置。
如果没有,可以参考这篇解释
穷其林这第一件事,就是找门,门都找不到,那不是没门吗!
既然想找门,就得从程序的启动入口去找,任何SpringBoot程序都会用到这么两个
@SpringBootApplication public class Application{ public static void main(String[] args){ SpringApplication.run(Application.class, args); } }看到这个后,如果好奇其实现,应该会首先查看SpringApplication#run方法,实际调用的是这个重载的静态方法。
// org.springframework.boot.SpringApplication public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } public ConfigurableApplicationContext run(String... args) { ···省略··· try { ···省略··· context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context); // 真正启动应用程序上下文前的一些准备动作 // 这里会去将Application.class,注册到org.springframework.context.annotation.AnnotatedBeanDefinitionReader // 也就是去把Application.class注册成一个BeanDefinition实例 // 不过Application必须要是一个@Component prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新上下文,这个过程中主要就是Bean的实例化和属性赋值绑定 // 如果是Web环境,涉及到Web相关的一些东西,但是本质上还是各种Bean的实例化 // 和Bean之间依赖关系的处理,代理Bean的生成(涉及到AspectJ的Advice处理)等等 refreshContext(context); } return context; }BeanDefinition实例有了,就能去启动上下文,处理Bean容器了,容器启动完成后,整个SpringBoot程序基本启动完成!
等等! 是不是少了什么?
这里就注册了一个BeanDefinition,那么多@Component、@Configuration、@Service、@Controller怎么办?
先留着疑问,且待后面解答!
遇山口林尽水源,便得一山,山有小口,仿佛若有光。
注意到上面的准备阶段,被注册的Bean必须要被@Component注解,现在Application.class仅有一个注解@SpringBootApplication。
@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 { ···省略··· }挨个查看几个注解的定义后,会发现@SpringBootConfiguration被@Component所注解,这就解释了为什么被@SpringBootApplication所注解的Application.class类可以被作为一个Bean注册到BeanDefinitionRegistry。
除此之外,还有个令人惊喜的名称:@EnableAutoConfiguration,看名字就看出来它是做啥的了。
没错,SpringBoot的所谓自动配置,就是它在起作用。