@SpringBootApplication
运行原理分析
首先来看一下springboot项目的主启动类
@SpringBootApplication
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
}
可以发现此类是由注解@SpringBootApplication
标注的,要想研究自动装配原理,一层一层点进去查看源码即可。
点进去,可以看到以下内容。
核心是这三个注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
我们来一个一个看,首先看第一个@SpringBootConfiguration
,点进去查看
他被@Configuration
这个注解标记着,这个注解是spring中的注解,代表被标记类是一个配置类,然后再点进去。
可以看到被@Component
标记着,代表着是一个组件,被spring管理着,到此这个注解结束了。
总结@SpringBootConfiguration
:被标记类是一个配置类,并且是spring容器中的一个组件。
接下来看下一个注解@EnableAutoConfiguration
,点进去一探究竟。
它里面又有两个核心注解:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
先看其中的一个@AutoConfigurationPackage
,点进去
再往下一层点,点击Registrar
进入查看
可以看到核心方法,点击上图中红框的方法PackageImports
继续查看
可以看到参数是AutoConfigurationPackage.class.getName()
,再点击AutoConfigurationPackage
发现回到了@AutoConfigurationPackage
注解
也就是说,@AutoConfigurationPackage
的作用是将被此注解标记的类(主启动类)下的所有springboot自带的组件扫描到spring容器中。
至此@AutoConfigurationPackage
扫描到了包,接下来就该是导入自动配置选择器@Import(AutoConfigurationImportSelector.class)
。
,点进去看看。
里面有一个selectImports方法,将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中。那么是怎么导入的呢?
通过一系列的点击,最后可以发现是通过这一文件spring.factories
导入的,让我们来看看这文件里都有些什么。
看到了很多自动配置的文件;这就是自动配置根源所在!
点进一个看看
发现里面都是一个个的JavaConfig
配置类,而且都注入了一些Bean
(此方面实在spring中学习的)
总结@Import(AutoConfigurationImportSelector.class)
,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories
配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure.
包下的配置项,通过反射实例化为对应标注了 @Configuration
的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。
那么启动springboot时会将所有的自动配置组件都导入吗?答案:当然不是,是由一个注解控制的@ConditionalOnClass
,如果在pom.xml导入了对应的启动器,则会在启动时自动配置此功能,否则爆红。
最后一个注解@ComponentScan
组件扫描,它是将被标记的类(主启动类)的上一级目录(com.kuang)包下的所有带@Component标签(@Controller,@Service本质都是@Component)的类都封装成了beandefinition然后就注册到beanfactory当中。
这也就是代码要写在和主启动类同级的包下,以便可以被扫描到。
@ComponentScan和@EnableAutoConfiguration的不同点
1.两者虽然都能将带有注解的对象放入ioc容器中,但是它们扫描的范围是不一样的。@ComponentScan扫描的范围默认是它所在的包以及子包中所有带有注解的对象,@EnableAutoConfiguration扫描的范围默认是它所在类。
2.它们作用的对象不一样,@EnableAutoConfiguration除了扫描本类带有的注解外,还会 借助@Import的支持,收集和注册依赖包中相关的bean定义,将这些bean注入到ioc容器中。在springboot中注入的bean有两部分组成,一部分是自己在代码中写的标注有@Controller,@service,@Respority等注解的业务bean,这一部分bean就由@ComponentScan将它们加入到ioc容器中,还有一部分是springboot自带的相关bean,可以将这部分bean看成是工具bean,这部分bean就是由@EnableAutoConfiguration负责加入到容器中。
3.@EnableAutoConfiguration可以单独启动springboot项目,而@ComponentScan是不能的。
总结:@SpringBootApplication
1.主启动类是一个配置类,且是一个组件被注入到容器中。
2.SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值(pom.xml指定),将这些指定的值完成自动配置,并将主启动类下的所有springboot自带的组件扫描到spring容器中。
3.自动扫描启动类同级包下的所有自己添加的组件,添加到容器中。
SpringApplication.run分析
分析该方法主要分两部分,一部分是SpringApplication
的实例化,二是run
方法的执行;
SpringApplication类帮我们做了哪些事情?
1、推测web应用类型,是普通的项目还是Web项目,并赋值到属性webApplicationType
2、设置所有可用初始化器到List<ApplicationContextInitializer<?>> initializers属性中
3、设置所有监听器到List<ApplicationListener<?>> listeners属性中
4、 推断运行的主类,并赋值到属性mainApplicationClass
run方法流程分析
自动装配原理分析
我们以**HttpEncodingAutoConfiguration(Http编码自动配置)**为例解释自动配置原理;
//表示这是一个配置类,可以向容器中添加组件
@Configuration(proxyBeanMethods = false)
//自动装配时加载ServerProperties类的属性
@EnableConfigurationProperties(ServerProperties.class)
//判断是否是web应用,如果是,就生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判断是否存在CharacterEncodingFilter这个类,springmvc中的字符编码过滤器
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断是否存在server.servlet.encoding.enabled这个属性
//如果不存在,判断也成立
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
//已经和SpringBoot的配置文件映射了
private final Encoding properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
//给容器中添加一个组件,这个组件的某些值需要从properties中获取
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
}
总结:启动时根据条件判断是否加载这个配置类
- 一旦加载这个配置类(xxxAutoConfiguration),这个配置类就会向容器中添加各种组件(@Bean)
- 这些组件的属性是从对应的xxxProperties类中获取的,xxxProperties中的属性也可以通过配置文件修改的,也就是说,所有在配置文件中能配置的属性都是在xxxxProperties类中封装着
精髓
1、SpringBoot启动会加载大量的自动配置类
2、我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
3、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
4、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
xxxxAutoConfigurartion:自动配置类;给容器中添加组件
xxxxProperties:封装配置文件中相关属性;
了解:@Conditional
了解完自动装配的原理后,我们来关注一个细节问题,自动配置类必须在一定的条件下才能生效;
@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
#开启springboot的调试类
debug=true