SpringBoot自动装配-自定义Start

SpringBoot自动装配

JAVA技术交流群:737698533

SpringBootApplication注解

什么是自动装配,也就是说帮你把需要的类自动添加到Spring容器中

只要是一个SpringBoot项目肯定有这样一个类

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(TokenApplication.class, args);
    }
}

而自动装配是在 @SpringBootApplication这个注解中实现的,点进去首先能看到这个注解上还有三个注解

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

其中SpringBootConfiguration上还有一个注解 @Configuration也就是说明其实SpringBootApplication也是一个配置类

@ComponentScan用于扫描需要被Spring管理的类,这也就是为什么写的类需要在SpringBoot启动类同级或在同级下的子包中

@EnableAutoConfiguration点进去发现它上面有一个特殊的注解 @Import(AutoConfigurationImportSelector.class)

@Import注解的作用是将指定类添加到Spring容器中成为一个Bean

而在AutoConfigurationSelector类中有自动装配的实现

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    //下面的方法
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
    //获取注解的名称作为key
    String name = getAnnotationClass().getName();
    AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
    Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
                   + " annotated with " + ClassUtils.getShortName(name) + "?");
    return attributes;
}

其中我们关注与返回值相关的代码,也就是getCandidateConfigurations这个方法

protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                                                                         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;
}

继续查看loadFactoryNames方法

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    try {
        //从META-INF/spring.factories中获取配置文件
        Enumeration urls = (classLoader != null ?
                                 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                                 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                                           FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

其中需要解释一下的是:MultiValueMap[接口],它是一个特殊的Map,由SpringBoot自己写的,查看它的实现类LinkedMultiValueMap

private final Map> targetMap;

通常我们使用的Map是一个

首先SpringBoot会从缓存中尝试获取(其实也就是个map,不过有点特殊),如果获取不到,那就一次将全部内容读取出来,然后以K V V V…的形式存放到类中

那么META-INF/spring.factories这个文件在哪呢?

SpringBoot自动装配-自定义Start

它里面的内容是这样的

SpringBoot自动装配-自定义Start

以类的全限定名作为Key,其他类的全限定名作为Value

那到现在还是一头雾水,读到了这个有什么用呢?我们拿常见的Redis来看看

SpringBoot自动装配-自定义Start

点进RedisAutoConfiguration看看

SpringBoot自动装配-自定义Start

发现里面全是报错,因为我还没有导入Redis的start,当我在pom文件中添加redis的依赖后


    org.springframework.boot
    spring-boot-starter-redis
    1.4.1.RELEASE

SpringBoot自动装配-自定义Start

发现不报错了,使用RedisTemplate存取值也可以了,这里就不再演示存取值

上面这个类主要看类上面的注解,主要的两个为: ConditionalOnClassEnableConfigurationProperties

ConditionalOnClass

@ConditionalOnClass的作用是当前项目的classpath中存在某个类在会实例化这个类为bean,Spring还提供了其他类似的注解

SpringBoot自动装配-自定义Start

那么毫无疑问pom中导入的那个依赖中肯定有一个类或接口叫做 RedisOperations,点进去查看它的包路径

package org.springframework.data.redis.core;

我们去导入的包中找一找

SpringBoot自动装配-自定义Start

SpringBoot自动装配-自定义Start

EnableConfigurationProperties

@EnableConfigurationProperties注解是使 @ConfigurationProperties 注解的类生效,点进注解上的类

SpringBoot自动装配-自定义Start

@ConfigurationProperties注解的作用是可以将参数的配置设置在application配置文件中,我们在application配置文件中配置的参数都配置类中的字段,要不然这些参数那来的?

SpringBoot自动装配-自定义Start

那么现在SpringBoot自动装配的大致流程就完成了

  1. 读取META-INF/spring.factories文件
  2. 将扫描出的类进行判断
  3. 如果符合类上的 @ConditionalOnxxxx注解就将类添加到Spring容器中

如何自定义一个Start

现在知道了SpringBoot是如何自动装配的,扫描MEAT-INF下spring.factories文件,key为:EnableAutoConfiguration,为什么key为EnableAutoConfiguration呢?在上面的代码中,扫描的以@EnableAutoConfiguration注解获取名称作为key

首先创建一个Maven项目,导入依赖


        org.springframework.boot
        spring-boot-autoconfigure
        2.0.0.RELEASE

        org.springframework.boot
        spring-boot-configuration-processor
        2.0.0.RELEASE
        true

之后在resources文件夹下创建META-INF/spring.factories文件

SpringBoot自动装配-自定义Start

然后创建配置类 当代码没有写完时@ConfigurationProperties会报错:没有开启这个配置类,可以暂时不管

SpringBoot自动装配-自定义Start

创建服务类

SpringBoot自动装配-自定义Start

创建自动注入的配置类

SpringBoot自动装配-自定义Start

最后在spring.factories中添加自动装配类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
\com.jame.UserServiceAutoConfiguration  //这里替换成自己的类全路径

整个项目结构如下

SpringBoot自动装配-自定义Start

最后将maven项目 clean install 打包到当前maven仓库中

在target输出文件夹下打开cmd 直接在路径框中输入cmd直接打开当前位置

SpringBoot自动装配-自定义Start

输入命令

mvn install:install-file -Dfile=start-user-1.0-SNAPSHOT.jar -DgroupId=com.jame -DartifactId=user-spring-boot-start -Dversion=1.0 -Dpackaging=jar

  • -Dfile 文件名
  • -DgroupId 就是groupId
  • -DartifactId 项目名称,可以不和文件名一样
  • -Dversion 版本号
  • -Dpackaging打包方式

完成后新建个SpringBoot项目来测试

导入刚才打包的项目

SpringBoot自动装配-自定义Start

配置参数

SpringBoot自动装配-自定义Start

创建一个Controller来测试

SpringBoot自动装配-自定义Start

这里使用@Autowired idea可能会提示错误,说找不到对应的类型,这个是idea的问题

如果不想看着难受可以设置:Setting->Editor->inspections->Spring Core->Core->AutoWring for bean class 将Error设置为Waring

最后访问测试:

SpringBoot自动装配-自定义Start

Original: https://www.cnblogs.com/sunankang/p/15046795.html
Author: Jame!
Title: SpringBoot自动装配-自定义Start

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/621612/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 达梦产品技术支持培训-day6-DM性能诊断与优化

    (本文只作为个人随笔用途,非官方文档,请勿作他用,谢谢) 1、DM8查询优化基本思路 1.1 操作系统性能诊断 linux常用系统监控命令 top 主要关注DMserver 的CP…

    数据库 2023年6月11日
    068
  • 数据库操作语句大全(sql)

    数据库操作语句大全(sql) 一、基础 1、说明:创建数据库 CREATE DATABASE database-name 2、说明:删除数据库 drop database dbna…

    数据库 2023年6月9日
    084
  • mysql数据库 Window下安装

    关系数据库,是建立在关系数据库模型基础上的数据库,借助于集合代数等概念和方法来处理数据 库中的数据,同时也是一个被组织成一组拥有正式描述性的表格,该形式的表格作用的实质是装载着数 …

    数据库 2023年6月9日
    084
  • SpringMVC完整学习!!!

    1.楔子 1.1、了解MVC 1.2、MVC框架的主要功能 2.初识SpringMVC 2.1、为什么要学习SpringMVC 2.2、了解SpringMVC 3.入门项目初体验!…

    数据库 2023年6月16日
    076
  • Docker常用命令

    镜像:Docker 镜像是用于创建 Docker 容器的模板容器:容器是独立运行的一个或一组应用仓库:用来保存镜像,可以理解为代码控制中的代码仓库 一个仓库中包含多个镜像,以镜像为…

    数据库 2023年6月11日
    076
  • Mysql 一主一从

    1. 主从原理 1.1 主从介绍 所谓 mysql 主从就是建立两个完全一样的数据库,其中一个为主要使用的数据库,另一个为次要的数据库,一般在企业中,存放比较重要的数据的数据库服务…

    数据库 2023年6月14日
    070
  • 深入浅出分析 HashMap

    作者:炸鸡可乐原文出处:www.pzblog.cn 一、摘要 在集合系列的第一章,咱们了解到,Map的实现类有HashMap、LinkedHashMap、TreeMap、Ident…

    数据库 2023年6月14日
    065
  • MySQL 关于 only_full_group_by 限制

    先上结论 如果 only_full_group_by 被启用,那么在查询时,如果某个列不在group by 列表中,此时如果不对该列进行聚合处理,则该列不能出现在 select 列…

    数据库 2023年6月16日
    087
  • Mysql客户端的安装

    Mysql数据库(简称)属于C/S架构,正常工作中一般都会提供服务端,我们只需要安装客户端进行查询修改数据等操作即可。 在正常工作中,无论是测试人员还是开发人员,总数据库管理员(测…

    数据库 2023年5月24日
    075
  • 23种设计模式之访问者模式(Visitor Pattern)

    文章目录 概述 访问者模式的优缺点 访问者模式的使用场景 访问者模式的结构和实现 * 模式结构 模式实现 总结 概述 访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可…

    数据库 2023年6月6日
    082
  • 客户流失?来看看大厂如何基于spark+机器学习构建千万数据规模上的用户留存模型 ⛵

    💡 作者:韩信子@ShowMeAI📘 大数据技术 ◉ 技能提升系列:https://www.showmeai.tech/tutorials/84📘 行业名企应用系列:https:/…

    数据库 2023年6月14日
    075
  • Vue 和 Django 实现 Token 身份验证

    使用 Django 编写的 B/S 应用通常会使用 Cookie + Session 的方式来做身份验证,用户登录信息存储在后台数据库中,前端 Cookie 也会存储少量用于身份核…

    数据库 2023年6月14日
    069
  • 位图的简单操作

    class BitMap { private byte[] words;//用一个字节数&#x7…

    数据库 2023年6月14日
    0131
  • 绿色安装MySQL5.7版本—-配置my.ini文件注意事项

    简述绿色安装MySQL5.7版本以及配置my.ini文件注意事项 前言 由于前段时间电脑重装,虽然很多软件不在C盘,但是由于很多注册表以及关联文件被删除,很多软件还需要重新配置甚至…

    数据库 2023年5月24日
    089
  • Linux

    1、关机命令 命令 说明 sync 将数据由内存同步到硬盘中 shutdown 关机 shutdown -h 10 10分钟后关机 shutdown -h now 立马关机 shu…

    数据库 2023年6月16日
    099
  • ReentrantLock 公平锁源码 第0篇

    ReentrantLock 0 关于ReentrantLock的文章其实写过的,但当时写的感觉不是太好,就给删了,那为啥又要再写一遍呢 最近闲着没事想自己写个锁,然后整了几天出来后…

    数据库 2023年6月16日
    076
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球