Java SpringBoot
    首先看SpringBoot的主配置类:

    1. @SpringBootApplication
    2. public class StartEurekaApplication
    3. {
    4. public static void main(String[] args)
    5. {
    6. SpringApplication.run(StartEurekaApplication.class, args);
    7. }
    8. }

    点进@SpringBootApplication来看,发现@SpringBootApplication是一个组合注解。

    1. @Target(ElementType.TYPE)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. @SpringBootConfiguration
    6. @EnableAutoConfiguration
    7. @ComponentScan(excludeFilters = {
    8. @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    9. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    10. public @interface SpringBootApplication {
    11. }

    先来看 @SpringBootConfiguration

    1. @Target({ElementType.TYPE})
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Configuration
    5. public @interface SpringBootConfiguration {
    6. }

    可以看到这个注解除了元注解以外,就只有一个@Configuration,那也就是说这个注解相当于@Configuration,所以这两个注解作用是一样的,它让开发者能够去注册一些额外的Bean,并且导入一些额外的配置。
    @Configuration还有一个作用就是把该类变成一个配置类,不需要额外的XML进行配置。所以@SpringBootConfiguration就相当于@Configuration。进入@Configuration,发现@Configuration核心是@Component,说明Spring的配置类也是Spring的一个组件。

    1. @Target({ElementType.TYPE})
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Component
    5. public @interface Configuration {
    6. @AliasFor(
    7. annotation = Component.class
    8. )
    9. String value() default "";
    10. }

    继续来看下一个@EnableAutoConfiguration,这个注解是开启自动配置的功能。

    1. @Target({ElementType.TYPE})
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. @AutoConfigurationPackage
    6. @Import({AutoConfigurationImportSelector.class})
    7. public @interface EnableAutoConfiguration {
    8. String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    9. Class<?>[] exclude() default {};
    10. String[] excludeName() default {};
    11. }

    可以看到它是由 @AutoConfigurationPackage@Import(EnableAutoConfigurationImportSelector.class)这两个而组成的,先说@AutoConfigurationPackage,他是说:让包中的类以及子包中的类能够被自动扫描到Spring容器中。

    1. @Target({ElementType.TYPE})
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. @Import({Registrar.class})
    6. public @interface AutoConfigurationPackage {
    7. }

    使用@Import来给Spring容器中导入一个组件 ,这里导入的是Registrar.class。来看下这个Registrar:

    1. static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    2. Registrar() {
    3. }
    4. public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    5. AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
    6. }
    7. public Set<Object> determineImports(AnnotationMetadata metadata) {
    8. return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
    9. }
    10. }

    就是通过以上这个方法获取扫描的包路径,可以debug查看具体的值:
    Spring Boot 注解原理,自动装配原理 - 图1
    那metadata是什么呢,可以看到是标注在@SpringBootApplication注解上的DemosbApplication,也就是主配置类Application:
    Spring Boot 注解原理,自动装配原理 - 图2
    其实就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。因此要把DemoApplication放在项目的最高级中(最外层目录)。
    看看注解@Import(AutoConfigurationImportSelector.class)@Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector。
    Spring Boot 注解原理,自动装配原理 - 图3
    可以从图中看出AutoConfigurationImportSelector 继承了 DeferredImportSelector 继承了 ImportSelector,ImportSelector有一个方法为:selectImports。将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。

    1. public String[] selectImports(AnnotationMetadata annotationMetadata) {
    2. if (!this.isEnabled(annotationMetadata)) {
    3. return NO_IMPORTS;
    4. } else {
    5. AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
    6. AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry =
    7. this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
    8. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    9. }
    10. }

    会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件。
    image.png
    有了自动配置类,免去了手动编写配置注入功能组件等的工作。那是如何获取到这些配置类的呢,看看下面这个方法:

    1. protected AutoConfigurationImportSelector.AutoConfigurationEntry
    2. getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
    3. if (!this.isEnabled(annotationMetadata)) {
    4. return EMPTY_ENTRY;
    5. } else {
    6. AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
    7. List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
    8. configurations = this.removeDuplicates(configurations);
    9. Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
    10. this.checkExcludedClasses(configurations, exclusions);
    11. configurations.removeAll(exclusions);
    12. configurations = this.filter(configurations, autoConfigurationMetadata);
    13. this.fireAutoConfigurationImportEvents(configurations, exclusions);
    14. return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    15. }
    16. }

    可以看到getCandidateConfigurations()这个方法,他的作用就是引入系统已经加载好的一些类,到底是那些类呢:

    1. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    2. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    3. Assert.notEmpty(configurations,
    4. "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    5. return configurations;
    6. }
    1. public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    2. String factoryClassName = factoryClass.getName();
    3. return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    4. }

    会从META-INF/spring.factories中获取资源,然后通过Properties加载资源:

    1. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    2. MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    3. if (result != null) {
    4. return result;
    5. } else {
    6. try {
    7. Enumeration<URL> urls = classLoader !=
    8. null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
    9. LinkedMultiValueMap result = new LinkedMultiValueMap();
    10. while(urls.hasMoreElements()) {
    11. URL url = (URL)urls.nextElement();
    12. UrlResource resource = new UrlResource(url);
    13. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    14. Iterator var6 = properties.entrySet().iterator();
    15. while(var6.hasNext()) {
    16. Map.Entry<?, ?> entry = (Map.Entry)var6.next();
    17. String factoryClassName = ((String)entry.getKey()).trim();
    18. String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
    19. int var10 = var9.length;
    20. for(int var11 = 0; var11 < var10; ++var11) {
    21. String factoryName = var9[var11];
    22. result.add(factoryClassName, factoryName.trim());
    23. }
    24. }
    25. }
    26. cache.put(classLoader, result);
    27. return result;
    28. } catch (IOException var13) {
    29. throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
    30. }
    31. }
    32. }

    可以知道SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,进行自动配置工作。以前需要自己配置的东西,自动配置类都完成了。如下图可以发现Spring常见的一些类已经自动导入。
    Spring Boot 注解原理,自动装配原理 - 图5
    接下来看@ComponentScan注解,@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }),这个注解就是扫描包,然后放入spring容器。

    1. @ComponentScan(excludeFilters = {
    2. @Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),
    3. @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})})
    4. public @interface SpringBootApplication {}

    总结下@SpringbootApplication:就是说,他已经把很多东西准备好,具体是否使用取决于程序或者说配置。
    接下来继续看run方法:

    1. public static void main(String[] args) {
    2. SpringApplication.run(Application.class, args);
    3. }

    来看下在执行run方法到底有没有用到哪些自动配置的东西,点进run:

    1. public ConfigurableApplicationContext run(String... args) {
    2. //计时器
    3. StopWatch stopWatch = new StopWatch();
    4. stopWatch.start();
    5. ConfigurableApplicationContext context = null;
    6. Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    7. this.configureHeadlessProperty();
    8. //监听器
    9. SpringApplicationRunListeners listeners = this.getRunListeners(args);
    10. listeners.starting();
    11. Collection exceptionReporters;
    12. try {
    13. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    14. ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
    15. this.configureIgnoreBeanInfo(environment);
    16. Banner printedBanner = this.printBanner(environment);
    17. //准备上下文
    18. context = this.createApplicationContext();
    19. exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
    20. //预刷新context
    21. this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    22. //刷新context
    23. this.refreshContext(context);
    24. //刷新之后的context
    25. this.afterRefresh(context, applicationArguments);
    26. stopWatch.stop();
    27. if (this.logStartupInfo) {
    28. (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
    29. }
    30. listeners.started(context);
    31. this.callRunners(context, applicationArguments);
    32. } catch (Throwable var10) {
    33. this.handleRunFailure(context, var10, exceptionReporters, listeners);
    34. throw new IllegalStateException(var10);
    35. }
    36. try {
    37. listeners.running(context);
    38. return context;
    39. } catch (Throwable var9) {
    40. this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
    41. throw new IllegalStateException(var9);
    42. }
    43. }

    那关注的就是 refreshContext(context); 刷新context,点进来看。

    1. private void refreshContext(ConfigurableApplicationContext context) {
    2. refresh(context);
    3. if (this.registerShutdownHook) {
    4. try {
    5. context.registerShutdownHook();
    6. }
    7. catch (AccessControlException ex) {
    8. // Not allowed in some environments.
    9. }
    10. }
    11. }

    继续点进refresh(context);

    1. protected void refresh(ApplicationContext applicationContext) {
    2. Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    3. ((AbstractApplicationContext) applicationContext).refresh();
    4. }

    他会调用 ((AbstractApplicationContext) applicationContext).refresh();方法,点进来看:

    1. public void refresh() throws BeansException, IllegalStateException {
    2. synchronized (this.startupShutdownMonitor) {
    3. // Prepare this context for refreshing.
    4. prepareRefresh();
    5. // Tell the subclass to refresh the internal bean factory.
    6. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    7. // Prepare the bean factory for use in this context.
    8. prepareBeanFactory(beanFactory);
    9. try {
    10. // Allows post-processing of the bean factory in context subclasses.
    11. postProcessBeanFactory(beanFactory);
    12. // Invoke factory processors registered as beans in the context.
    13. invokeBeanFactoryPostProcessors(beanFactory);
    14. // Register bean processors that intercept bean creation.
    15. registerBeanPostProcessors(beanFactory);
    16. // Initialize message source for this context.
    17. initMessageSource();
    18. // Initialize event multicaster for this context.
    19. initApplicationEventMulticaster();
    20. // Initialize other special beans in specific context subclasses.
    21. onRefresh();
    22. // Check for listener beans and register them.
    23. registerListeners();
    24. // Instantiate all remaining (non-lazy-init) singletons.
    25. finishBeanFactoryInitialization(beanFactory);
    26. // Last step: publish corresponding event.
    27. finishRefresh();
    28. }catch (BeansException ex) {
    29. if (logger.isWarnEnabled()) {
    30. logger.warn("Exception encountered during context initialization - " +
    31. "cancelling refresh attempt: " + ex);
    32. }
    33. // Destroy already created singletons to avoid dangling resources.
    34. destroyBeans();
    35. // Reset 'active' flag.
    36. cancelRefresh(ex);
    37. // Propagate exception to caller.
    38. throw ex;
    39. }finally {
    40. // Reset common introspection caches in Spring's core, since we
    41. // might not ever need metadata for singleton beans anymore...
    42. resetCommonCaches();
    43. }
    44. }
    45. }

    由此可知,就是一个Spring的bean的加载过程。继续来看一个方法叫做 onRefresh()

    1. protected void onRefresh() throws BeansException {
    2. // For subclasses: do nothing by default.
    3. }

    他在这里并没有直接实现,找他的具体实现:
    Spring Boot 注解原理,自动装配原理 - 图6
    比如Tomcat跟web有关,可以看到有个ServletWebServerApplicationContext:

    1. @Override
    2. protected void onRefresh() {
    3. super.onRefresh();
    4. try {
    5. createWebServer();
    6. }
    7. catch (Throwable ex) {
    8. throw new ApplicationContextException("Unable to start web server", ex);
    9. }
    10. }

    可以看到有一个createWebServer();方法他是创建web容器的,而Tomcat不就是web容器,那是如何创建的呢,继续看:

    1. private void createWebServer() {
    2. WebServer webServer = this.webServer;
    3. ServletContext servletContext = getServletContext();
    4. if (webServer == null && servletContext == null) {
    5. ServletWebServerFactory factory = getWebServerFactory();
    6. this.webServer = factory.getWebServer(getSelfInitializer());
    7. }
    8. else if (servletContext != null) {
    9. try {
    10. getSelfInitializer().onStartup(servletContext);
    11. }
    12. catch (ServletException ex) {
    13. throw new ApplicationContextException("Cannot initialize servlet context",
    14. ex);
    15. }
    16. }
    17. initPropertySources();
    18. }

    factory.getWebServer(getSelfInitializer());他是通过工厂的方式创建的。

    1. public interface ServletWebServerFactory {
    2. WebServer getWebServer(ServletContextInitializer... initializers);
    3. }

    可以看到 它是一个接口,为什么会是接口。因为不止是Tomcat一种web容器。
    Spring Boot 注解原理,自动装配原理 - 图7
    看到还有Jetty,来看TomcatServletWebServerFactory:

    1. @Override
    2. public WebServer getWebServer(ServletContextInitializer... initializers) {
    3. Tomcat tomcat = new Tomcat();
    4. File baseDir = (this.baseDirectory != null) ? this.baseDirectory
    5. : createTempDir("tomcat");
    6. tomcat.setBaseDir(baseDir.getAbsolutePath());
    7. Connector connector = new Connector(this.protocol);
    8. tomcat.getService().addConnector(connector);
    9. customizeConnector(connector);
    10. tomcat.setConnector(connector);
    11. tomcat.getHost().setAutoDeploy(false);
    12. configureEngine(tomcat.getEngine());
    13. for (Connector additionalConnector : this.additionalTomcatConnectors) {
    14. tomcat.getService().addConnector(additionalConnector);
    15. }
    16. prepareContext(tomcat.getHost(), initializers);
    17. return getTomcatWebServer(tomcat);
    18. }

    那这块代码,就是要寻找的内置Tomcat,在这个过程当中,可以看到创建Tomcat的一个流程。
    如果不明白的话, 在用另一种方式来理解下,大家要应该都知道stater,举点例子。

    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-data-redis</artifactId>
    4. </dependency>
    5. <dependency>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-freemarker</artifactId>
    8. </dependency>

    首先自定义一个stater。

    1. <parent>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-parent</artifactId>
    4. <version>2.1.4.RELEASE</version>
    5. <relativePath/>
    6. </parent>
    7. <groupId>com.zgw</groupId>
    8. <artifactId>gw-spring-boot-starter</artifactId>
    9. <version>1.0-SNAPSHOT</version>
    10. <dependencies>
    11. <dependency>
    12. <groupId>org.springframework.boot</groupId>
    13. <artifactId>spring-boot-autoconfigure</artifactId>
    14. </dependency>
    15. </dependencies>

    先来看maven配置写入版本号,如果自定义一个stater的话必须依赖spring-boot-autoconfigure这个包,先看下项目目录。
    Spring Boot 注解原理,自动装配原理 - 图8

    1. public class GwServiceImpl implements GwService{
    2. @Autowired
    3. GwProperties properties;
    4. @Override
    5. public void Hello()
    6. {
    7. String name=properties.getName();
    8. System.out.println(name+"说:你们好啊");
    9. }
    10. }

    做的就是通过配置文件来定制name这个是具体实现。

    1. @Component
    2. @ConfigurationProperties(prefix = "spring.gwname")
    3. public class GwProperties {
    4. String name="zgw";
    5. public String getName() {
    6. return name;
    7. }
    8. public void setName(String name) {
    9. this.name = name;
    10. }
    11. }

    这个类可以通过@ConfigurationProperties读取配置文件。

    1. @Configuration
    2. @ConditionalOnClass(GwService.class) //扫描类
    3. @EnableConfigurationProperties(GwProperties.class) //让配置类生效
    4. public class GwAutoConfiguration {
    5. /**
    6. * 功能描述 托管给spring
    7. * @author zgw
    8. * @return
    9. */
    10. @Bean
    11. @ConditionalOnMissingBean
    12. public GwService gwService()
    13. {
    14. return new GwServiceImpl();
    15. }
    16. }

    这个为配置类,为什么这么写因为,spring-boot的stater都是这么写的,可以参照他仿写stater,以达到自动配置的目的,然后在通过spring.factories也来进行配置。

    1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gw.GwAutoConfiguration

    然后这样一个简单的stater就完成了,然后可以进行maven的打包,在其他项目引入就可以使用。