SpringBoot Starter依赖原理与实例详解

目录
  • 1 Starter
  • 2 了解 spring.factories机制
    • 2.1 不同包路径下的依赖注入
    • 2.2 spring.factories 机制
  • 3 spring.factories 机制的实现源码分析
  • 4 程序运行入口run()

1 Starter

在开发 SpringBoot 项目的时候,我们常常通过 Maven 导入自动各种依赖,其中很多依赖都是以 xxx-starter 命名的。

像这种 starter 依赖是怎么工作的呢?

2 了解 spring.factories机制

导入一个依赖,我们就可以调用包内的公共类,这是因为公共类可以被异包调用。很多时候我们添加依赖它会自动往我们的主程序注入一些对象或者监听器,这个是怎么做到的?

2.1 不同包路径下的依赖注入

SpringBoot 默认只扫描启动类所在目录里面的对象

而我们导入的依赖是在另外一个包里,SpringBoot 是扫描不到的!

如何让主项目注入(加载)异包对象呢?通常有两种方法:

  • 在启动类上加上@SpringBootApplication注解,配置scanBasePackages属性,指定扫描路径。
  • resources/META-INF目录下创建spring.factories配置文件,在里面配置需要加载的类

2.2 spring.factories 机制

spring.factories机制是springboot的核心基础之一,这可以描述为一种 可插拔结构,模仿自java中的SPI扩展机制。

spring.factories 实现例子

1.在任意一个项目中新建一个starter模块(springboot项目)

导入 springboot 的自动配置依赖,这里我们主要用到它的@Configuration@Bean注解和ApplicationListener监听器接口

<!-- SpringBoot 自动配置 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

2.随便创建一个bean

public class User {
    private String name;
    public void setName(String name) { this.name = name; }
    public String getName() { return name; }
}

3.创建一个初始化监听器

一般的starter会在容器启动时做一些初始化的操作,这里作为演示只打印一句话。

public class ApplicationInitialize implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("应用初始化");
    }
}

4.创建配置类

这个配置类就是主项目注入starter模块依赖的入口,当它扫描到这个配置类的时候就会加载里面的Bean对象

@Configuration
public class StarterGenericConfig {
    @Bean
    public User getUser() {
        User user = new User();
        user.setName("我来自starter模块");
        return user;
    }
    @Bean
    public ApplicationInitialize getAppli() {
        return new ApplicationInitialize();
    }
}

5.创建spring.factories配置文件

配置类有了,但是因为和主项目不同包启动类它扫描不到,这时我们就要通过spring.factories机制让它能扫描到这个配置类,完成依赖注入。

先在resource资源目录下创建META-INF文件夹,然后创建一个名为spring.factories的文件

内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.echoo.cloud.nacos.starter.config.StarterGenericConfig

这里需要配置StarterGenericConfig配置类的全限定名。

把这个项目导入主项目(添加到主项目的pom依赖中),运行主项目,看看是否注入成功

这就是starter依赖注入的基本思路,实际可能复杂得多,需要继续摸索。

3 spring.factories 机制的实现源码分析

springframework 框架中有这样一个类

package org.springframework.core.io.support;
public final class SpringFactoriesLoader {
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();
	...
	/** SpringFactories
	  * 静态方法, 加载spring.factories文件
	  * */
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
       		...
            try {
            	// 通过类加载器加载资源目录下的"META-INF/spring.factories"文件
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();
                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[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;
                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }
                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }
 	...
}

SpringFactoriesLoaderSpring容器初始化时会加载的一个类,而它的静态方法loadSpringFactories()里会用类加载器去加载资源文件resourece/META-INF/spring.factories,然后读取里面的配置参数(应该都是待加载Bean类的映射数据),用集合封装返回Spring容器,后面应该就是Spring容器加载对应的Bean类。

4 程序运行入口run()

前面知道了Spring容器会加载加载资源文件resourece/META-INF/spring.factories然后加载里面对应的类,那为什么对应的keyorg.springframework.boot.autoconfigure.EnableAutoConfiguration?

先说结论:Spring容器初始化会加载org.springframework.boot.autoconfigure.EnableAutoConfiguration这个类,完了还会去扫描resourece/META-INF/spring.factories加载里面的Bean类,配置文件是键值对形式的,那keyorg.springframework.boot.autoconfigure.EnableAutoConfiguration是因为这个是一个注解,本身就是为了注入拓展类用的,它会在容器初始化或刷新的适当时机注入对应的类。因为扫描不到异包配置类上的@Configuration注解,所以创建了一个@EnableAutoConfiguration注解配合spring.factories配置文件的形式来注入配置类

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch(); // 创建stopWatch对象
        stopWatch.start(); // 开始计算时间
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty(); // 是否使用模拟输入输出设备(默认是,因为服务器不一定有鼠标键盘显示器)
        SpringApplicationRunListeners listeners = this.getRunListeners(args); // 获取并启动监听器
        listeners.starting(); // 获取的监听器为 Event PublishingRunListener,监听并发布启动事件
        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 准备应用环境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment); // 打印 banner
            context = this.createApplicationContext(); 创建容器
            // 加载 SpringFactories 实例(返回的是实例加载的记录、报告)
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            // 准备上下文环境
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context); // 刷新容器
            this.afterRefresh(context, applicationArguments); // 容器刷新后的动作,这里默认没有做任何实现
            stopWatch.stop(); // 停止计算时间
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
           ...
        }
        ...
    }

里面有个创建应用容器的方法createApplicationContext(),深入进去发现他是根据webApplicationType类型去决定创建那种容器,而webApplicationType类型在SpringApplication初始化的时候指定。

    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                }
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }
		// 反射创建容器实例
        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
    }

这里一共有三种类型的容器

  • SERVLET 类型创建 AnnotationConfigServletWebServerApplicationContextServlet容器)
  • REACTIVE类型创建AnnotationConfigReactiveWebServerApplicationContextReactive容器)
  • 默认创建AnnotationConfigApplicationContextApplication容器)

SpringApplication的构造器中对webApplicationType类型进行了初始化,默认返回SERVLET 类型。

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        ...
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        ...
    }

也就是说默认创建AnnotationConfigServletWebServerApplicationContextServlet容器)

在上面入口方法run()方法中,有一个refreshContext()方式,这个刷新容器的方法里面

跟踪这个refreshContext()底层是一个refresh()方法,三种容器都分别实现了这个方法

这里着重看ServletWebServerApplicationContext.refresh()

    public final void refresh() throws BeansException, IllegalStateException {
        try { super.refresh(); } catch (RuntimeException var3) { ... }
    }

发现它调用的是父类AbstractApplicationContextrefresh()函数

    public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) { // 上锁,防止并发
            this.prepareRefresh();                  // 刷新准备工作,记录开始时间,校验配置文件
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); // 获取Bean工厂
            this.prepareBeanFactory(beanFactory);   // Bean工厂准备工作,不详谈
            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory); // Spring拓展点之一
                ...
            } catch (BeansException var9) {
                ...
            } finally {
                this.resetCommonCaches();
            }
        }
    }

重点在invokeBeanFactoryPostProcessors(beanFactory)方法上,这是SpringBoot实现Spring拓展的关键节点,这个方法执行时会调用实现了BeanFactoryPostProcessors接口的实现类的postProcessBeanFactory(factory)方法

(也会调用BeanDefinitionRegistryPostProcessor接口的各个实现类的postProcessBeanDefinitionRegistry(registry)方法)

进入invokeBeanFactoryPostProcessors(beanFactory)方法

    public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
   			...
            currentRegistryProcessors = new ArrayList();
            // 获取所有 BeanDefinitionRegistryPostProcessor 接口实现类的全限定名集合
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            String[] var16 = postProcessorNames;
            var9 = postProcessorNames.length;
            int var10;
            String ppName;
            for(var10 = 0; var10 < var9; ++var10) {
                ppName = var16[var10];
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                	// 根据 Bean 名获取 Bean 对象放入 currentRegistryProcessors
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory); // 排序,暂时没看,不知道排啥
            registryProcessors.addAll(currentRegistryProcessors);
            // 调用 Bean 定义注册后处理器
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
			...
	}

每一个Bean类的定义注册是在Spring容器中完成的,在上面invokeBeanFactoryPostProcessors()方法中,通过 Bean 工厂获取 了所有BeanDefinitionRegistryPostProcessor接口的实现类名,然后再通过 invokeBeanDefinitionRegistryPostProcessors()方法调用所有实现类的postProcessBeanDefinitionRegistry()方法去做 Bean 类注册后的相关处理动作。

BeanDefinitionRegistryPostProcessor接口:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}

知道这个有什么用呢?前面我们知道了Springboot容器中的AnnotationConfigServletWebServerApplicationContextServlet容器)是通过反射获取AnnotationConfigServletWebServerApplicationContext的构造器创建实例的,所以我们看看AnnotationConfigServletWebServerApplicationContext的构造器长什么样儿。

    public AnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) {
        super(beanFactory);
        this.annotatedClasses = new LinkedHashSet();
        this.reader = new AnnotatedBeanDefinitionReader(this); // 注解 Bean 定义读取器
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

进入AnnotatedBeanDefinitionReader看它的构造器

    public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
        ...
        // 通过 AnnotationConfigUtils 工具注册 注解配置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

再进入AnnotationConfigUtils 工具的registerAnnotationConfigProcessors()看看它是如何注册的

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {
	...
	Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet(8); // 存储 BeanDefinitionHolder 对象的集合
    RootBeanDefinition def;
	if (!registry.containsBeanDefinition("org.springframework.context.annotation.internalConfigurationAnnotationProcessor")) {
		// 创建一个 ConfigurationClassPostProcessor 的 RootBeanDefinition 对象
		def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        // 把 ConfigurationClassPostProcessor 的 RootBeanDefinition 对象装入一个 BeanDefinitionHolder (容器)
        // 并映射名字为 org.springframework.context.annotation.internalConfigurationAnnotationProcessor
        beanDefs.add(registerPostProcessor(registry, def, "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"));
    }
	...
}

在它的registerAnnotationConfigProcessors()方法中看到了它用注册后处理器registerPostProcessor去注册org.springframework.context.annotation.internalConfigurationAnnotationProcessorBeanDefinition(Bean定义描述对象)和BeanDefinitionHolder(Bean定义描述对象容器),然后返回这个BeanDefinitionHolder(Bean定义描述对象容器)存储到beanDefs(Bean定义描述对象容器集合)里面。

到这里就是说明在初始化AnnotationConfigServletWebServerApplicationContextServlet容器)时,会用org.springframework.context.annotation.internalConfigurationAnnotationProcessor这个名字注册ConfigurationClassPostProcessor这个 Bean对象,然后就能根据它的BeanDefinitionHolder(Bean定义描述对象容器)去创建ConfigurationClassPostProcessor对象。

现在问题就来到了ConfigurationAnnotationProcessor对象身上了,为啥要创建它?因为它就是加载spring.factories配置文件的关键。

进入它的postProcessBeanDefinitionRegistry()方法

    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)) { // 是否有对应的 Bean 工厂
            throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);
        } else { // 都没有,说明这个 BeanDefinition 没有注册加载过
            this.registriesPostProcessed.add(registryId); // 添加注册记录
            this.processConfigBeanDefinitions(registry);  // 处理这个 BeanDefinition 的配置
        }
    }

深入processConfigBeanDefinitions()看它怎么处理这个 BeanDefinition 的配置

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        List<BeanDefinitionHolder> configCandidates = new ArrayList(); // 待处理配置集合
        String[] candidateNames = registry.getBeanDefinitionNames();
        String[] var4 = candidateNames;
        int var5 = candidateNames.length;
        for(int var6 = 0; var6 < var5; ++var6) {
            String beanName = var4[var6];
            BeanDefinition beanDef = registry.getBeanDefinition(beanName); // 根据名称获取 BeanDefinition
            // 判断这个 BeanDefinition 的配置属性是不是空
            if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
                if (this.logger.isDebugEnabled()) {
                	// 如果不是空就说明这个 BeanDefinition 已经被当作配置类处理过了
                    this.logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
                }
            } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            	// 如果为空,放入待处理配置集合里等待后续处理
                configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            }
        }
		// 指令排序相关,不深究
        if (!configCandidates.isEmpty()) {
            configCandidates.sort((bd1, bd2) -> {
                int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
                int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
                return Integer.compare(i1, i2);
            });
            ...
            // 根据环境创建了一个配置类解析器
            ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
            Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);
            HashSet alreadyParsed = new HashSet(configCandidates.size());

            do {
                parser.parse(candidates); // 解析配置类
                parser.validate();
                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());
                }
				...
            } while(!candidates.isEmpty())
            ...
        }
    }

进入ConfigurationClassParser.parse()方法,看看它怎么解析配置类

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try { // 对 BeanDefinition 做解析操作
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}
		// 延迟导入选择器
		this.deferredImportSelectorHandler.process();
	}

解析发现不了什么线索,进入this.deferredImportSelectorHandler.process()延迟导入选择处理器看看

层层追踪:

this.deferredImportSelectorHandler.process()

DeferredImportSelectorGroupingHandler.processGroupImports()

grouping.getImports()

grouping.getImports()

this.group.selectImports()

追踪到public interface ImportSelector {...}接口

在找到它的实现类AutoConfigurationImportSelector

在实现类AutoConfigurationImportSelector里层层追踪

selectImports()

getAutoConfigurationEntry()

getCandidateConfigurations()

getSpringFactoriesLoaderFactoryClass()

最后追踪到 getSpringFactoriesLoaderFactoryClass()方法

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

里面返回的是一个EnableAutoConfiguration.class类,这个类就是我们在spring.factories配置文件里面配置的org.springframework.boot.autoconfigure.EnableAutoConfiguration

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.echoo.cloud.nacos.starter.config.StarterGenericConfig

到此这篇关于SpringBoot Starter依赖原理与实例详解的文章就介绍到这了,更多相关SpringBoot Starter依赖内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot详细分析自动装配原理并实现starter

    目录 约定优于配置 自动装配 手写一个starter组件 约定优于配置 SpringBoot的预定优于配置主要体现在以下几个方面: maven的目录结构: 配置文件默认存放在resources目录下 项目编译后的文件存放在target目录下 项目默认打包成jar格式 配置文件默认为application.yml或application.yaml或application.properties 默认通过 spring.profiles.active 属性来决定运行环境时的配置文件. 自动装配 相对于

  • springboot自定义starter方法及注解实例

    目录 SpringBoot starter 自定义starter 自定义starter步骤 实现 打包测试 注解解释 SpringBoot starter 用了springboot 那么久了居然都还没自定义过starter,想想都觉得羞愧,所以今天来玩一下. SpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进starter,应用者只需要在maven中引入starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置.star

  • SpringBoot如何自定义starter

    目录 1. 什么是starter 2. 自动配置原理 2.1 自动配置生效 3. 自定义starter 3.1 命名规范 4.总结 4.1为什么要自定义starter? 4.2 自定义starter的案例 1. 什么是starter Springboot的出现极大的简化了开发人员的配置,而这之中的一大利器便是springboot的starter,starter是springboot的核心组成部分,为什么说引入如下依赖就满足了日常web开发? <dependency>   <groupId

  • 教你利用SpringBoot写一个属于自己的Starter

    目录 (一)概述 (二)看个例子 (三)实现信息播报Starter (四)调用这个Starter (五)总结 (一)概述 SpringBoot以其自动装配的能力被广泛应用,我们在写代码时肯定遇到过很多spring-boot-starter命名的依赖,比如spring-boot-starter-web,在pom文件中引入这些starter依赖后,SpringBoot就能通过自动装配的技术扫描到这些类并装载到Bean容器中. 除了SpringBoot官方的这些Starter外,我们自己也可以开发St

  • 详解SpringBoot如何自定义Starter

    目录 阅读收获 本章源码下载 什么是Starter 为什么使用Starter Springboot自动配置 spring.factories Starter开发常用注解 Full全模式和Lite轻量级模式 Starter命名规范 开发Starter 1. 创建Starter项目 2. 添加依赖 3. 编写属性类 4. 自定义业务类 5. 编写自动配置类 6. 编写spring.factories 7. 编写配置提示文件(非必须) 测试Starter 1. 前置环境 2. 添加依赖 3. 测试类

  • SpringBoot Starter机制及整合tomcat的实现详解

    目录 Starter机制和springboot整合tomcat Starter机制 springboot整合tomcat 总结 Starter机制和springboot整合tomcat Starter机制 先解释一下什么是Starter机制.Starter机制就是maven工程中pom文件引入了某个Starter依赖,就能使用对应的功能 例如 引入web的starter依赖 ,就可以使用有关于web方面的功能. <dependency> <groupId>org.springfra

  • SpringBoot项目为何引入大量的starter?如何自定义starter?

    目录 1 前言 2 @EnableConfigurationProperties实现自动装配 2.1 创建一个starter项目 2.2 创建一个需要自动装配的Bean 2.3 自动装配类实现 2.4 编写测试项目 3 @import 实现自动注入 3.1 方式一 直接制定Bean的导入 3.2 方式二 使用ImportSelector注入Bean 3.3 方式三 使用ImportBeanDefinitionRegistrar注入Bean 4 实现跨项目自动配置 4.1 添加依赖 4.2 编译项

  • SpringBoot自定义Starter实现流程详解

    目录 starter起步依赖 starter命名规则 自定义starter new module 添加依赖 simplebean 自动配置类 META-INF\spring.factories 在spring-boot-mytest中引入mystarter-spring-boot-starter 添加配置 通过@Autowired引用 启动访问 starter起步依赖 starter起步依赖是springboot一种非常重要的机制, 它打包了某些场景下需要用到依赖,将其统一集成到starter,

  • SpringBoot Starter依赖原理与实例详解

    目录 1 Starter 2 了解 spring.factories机制 2.1 不同包路径下的依赖注入 2.2 spring.factories 机制 3 spring.factories 机制的实现源码分析 4 程序运行入口run() 1 Starter 在开发 SpringBoot 项目的时候,我们常常通过 Maven 导入自动各种依赖,其中很多依赖都是以 xxx-starter 命名的. 像这种 starter 依赖是怎么工作的呢? 2 了解 spring.factories机制 导入一

  • IntelliJ Idea SpringBoot 数据库增删改查实例详解

    SpringBoot 是 SpringMVC 的升级,对于编码.配置.部署和监控,更加简单 微服务 微服务是一个新兴的软件架构,就是把一个大型的单个应用程序和服务拆分为数十个的支持微服务.一个微服务的策略可以让工作变得更为简便,它可扩展单个组件而不是整个的应用程序堆栈,从而满足服务等级协议. Spring 为 微服务提供了一整套的组件-SpringClound , SpirngBoot 就是该基础. 第一个SpringBoot程序 这里使用的开发软件是IntelliJ Idea,和Eclipse

  • Spring IOC和aop的原理及实例详解

    这篇文章主要介绍了Spring IOC和aop的原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架.特点是面向接口编程,松耦合. 1:IOC(控制反转) 别名(DI:依赖注入) 首先来一段ioc的实现原来代码: public class ClassPathXmlApplicationContext implements BeanFactory { privat

  • 使用SpringBoot整合ssm项目的实例详解

    SpringBoot是什么? Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程. Spring Boot 现在已经成为 Java 开发领域的一颗璀璨明珠,它本身是包容万象的,可以跟各种技术集成.成为 SpringBoot 全家桶,成为一把万能钥匙. SpringBoot的特点 1.创建独立的 Spring 应用程序 2.嵌入的 Tomcat ,无需部署 WAR 文件 3.简化 Maven 配置 4.自动配置 Spr

  • Java8默认方法Default Methods原理及实例详解

    这篇文章主要介绍了Java8默认方法Default Methods原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Java 8 引入了新的语言特性--默认方法(Default Methods). Default methods enable new functionality to be added to the interfaces of libraries and ensure binary compatibility wit

  • springboot+rabbitmq实现智能家居实例详解

    目录 引言 一.什么是 MQTT协议? 二.为什么要用 MQTT协议? 三.MQTT协议介绍 MQTT数据包 1.固定头 2.可变头 3.消息体payload 消息质量(QoS ) 1.Qos 0 2.Qos 1 3.Qos 2 LWT(最后遗嘱) 四.MQTT协议应用场景 五.代码实现 1.启用 rabbitmq的mqtt协议 2.mqtt 客户端依赖包 3.消息发送者 4.消息订阅 六.测试消息 1.测试消息发送 2.测试消息订阅 七.应用注意事项 clientId 要唯一 八.其他中间件

  • Redis 实现队列原理的实例详解

    Redis 实现队列原理的实例详解 场景说明: ·用于处理比较耗时的请求,例如批量发送邮件,如果直接在网页触发执行发送,程序会出现超时 ·高并发场景,当某个时刻请求瞬间增加时,可以把请求写入到队列,后台在去处理这些请求 ·抢购场景,先入先出的模式 命令: rpush + blpop 或 lpush + brpop rpush : 往列表右侧推入数据 blpop : 客户端阻塞直到队列有值输出 简单队列: simple.php $stmt = $pdo->prepare('select id, c

  • SpringBoot使用WebSocket的方法实例详解

    WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议. WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据.在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输. 在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道.两者之间就直接可以数据互相传送. java怎么写 配置Be

  • Python 异步协程函数原理及实例详解

    这篇文章主要介绍了Python 异步协程函数原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一. asyncio 1.python3.4开始引入标准库之中,内置对异步io的支持 2.asyncio本身是一个消息循环 3.步骤: (1)创建消息循环 (2)把协程导入 (3)关闭 4.举例: import threading # 引入异步io包 import asyncio # 使用协程 @ asyncio.coroutine def

  • springboot单元测试两种方法实例详解

    这篇文章主要介绍了springboot单元测试两种方法实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 springboot的单元测试,这里介绍两种方式,一种是在测试类中添加注解:另一种是在代码中启动项目的main方法中继承接口(也可以写在其他方法中). 如 对查看数据库的连接池信息 进行单元测试 1. 在类上使用注解: @RunWith(SpringRunner.class) @SpringBootTest @RunWith(Sprin

随机推荐