简介
SpringBoot 就是脚手架,就是把像 SSM 中非常繁杂的配置工作给融合并简化了。
1、SpringBoot 入门
1.1、SpringBoot 的功能
-
创建独立的 Spring应用程序
-
直接嵌入 Tomcat、 Jetty 或 Undertow (无需部署 WAR包,打包成 Jar 本身就是个可以运行的应用程序 )
-
提供一站式的“starter”依赖项, 以简化 Maven配置 ( 需要整合计么框架,直接导对应框架的 starter依赖)
-
尽可能地自动配置 Spring 和第三方库(除非特殊情况,否则几乎不需要你进行什么配置)
-
提供生产就绪功能,如指标、运行状况检查和外部化配置
-
没有代码生成,也没有 XML配置的要求
1.2、项目文件目录介绍
.mvn、mvnw、mvnw.cmd 这几个文件删了都可以,就是解决如果没有 maven 环境的话。HELP.md 也可以直接删掉。
主函数类、pom.xml、application.properties、.gitignore 这几个文件比较重要
.gitignore 是 git 版本控制的忽略清单
application.properties 是配置类,在里面可以修改许多 SpringBoot 默认的配置,还可以自定义一些属性,然后在代码的变量上 @Value("${aaa.bbb}") 就可以取到了。
1.3、SpringBoot 整合 Web 相关框架
主要就是加入 Spring Web 依赖。引入依赖就会内置 tomcat服务器。
然后就可以直接写 Controller,加上 @RestController 注解就可以使用了。
因为 SpringBoot 的默认包扫描机制:从启动类所在包开始,扫描当前包及其子级包下的所有文件,所以一般可以不用加 @ComponentScan(basePackages = {"xxx"}) 这个注解了。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
1.3.1、整合 Thymeleaf框架
thymeleaf 是前后端不分离的项目使用的,需要后端去解析。
先引入 thymeleaf依赖,然后静态的资源方到 resources/static/ 下面,页面资源放到 resources/templates/ 下面,然后配置前缀。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
spring: mvc: static-path-pattern: /static/**
1.4、SpringBoot 整合 MyBatis框架
需要引入两个依赖,MyBatis、数据库对应的 Connector 的依赖。
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
引入依赖后,若没有给主类取消数据源的配置,则必须去配置数据源
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) # mysql数据库连接 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/student?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8 username: root password: 123456
1.5、SpringBoot 的日志框架
日志级别:TRACE < DEBUG < INFO < WARN < ERROR < FATAL
1.5.1、日志门面和日志实现
我们首先要区分一下,什么是日志门面(Facade),什么是日志实现,我们之前学习的 JUL 实际上就是一种日志实现,我们可以直接使用 JUL 为我们提供的日志框架来规范化打印日志,
而日志门面,如 SIf4j, 是把不同的日志系统的实现进行了具体的抽象化,只提供了统
一的日志使用接口,使用时只需要按照其提供的接口方法进行调用即可,由于它只是一个接口,并不是一个具体的可以直接单独使用的日志框架,所以最终日志的格式、记录级别、输出方式等都要通过接口绑定的具体的日志系统来实现,这些具体的日志系统就有 log4j、 logback、java.util.logging 等, 它们才实现了具体的日志系统的功能。
日志门面就像 JDBC 一样,是中间层。
为了做到在众多日志框架下一律统一使用 Slf4j 进行日志打印,就保留不同日志框架的接口和类定义等关键信息,而将实现全部定向为 SIf4j 的调用。相当于有着和原有日志框架一样的外壳, 对于其他框架来说依然可以使用对应的类进行操作,而具体的实现已经是Slf4j 的了。
SpringBoot 使用 Slf4j 作为日志门面,然后内置了 Logback 作为日志实现,其依赖是 spring-boot-starter-logging。
1.5.2、使用日志
// 这种方法较为麻烦 Logger logger = LoggerFactory.getLogger(指定类.class); logger.info("xxxx"); // 引入 lombok注解,在当前类上加上 @Slf4j注解 log.info("xxxx");
1.5.3、配置 Logback日志
可以定制化,写一个配置文件,SpringBoot 推荐的命名是 logback-spring.xml,放在 resources文件夹下。
一旦添加了这个文件,就会覆盖掉原有的文件。所以最好下载别人写好的模板,然后用 <include resource="相对路径" /> 导入使用。
然后 banner 和日志是分开的,如果想修改,就在 resources/ 下添加 banner.txt 即可,然后还可以写一些代码进行自定义。
对于不同的日志输出,可以添加不同的 appender,然后加入 root 里面,就可以直接使用了,比如控制台打印、文件打印
1.5.4、MDC机制
Logback 内置的日志字段还是比较少,如果我们需要打印有关业务的更多的内容,包括自定义的一些数据,需要借助 logback MDC 机制,MDC 为“Mapped Diagnostic Context" (映射诊断上下文),即将一些运行时的上下文数据通过 logback 打印出来;此时我们需要借助org.sl4j.MDC 类。
比如我们现在需要记录是哪个用户访问我们网站的日志,只要是此用户访问我们网站,都会在日志中携带该用户的 ID,我们希望每条日志中都携带这样一段信息文本,而官方提供的字段无法实现此功能,这时就需要使用 MDC机制,是用 ThreadLocal 实现的。
1.6、多环境配置
1.6.1、配置文件的多环境
# 在 application.yml 中指定使用的配置文件 # 这样就会去追加使用 application-dev.yml,如果指定为 prod,则是 application-prod.yml spring: profiles: # 这样是直接指定,使用下面这种方式就需要在 pom.xml 中配置 maven 的 # active: dev active: '@environment@'
1.6.2、日志系统的多环境
<!-- 修改 logback-spring.xml 文件的那一段,即在 root 上加一层 --> <springProfile name="dev"> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="FILE" /> </root> </springProfile> <springProfile name="prod"> <root level="INFO"> <appender-ref ref="FILE" /> </root> </springProfile>
1.6.3、Maven打包 的多环境
<!-- 加在 pom.xml 文件的 project标签里面 --> <profiles> <profile> <id>dev</id> <avtivation> <activeByDefault>true</activeByDefault> </avtivation> <properties> <environment>dev</environment> </properties> </profile> <profile> <id>prod</id> <avtivation> <activeByDefault>false</activeByDefault> </avtivation> <properties> <environment>prod</environment> </properties> </profile> </profiles> <!-- 加在 pom.xml 文件的 build标签里面 --> <resources> <!-- 排除配置文件 --> <resource> <directory>src/main/resources</directory> <!-- 先排除所有的配置文件 --> <excludes> <!-- 这里使用了通配符,也可以写多个 exclude标签进行排除 --> <exclude>application*.yml</exclude> </excludes> </resource> <!-- 根据激活条件引入打包所需的配置和文件 --> <resource> <directory>src/main/resources</directory> <!-- 引入所需环境的配置文件 --> <filtering>true</filtering> <includes> <include>application.yml</include> <!-- 根据 maven 选择的环境导入配置文件 --> <include>application-${environment}.yml</include> </includes> </resource> </resources>
1.7、项目打包部署
切换到对应的环境,比如 prod,然后刷新一下 maven,然后 clean 掉之前的东西,然后点 package 进行打包。
部署就是将 jar 包放到指定的文件夹下,然后 cmd,然后 java -jar jar包的名称。这样就运行了
也可以创建一个 txt 文件,然后名称改为 start.bat,然后在里面写入 java -jar jar包的名称。
1.7.1、执行 maven 命令
-
mvn package -DskipTests:打包时通过测试
1.8、实现 Runner接口
如果我们需要在项目启动完成之后,紧接着执行一段代码,那我们就可以编写自定义的 ApplicationRunner、CommandLineRunner 来解决,它会在项目启动完成后执行。
当然,如果你写在主类的 main方法的 run方法之后,也是可以的,因为 SpringBoot 提供了异步执行机制,但是肯定不优雅。
1.8.1、Runner实现类
可以有多个自定义的 Runner,若需要给他们排序,就可以加上 @Order(数字)注解,数字越小优先级越高。
除了 @Order注解,还可以实现 Ordered接口,然后重写 getOrder方法,返回值就行了。
@Order(1) @Component public class TestRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("我是自定义执行!"); } }
2、扩展
2.1、异步任务
在配置类或启动类上添加 @EnableAsync 开启异步,然后在要进行异步的方法上添加 @Async注解,但类必须是一个 bean,可被扫描到。
2.2、定时任务
在配置类或启动类上添加注解 @EnableScheduling,开启定时任务
在方法上添加注解 @Scheduled(fixedRate = 10006030),里面也可以是 cron表达式
fixedRate 是无论上一次任务执行完与否,两次任务开始时间的间隔,
fixedDelay 是必须等上一次执行完之后,即上一个任务执行结束与下一个任务开始执行的间隔
2.3、监听器
监听器就是等待某个事件的触发,触发后执行相关操作
不仅有 ContextRefreshedEvent,还可以监听其他的事件。
@Component public class TestListener implements ApplicationListener<ContextRefreshedEvent> { @override public void onApplicationEvent(ContextRefreshedEvent event) { // 做相关操作 } } // 自定义事件 public class TestEvent extends ApplicationEvent { public TestEvent(Object source) { super(source); } } // 发布事件 public class TestController { @Resource ApplicationContext context; @GetMapping("publishTestEvent") public void publishTestEvent() { context.publishEvent(new TestEvent("哈哈哈")); } }
2.4、Aware系列接口
Aware 的中文意想为感知。简单来说,他就是一个标识,实现此接口的类会获得某些感知能力,Spring容器会在 Bean 被加载时,根据类实现的感知接口,会调用类中实现的对应感知方法。
比如 AbstractAuthenticationProcessingilter 实现了 ApplicationEventPublisherAware接口,此接口的感知功能为事件发布器,在 Bean 加载时,会调用实现类中的 setApplicationEventPublisher方法,就可以得到时间发布者了,而 AbstractAuthenticationProcessingFilter类则利用此方法,在 Bean加载阶段获得了容器的事件发布器,以便之后发布事件使用。
@Service public class TestService implements BeanNameAware { @override public void setBeanName(String s) { // BeanNameAware 会在当前 service 被注册成 bean 的时候感知到 bean 的名称, // 然后调用 setBeanName 就可以给到你名称 s,s 其实就是 testService } }
2.5、邮件 Mail
2.5.1、开启邮箱的服务
发邮件之前需要打开邮箱的 POP3/SMTP服务,开启服务之后会给一个签名,有用的。
2.5.2、SpringBoot 整合
2.5.2.1、配置
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> spring: mail: host: 邮箱提供的 smtp服务器地址,比如 smtp.163.com username: 你的邮箱账号 password: 开启服务给的签名
2.5.2.2、代码测试
public class Test { @Autowired JavaMailSender mailSender; void sendTest() { // SimpleMailMessage 是一个比较简单的邮件封装,不能加附件;MimeMessage 可以 // MimeMessage mimeMessage = mailSender.createMimeMessage(); // MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true); SimpleMailMessage mailMessage = new SimpleMailMessage(); // 设置邮件标题 mailMessage.setSubject("睡觉通知书"); // 设置邮件内容 mailMessage.setText("XXX你好,请您睡觉!"); // 设置邮件发送者,需要与配置文件中的邮箱一致 mailMessage.setFrom("发送方邮箱号"); // 设置邮件接收方 mailMessage.setTo("接收方邮箱号"); mailSender.send(mailMessage); } }
3、SpringBoot原理
3.1、SpringApplication类
3.1.1、项目启动类
总之,就是在初始阶段将主类注册成为一个 Bean。
-
首先是在启动类里面,我们调用 SpringApplication 的 run方法来启动项目。所以去看 run
public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } }
3.1.2、SpringApplication类
-
然后 run 的返回类型是一个可配置的应用上下文,然后看 return 的 run方法
-
这个 run 先 new 了一个 SpringApplication类的实例,然后再调用类方法 run
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); }
3.1.2.1、构造方法
-
构造方法其实就是在初始化赋值等一系列操作,很重要的就是 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))
然后去调用类中的非静态 run方法
public SpringApplication(Class<?>... primarySources) { this((ResourceLoader)null, primarySources); } public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { ...... this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); // 判断当前 SpringBoot应用程序是否为 Web项目,并返回当前的项目类型 // 一般就是返回 NONE 或 SERVLET,还有一个 return 是 REACTIVE // 根据类路径下判断是否包含 spring-boot-web依赖,有就是 SERVLET类型,没有就是 NONE类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); // 创建所有 ApplicationContextInitializer 的实现类的实例对象 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); }
3.1.2.2、getSpringFactoriesInstances方法
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return this.getSpringFactoriesInstances(type, new Class[0]); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = this.getClassLoader(); // 主要是会调用 SpringFactoriesLoader.loadFactoryNames 这个方法 // 获取项目所有的依赖中 META-INF/spring.factories 中配置的对应接口类的实现类的全限定名列表 // 如果传的 type 不是 null,那么就会去找对应接口类的实现类,否则就是找默认的 Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 根据 全限定名Set 迭代创建实例对象集合 List List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 根据对应类上的 Order接口或是注解进行排序 AnnotationAwareOrderComparator.sort(instances); return instances; }
3.1.2.3、类方法 run
查看这个可以发现,SpringBoot 就是 Spring 的一层壳,是在 ApplicationContext 之上做了封装。
public ConfigurableApplicationContext run(String... args) { long startTime = System.nanoTime(); DefaultBootstrapContext bootstrapContext = this.createBootstrapContext(); ConfigurableApplicationContext context = null; this.configureHeadlessProperty(); // 获取所有的 SpringApplicationRunListener,并通知启动事件, // 默认只有一个实现类 EventPublishingRunListener // EventPublishingRunListener 会将初始化各个阶段的事件转发给所有监听器 SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass); try { // 环境配置 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); this.configureIgnoreBeanInfo(environment); // 打印 Banner Banner printedBanner = this.printBanner(environment); // 创建 ApplicationContext,会根据是否为 Web容器使用不同的 ApplicationContext实现类 context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); // 初始化 ApplicationContext this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 初始化 ApplicationContext 的 refresh方法。bean 的注册、生成都在这里面 this.refreshContext(context); this.afterRefresh(context, applicationArguments); Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup); } listeners.started(context, timeTakenToStartup); // 在这里就是执行所有的自定义 Runner,只要实现了 ApplicationRunner 或 CommandLineRunner this.callRunners(context, applicationArguments); } catch (Throwable var12) { this.handleRunFailure(context, var12, listeners); throw new IllegalStateException(var12); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); listeners.ready(context, timeTakenToReady); return context; } catch (Throwable var11) { this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null); throw new IllegalStateException(var11); } }
3.1.2.4、createApplicationContext方法
这个类对象 applicationContextFactory 是 ApplicationContextFactory类型。
protected ConfigurableApplicationContext createApplicationContext() { return this.applicationContextFactory.create(this.webApplicationType); }
3.1.2.5、prepareContext方法
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 环境配置 ...... // 将 Banner 注册为一个 Bean ...... // 获取一开始传入的项目主类 Set<Object> sources = this.getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 将主类直接注册成 Bean,这样就可以通过注解加载了 this.load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); } protected void load(ApplicationContext context, Object[] sources) { ...... // 这个 loader 是 BeanDefinitionLoader类的对象,套了很多层 loader.load(); }
3.1.3、WebApplicationType类
3.1.3.1、deduceFromClasspath方法
deduce方法就是推断项目类型,如果项目导入的依赖里有指定的依赖,就判断为对应的项目类型。
static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) { return REACTIVE; } else { String[] var0 = SERVLET_INDICATOR_CLASSES; int var1 = var0.length; for(int var2 = 0; var2 < var1; ++var2) { String className = var0[var2]; if (!ClassUtils.isPresent(className, (ClassLoader)null)) { return NONE; } } return SERVLET; } }
3.1.4、SpringFactoriesLoader类
3.1.4.1、loadFactoryNames方法
-
this.setInitializers 的时候,传的 type 是 ApplicationContextInitializer.class spring.factories 是 Spring 仿造 Java SPI 实现的一种类加载机制,在 spring.factories 中配置接口的实现类名称,然后在程序中读取这些配置文件并实例化。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } // 得到全限定名 String factoryTypeName = factoryType.getName(); // 又调用了 loadSpringFactories 和 getOrDefault 这两个方法 // loadSpringFactories 会得到所有的实现类 // getOrDefault 即如果传的 type 不是 null,那么就会去找对应接口类的实现类,否则就是找默认的 return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
3.1.4.2、loadSpringFactories方法
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { Map<String, List<String>> result = (Map)cache.get(classLoader); if (result != null) { return result; } else { HashMap result = new HashMap(); try { // 通过 classLoader 获取指定路径 META-INF/spring.factories 下的资源 // 自动装配的核心内容 Enumeration urls = classLoader.getResources("META-INF/spring.factories"); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryTypeName = ((String)entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); String[] var10 = factoryImplementationNames; int var11 = factoryImplementationNames.length; for(int var12 = 0; var12 < var11; ++var12) { String factoryImplementationName = var10[var12]; ((List)result.computeIfAbsent(factoryTypeName, (key) -> { return new ArrayList(); })).add(factoryImplementationName.trim()); } } } result.replaceAll((factoryType, implementations) -> { return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); }); cache.put(classLoader, result); return result; } catch (IOException var14) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14); } } }
3.1.5、ApplicationContextFactory类
在构造方法中已经用 Lambda表达式通过匿名内部类创建了一个 DEFAULT
public interface ApplicationContextFactory { ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { switch(webApplicationType) { case SERVLET: return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: // 这个就是 Spring 中使用的类, return new AnnotationConfigApplicationContext(); } } catch (Exception var2) { throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2); } }; ConfigurableApplicationContext create(WebApplicationType webApplicationType); static ApplicationContextFactory ofContextClass(Class<? extends ConfigurableApplicationContext> contextClass) { return of(() -> { return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass); }); } static ApplicationContextFactory of(Supplier<ConfigurableApplicationContext> supplier) { return (webApplicationType) -> { return (ConfigurableApplicationContext)supplier.get(); }; } }
3.1.6、AnnotationConfigApplicationContext类
创建了一个 reader 和一个 scanner,
public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); // 一个 AnnotatedBeanDefinitionReader类型的 reader this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); // 一个 ClassPathBeanDefinitionScanner类型的 scanner this.scanner = new ClassPathBeanDefinitionScanner(this); }
3.1.7、AnnotatedBeanDefinitionReader类
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { ...... // 注册一些后置处理器 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); }
3.1.8、AnnotationConfigUtils类
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { registerAnnotationConfigProcessors(registry, (Object)null); } public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet(8); RootBeanDefinition def; if (!registry.containsBeanDefinition("org.springframework.context.annotation.internalConfigurationAnnotationProcessor")) { // 注册了 ConfigurationClassPostProcessor 用于处理 @Configuration、@Import 等注解 // 它继承自 BeanDefinitionRegistryPostProcessor, // 所以它的执行时间在 Bean 定义加载完成之后,Bean 初始化之前 def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalConfigurationAnnotationProcessor")); } if (!registry.containsBeanDefinition("org.springframework.context.annotation.internalAutowiredAnnotationProcessor")) { // AutowiredAnnotationBeanPostProcessor 用于处理 @Value 等注解自动注入 def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalAutowiredAnnotationProcessor")); } ...... . return beanDefs; }
3.2、@SpringBootApplication 的自动配置原理
启动类已经在初始阶段注册为 Bean,那么在加载时,就会根据注解进行更多额外的操作。
导的依赖全是以 jar包的形式打包的,只有自己写的代码,在打包的时候才会是 class文件。
3.2.1、@SpringBootApplication
-
@ComponentScan 会默认将主类所在包作为 basePackage,然后进行自动扫描
-
@SpringBootConfiguration 就是声明是一个配置类,再进去有 @Configuration注解
-
@EnableAutoConfiguration 非常重要,
@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 }
3.2.2、@EnableAutoConfiguration
-
主要就是 @Import 这个注解,老套路了,通过这种方式将一些外部的 Bean 加载到容器中,这里加载了 AutoConfigurationImportSelector @Import、@Configuration 能生效是因为 ConfigurationClassPostProcessor。
@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 {}; }
3.2.3、AutoConfigurationImportSelector类
可以看到 AutoConfigurationImportSelector 实现了大量的接口,包括很多 Aware类型的接口,主要就是来感知某些必要的对象,并将其存到当前类中。
其中最核心的是 DeferredImportSelector接口,它是 ImportSelector的子类,它定义了 selectImports方法,用于返回需要加载的类名称,在 Spring 加载 ImportSelector类型的 Bean 的时候,就会调用此方法获取更多需要加载的类,然后将这些类注册为 Bean。
现在知道了项目中的 @Configuration配置类的大致配置流程,即知道了 SpringBoot框架如何注册项目中的 Bean;再看看 AutoConfigurationImportSelector 是如何实现自动配置的,即如何把外部依赖的配置类注册成为 Bean。
静态内部类AutoConfigurationGroup 有一个 process方法,它是父接口 DeferredImportSelector 的实现。会调用 process方法获取所有的自动配置类。
process 这个方法就是最终的处理,可以得到 EnableAutoConfiguration 的实现类,然后进行自动配置。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { // ... // 有一个静态内部类 AutoConfigurationGroup private static class AutoConfigurationGroup implements Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware { } }
3.2.3.1、静态内部类的 process方法
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> { return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName()); }); // 调用 getAutoConfigurationEntry 这个方法, // 获取所有的 配置类Entry,其实就是读取 spring.factories 来查看有哪些自动配置类 AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); Iterator var4 = autoConfigurationEntry.getConfigurations().iterator(); while(var4.hasNext()) { String importClassName = (String)var4.next(); this.entries.putIfAbsent(importClassName, annotationMetadata); } }
3.2.3.2、getAutoConfigurationEntry方法
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); // getCandidateConfigurations方法得到 候选配置类集合 // 就是得到了所有依赖给的 自动配置类, List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); // 进行去重操作 configurations = this.removeDuplicates(configurations); // 得到手动排除掉的自动配置类,然后去排除掉。一般没有 Set<String> exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 排除操作完成之后,根据注解进行过滤。会根据条件注解进行过滤, // 即一般依赖的配置类中会有 @ConditionalOnClass 等等@ConditionalOn开头的注解, // 当引入对应依赖后,依赖给的配置才生效,SpringBoot 就不会把相关的配置类过滤掉 configurations = this.getConfigurationClassFilter().filter(configurations); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions); } }
3.2.3.3、getCandidateConfigurations方法
-
就是根据 EnableAutoConfiguration 这个类型,去调用 loadFactoryNames方法,
-
然后 loadFactoryNames 这个方法我们之前看过,就会去调用 loadSpringFactories方法
-
然后 loadSpringFactories方法就是去 META-INF/spring.factories 中来查看有哪些 EnableAutoConfiguration 的实现类,即自动配置类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> 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; }
3.2.4、ImportSelector类
selectImports方法用于返回需要加载的类名称,在 Spring 加载 ImportSelector类型的 Bean 的时候,就会调用此方法获取更多需要加载的类,然后将这些类注册为 Bean。
目前已知使用 @Import 有特殊机制的接口:Importselector、ImportBeanDefinitionRegistrar,以及 @Confoiguration,都是由 ConfigurationClassPostProcessor 来处理 @Import 的。
public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); @Nullable default Predicate<String> getExclusionFilter() { return null; } }
3.2.5、ConfigurationClassPostProcessor类
这个类是 Spring 的一个类,不是 SpringBoot 的。
它实现了 BeanDefinitionRegistryPostProcessor 这个接口,这个接口有 postProcessBeanDefinitionRegistry 这个方法,就是在 Bean 注册完成之后进行一个后置处理
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware { // ... }
3.2.5.1、postProcessBeanDefinitionRegistry方法
主要是去调用了 processConfigBeanDefinitions方法。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } else if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry); } else { this.registriesPostProcessed.add(registryId); this.processConfigBeanDefinitions(registry); } }
3.2.5.2、processConfigBeanDefinitions方法
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList(); // 注意:这个阶段仅仅是将所有的 Bean 完成扫描,得到了所有的 BeanDefinition, // 但还没进行任何区分。即此时只有 Bean定义,但 Bean 还没有被初始化。 // 是后置处理器,在 BeanDefinition 全部加载完成后,Bean 初始化加载前,进行工作。 // Candidate 即候选者,一会会将标记了 @Configuration 的类, // 作为 ConfigurationClass 加入到 configCandidates 中 // 获取所有 Bean定义的名称 String[] candidateNames = registry.getBeanDefinitionNames(); String[] var4 = candidateNames; int var5 = candidateNames.length; // 遍历 Bean定义的名称的数组 for(int var6 = 0; var6 < var5; ++var6) { String beanName = var4[var6]; BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (this.logger.isDebugEnabled()) { this.logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { // 这里判断此 Bean定义上是否添加了 @Configuration注解,即是否是一个配置类 // 如果添加了 @Configuration注解,是一个配置类,那么就加入候选者列表去。 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } if (!configCandidates.isEmpty()) { // ...... // 创建了一个 ConfigurationClassParser解析器,用于解析配置类 @Import 这些注解 ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); // 所有配置类的 BeanDefinitionHolder集合,存放所有的待解析配置类 Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates); // 这个 Set集合用于存放已经解析完成的类 HashSet alreadyParsed = new HashSet(configCandidates.size()); do { StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse"); // 调用 parse 这个核心方法,进行解析 parser.parse(candidates); parser.validate(); // 得到 configurationClasses 的 ketSet(),即解析好的配置类信息 // parser.getConfigurationClasses() 就是 parser类对象的 getter方法 Set<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } // 已经解析出所有的配置类了,就通过 BeanDefinition 去注册 Bean this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); // ...... // 这里就是将 candidates 这个 Set集合全部进行解析 // 循环中可能会因为 @Import 新增更多的待解析配置类,全放进 candidates 里面 } while(!candidates.isEmpty()); // ...... } }
3.2.6、ConfigurationClassParser类
3.2.6.1、parse方法
public void parse(Set<BeanDefinitionHolder> configCandidates) { Iterator var2 = configCandidates.iterator(); while(var2.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next(); BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { // 主要是调用这个 parse方法 this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) { this.parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName()); } else { this.parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException var6) { throw var6; } catch (Throwable var7) { throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", var7); } } // 最后延迟处理的配置类,调用内部类DeferredImportSelectorHandler 的 process方法 // 最后调用的是 AutoConfigurationImportSelector 的静态内部类AutoConfigurationGroup 的实现方法 process this.deferredImportSelectorHandler.process(); } protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { this.processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER); }
3.2.6.2、processConfigurationClass方法
这个方法就是在处理配置类,
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException { // @Configuration 相关注解处理 if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } return; } this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter); do { // doProcessConfigurationClass 是核心方法, sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter); } while(sourceClass != null); // 把解析完成的类信息放到 configurationClasses 里面 this.configurationClasses.put(configClass, configClass); } }
3.2.6.3、doProcessConfigurationClass方法
@Nullable protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass, Predicate<String> filter) throws IOException { // ...... // 处理 Import注解 this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true); // ...... return null; }
3.2.6.4、processImports方法
private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) { if (!importCandidates.isEmpty()) { if (checkForCircularImports && this.isChainedImportOnStack(configClass)) { this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { Iterator var6 = importCandidates.iterator(); while(var6.hasNext()) { ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var6.next(); Class candidateClass; // 如果是 ImportSelector类型,就进入 if 做一系列工作 if (candidate.isAssignable(ImportSelector.class)) { candidateClass = candidate.loadClass(); ImportSelector selector = (ImportSelector)ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } // 再判断是不是 DeferredImportSelector 的实现类,是就做 handler方法 // 不是的话就正常按照 ImportSelector类型进行加载 // Deferred 就是延迟的意思,即不会像 else 里面一样,立即做一个递归处理 // 而是交给内部类deferredImportSelectorHandler 去处理 // 最后会在 parse方法的最后一行调用内部类的 process方法去处理 // 内部类的 process方法其实最后调用的是 AutoConfigurationImportSelector 的静态内部类AutoConfigurationGroup 的实现方法 if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector); } else { // 不是 DeferredImportSelector 的实现类,就立即进行递归处理 // 调用 selectImports方法获取所有需要加载的类 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter); // 递归处理,直到全部处理完 this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // 如果不是 ImportSelector类型,再判断是不是 ImportBeanDefinitionRegistrar 类型 candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); // 往 configClass 放入 ImportBeanDefinitionRegistrar信息,之后再处理 // 处理这个 configClass 的时机是 loadBeanDefinitions的实现方法的最后一行 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // 如果两个类型都不是,即是一个普通的配置类 this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } } catch (BeanDefinitionStoreException var17) { throw var17; } catch (Throwable var18) { throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var18); } finally { this.importStack.pop(); } } } }
3.2.6.5、DeferredImportSelectorHandler内部类
这个内部类的 process方法其实最后调用的是 AutoConfigurationImportSelector 的静态内部类AutoConfigurationGroup 的实现方法 process。
private class DeferredImportSelectorHandler { @Nullable private List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImportSelectors; private DeferredImportSelectorHandler() { this.deferredImportSelectors = new ArrayList(); } public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) { ConfigurationClassParser.DeferredImportSelectorHolder holder = new ConfigurationClassParser.DeferredImportSelectorHolder(configClass, importSelector); if (this.deferredImportSelectors == null) { ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler(); handler.register(holder); handler.processGroupImports(); } else { this.deferredImportSelectors.add(holder); } } public void process() { List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; try { if (deferredImports != null) { ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler(); deferredImports.sort(ConfigurationClassParser.DEFERRED_IMPORT_COMPARATOR); deferredImports.forEach(handler::register); handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList(); } } }
3.2.7、DeferredImportSelector接口
这个 process方法是 AutoConfigurationImportSelector类的静态内部类的 process方法的抽象方法。
public interface DeferredImportSelector extends ImportSelector { @Nullable default Class<? extends DeferredImportSelector.Group> getImportGroup() { return null; } public interface Group { void process(AnnotationMetadata metadata, DeferredImportSelector selector); Iterable<DeferredImportSelector.Group.Entry> selectImports(); public static class Entry { // ...... } } }
3.2.8、ConfigurationClassBeanDefinitionReader类
3.2.8.1、loadBeanDefinitions方法
进行迭代注册
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) { ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator trackedConditionEvaluator = new ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator(); Iterator var3 = configurationModel.iterator(); while(var3.hasNext()) { ConfigurationClass configClass = (ConfigurationClass)var3.next(); this.loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); } }
3.2.8.2、loadBeanDefinitionsForConfigurationClass方法
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator trackedConditionEvaluator) { if (trackedConditionEvaluator.shouldSkip(configClass)) { String beanName = configClass.getBeanName(); if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { this.registry.removeBeanDefinition(beanName); } this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName()); } else { if (configClass.isImported()) { // 把配置类自己给注册了,即先注册自己 this.registerBeanDefinitionForImportedConfigurationClass(configClass); } Iterator var3 = configClass.getBeanMethods().iterator(); while(var3.hasNext()) { BeanMethod beanMethod = (BeanMethod)var3.next(); // 还要注册配置类中有 @Bean注解标识的方法 this.loadBeanDefinitionsForBeanMethod(beanMethod); } // 注册 @ImportResources 引入的 XML配置文件中读取的 Bean定义 this.loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); // 注册 configClass 中经过解析后保存的所有 ImportBeanDefinitionRegistrar, // 注册对应的 BeanDefinition。即之前在 processImports方法中, // 配置类是 ImportBeanDefinitionRegistrar类型的情况下放入的 this.loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); } }