目录
1、SpringBoot项目构建
1.1、官网生成
1.2、IDE在线模板生成
2、常见配置
2.1、入口类和相关注解
2.2、Banner
2.3、常规配置
2.4、日志
2.5、profile环境切换
2.6、静态资源
3、核心原理
3.1、自动装配
1、搭配@Configuration注解使用
2、实现importSelector接口
3、实现importBeanDefinitionRegistrar接口
SpringBoot是为了简化开发而出现的技术。
1、SpringBoot项目构建
1.1、官网生成
https://start.spring.io/
1.2、IDE在线模板生成
在IDEA等编译工具中,也有对应的生成方法。
2、常见配置
2.1、入口类和相关注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
在SpringBoot的启动类SpringApplication中的run方法里,可以看到,整个run方法返回的类就是ConfigurableApplicationContext,这个类继承了ApplicationContext。
在run方法内,refreshContext(ConfigurableApplicationContext context)方法完成对context的内部构建,启动时,它会执行,最终还是调用refresh()方法,这一点上和Spring框架IOC的初始化没有区别。
@SpringBootApplication注解内容如下,IOC在初始化时一定会加载这个注解。
//使用范围,类
@Target({ElementType.TYPE})
//作用域,运行时生效
@Retention(RetentionPolicy.RUNTIME)
//该注解会被API抽取
@documented
//可以被继承
@Inherited
//以上四个注解为元注解
//相当于@Configuration
@SpringBootConfiguration
//自动装配
@EnableAutoConfiguration
//读取指定文件
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication
可以看到,这是一个集合注解,其中包含很多注解内容
- 使用范围,类,@Target({ElementType.TYPE})
- 作用域,运行时生效,@Retention(RetentionPolicy.RUNTIME)
- 该注解会被API抽取,@documented
- 可以被继承,@Inherited
- @SpringBootConfiguration,其内部代码显示其实这就是一个@Configuration注解
- @EnableAutoConfiguration,自动装配
- @ComponentScan,扫描文件,目前的默认配置是扫描@SpringBootApplication注解标注下同级的包以及子包
2.2、Banner
在resources文件夹下增加文件“banner.txt”,就可以将启动图案替换为该文件中的图案。
2.3、常规配置
在SpringBoot中提供两种配置文件,applicationContext.properties和applicationContext.yml。这两种文件的作用是一样的,但是语法格式会有所不同。
- server.port=8081,将访问端口改为8081
- server.servlet.context-path=/spring,设置访问路径,/spring
- 如果想要自定义配置属性,可以根据对应语法进行书写,然后使用@Value注解进行获取,如:@Value("${自定义数据}")
- 自定义配置属性也可以被加载到一个Bean中,然后托管给IOC。使用注解@ConfigurationProperties,此时自定义属性必须有一个前缀,该注解会将所有带有这个前缀的配置加载进来。@ConfigurationProperties(prefix = "前缀"),前缀后的数据名称需要和bean的数据名称保持一致,不然无法找到
2.4、日志
SpringBoot默认支持Logback,依赖已经被包含进去。
生效需要在配置文件中设置日志文件路径和日志输出级别
logging.file.path=d;/log logging.level.org.springframework.web=DEBUG
或者直接使用logback.xml进行配置
2.5、profile环境切换
在SpringBoot中进行环境切换是比较简单的,只需要预先配置好对应环境的文件,比如生产环境配置:application-dev.properties,测试环境配置:application-dev.properties,然后在主配置中增加配置:
spring.profiles.active=dev
这样就可以决定引用哪一个配置文件。
2.6、静态资源
在创建SpringBoot项目时,resources文件夹下的static文件夹存放静态文件,template文件夹下存放动态文件。
3、核心原理
3.1、自动装配
@SpringBootApplication注解是一个复合注解,在其中起到自动装配作用的,是@EnableAutoConfiguration
@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 {};
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
@import({Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class>[] basePackageClasses() default {};
}
除了四个自定义注解必须的元注解,就是@import注解起到特殊的作用。
@import注解主要起到注入外部Class的作用,在Spring中,能将Bean导入IOC的方法有很多,比如:
- 基于xml配置文件Bean标签
- 基于xml配置文件@Component
- 基于jiava配置类@Configuration和@Bean
- 基于java配置类,@ComponentScan+@Component
- FactoryBean接口getObject
- @import注解
@import注解的使用有三种。
1、搭配@Configuration注解使用
@Configuration
@import({User.class})
public class JavaConfig {
}
这种写法可以直接将类注入到IOC容器中,但需要注意的是,这种写法是写死在程序中的。
2、实现importSelector接口
可以将一个类实现importSelector接口的selectimports方法,在这个方法内返回类的名字。使用@import注解在自定义注解上,引入继承importSelector的类,即可将需要的类加入IOC中。
public class MyimportSelector implements importSelector {
@Override
public String[] selectimports(Annotationmetadata importingClassmetadata) {
return new String[]{User.class.getName()};
}
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@import({MyimportSelector.class})
public @interface EnableMyDefine {
}
@EnableMyDefine
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
}
}
这种写法就更加灵活
3、实现importBeanDefinitionRegistrar接口
可以实现接口importBeanDefinitionRegistrar的方法registerBeanDefinitions,在这个方法中,有一个IOC的注册器,只要在其中完成注册,就可以实现加载,其他文件代码和第二种差别不大。
public class MyimportBeanDefinitionRegistrar implements importBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(Annotationmetadata importingClassmetadata,
BeanDefinitionRegistry registry) {
RootBeanDefinition user = new RootBeanDefinition(User.class);
registry.registerBeanDefinition("user", user);
}
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@import({MyimportBeanDefinitionRegistrar.class})
public @interface EnableMyDefine {
}
@EnableMyDefine
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
}
}
了解到这些,就可以看这几个@import注解的作用了
在注解@EnableAutoConfiguration中,可以看到对于@import注解中引入的AutoConfigurationimportSelector根据命名来看就是实现了importSelector接口,那么可以看一下代码
@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 {};
}
直接看selectimport方法,在正常流程下,会执行getAutoConfigurationEntry中。List
public String[] selectimports(Annotationmetadata annotationmetadata) {
if (!this.isEnabled(annotationmetadata)) {
return NO_importS;
} else {
AutoConfigurationimportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationmetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationimportSelector.AutoConfigurationEntry getAutoConfigurationEntry(Annotationmetadata annotationmetadata) {
if (!this.isEnabled(annotationmetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationmetadata);
List configurations = this.getCandidateConfigurations(annotationmetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set exclusions = this.getExclusions(annotationmetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationimportEvents(configurations, exclusions);
return new AutoConfigurationimportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
protected List getCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) {
List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in meta-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
这个位于org.springframework对应jar的配置文件spring.factories决定到底加载那些类。
后面的代码主要是为了对这些类做出一些过滤,最后把需要装配的类返回去。
阶段总结:实现importSelector接口的类可以根据springboot的jar包自动装配配置文件对指定的类进行加载,最后注入到IOC中,但是因为读取配置文件的原因,只能将第一方的类加载进去,第三方的无法被感知到,所以第三方jar也会带有一个一样的spring.factories文件来告诉IOC需要加载的类的路径。
那么既然自动装配装配了这么多类,有一些类其实并没有被依赖,那么没有被依赖的类是怎么过滤掉的呢?这就需要条件进行判断了。
在spring.factories同级目录下有一个文件spring-autoconfigure-metadata.properties,其内容大致为:
可以看到,里面有一些带有condition的配置,这是条件加载,只有在满足某些条件时才会将其装配到IOC中。
于是整个自动装配原理为:
- 在SpringBoot项目启动时,会加载@SpringBootApplication注解。
- 在@SpringBootApplication注解内,有@EnableAutoConfiguration注解
- 在@EnableAutoConfiguration注解中,有@import注解
- @import注解实现了importSelector接口的selectimports方法
- 加载meta-INF/spring-autoconfigure-metadata.properties文件,根据其条件过滤需要加载的文件内容。
- 加载meta-INF/spring.factories文件,根据配置类路径加载需要装配的类。
- 根据名称,配置文件等进行加载过滤,返回需要装配的类名,完成自动装配



