深入学习springboot ,记录学习心得,第一篇
1. 什么是springboot
springboot 是一个服务于spring框架的框架。spring的核心IoC(控制反转)、DI(依赖注入)用起来实在是太方便了;但是使用spring需要很多繁琐的配置。springboot简化了这些配置,是“约定优于配置”的最佳实践。
2. springboot体现了哪些约定
-
maven项目的目录结构
默认包含resource的文件夹存放配置文件
默认打jar包
-
spring-boot-starter-web 包中默认包含spring mvc相关依赖以及内置Tomcat容器,构建web应用更简单
-
默认提供application.properties / yml文件
-
默认通过spring.profiles.active属性来决定运行时要读取的配置文件
-
EnableAutoConfiguration默认对依赖的starter自动装载
3. 注解SpringBootApplication干了什么
springboot工程的main函数所在类加了@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
SpringBootApplication本质上由3个注解组成,分别是:
-
@SpringBootConfiguration, 实际上这玩意还是一个Configuration
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration
-
@EnableAutoConfiguration
-
@ComponentScan
简单分析这三个注解。
-
@Configuration
看名字就知道,他是一种配置。实际上,它是JavaConfig形式的基于Spring IoC 容器在配置Bean时使用的一种注解(传统的spring容器使用xml的形式来配置Bean,现在基本都使用JavaConfig形式)。
标注@Configuration的java类都是一个JavaConfig配置类。在这个类中,任何标注@Bean的方法都会将方法名作为Bean定义并注册到SpringIoC容器(方法名就是Bean的id)。
-
@ComponentScan
看名字 组件扫描。 它扫描指定路径(包含子文件夹)下的标识了需要自动装配的类,并装配到IoC容器中。
标识:@Component、@Service、@Repository、@Controller
-
@EnableAutoConfiguration
看名儿、启动自动配置。这是springboot可以自动装配的关键,它帮助springboot应用把所有符合条件的@Configuration配置都加载到当前使用的IoC容器中。查看该注解的定义:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration
@Import({AutoConfigurationImportSelector.class})是干啥?
AutoConfigurationImportSelector实现ImportSelector接口,帮助springboot将所有符合条件的@Configuration都加载至IoC容器中。那么什么样的配置才是符合条件的呢?
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }
点进
loadMetadata()
方法中击查看定义:return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
在
spring-autoconfigure-metadata.pngMETA-INF/spring-autoconfigure-metadata.properties
下,有
即,符合这些条件(Contitional)的将被自动加载到容器中,条件有如下几类:
- @ConditionalOnClass : classpath中存在该类时起效
- @ConditionalOnMissingClass : classpath中不存在该类时起效
- @ConditionalOnBean : DI容器中存在该类型Bean时起效
- @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
- @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
- @ConditionalOnExpression : SpEL表达式结果为true时
- @ConditionalOnProperty : 参数设置或者值一致时起效
- @ConditionalOnResource : 指定的文件存在时起效
- @ConditionalOnJndi : 指定的JNDI存在时起效
- @ConditionalOnJava : 指定的Java版本存在时起效
- @ConditionalOnWebApplication : Web应用环境下起效
- @ConditionalOnNotWebApplication : 非Web应用环境下起效
对于符合条件的配置通过SpringFactoriesLoader加载,大概流程是在spring.factories
中以spring-autoconfigure-metadata.properties
有效的的value作为作为查找key对指定的factoryClass进行加载。原理类似于java原生的SPI机制。
举个demo:
-
创建maven模板工程,新增HelloService类
public class HelloService { public String sayHello(String msg) { return "hello " + msg; } }
-
新建HelloConfig将HelloService注册为bean
@Configuration public class HelloConfig { @Bean public HelloService helloService() { return new HelloService(); } }
-
在resources目录下创建META-INF\spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.sz.HelloConfig
整个工程结构如下,spring-autoconfigure-metadata.properties文件可以先不配置
创建依赖的jar包-
将工程install到本地maven仓库,第一部分完成。
-
再创建一个springboot项目,将spi-dependency依赖添加进来
-
调用HelloService的sayHello()方法
@SpringBootApplication public class Main { public static void main(String[] args) { ConfigurableApplicationContext ca= SpringApplication.run(Main.class,args); System.out.println(ca.getBean(HelloService.class).sayHello("world")); } }
运行main可以看到helloService被获取到了。
执行结果
回到第3步,把spring.factories
的配置项注释掉,重新mvn install
再来执行main方法,则获取bean失败。。
网友评论